[BTRFS] Upgrade to 1.1
[reactos.git] / drivers / filesystems / btrfs / fileinfo.c
1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "btrfs_drv.h"
19
20 #if (NTDDI_VERSION >= NTDDI_WIN10)
21 // not currently in mingw - introduced with Windows 10
22 #ifndef FileIdInformation
23 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
24 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
25
26 typedef struct _FILE_STAT_LX_INFORMATION {
27 LARGE_INTEGER FileId;
28 LARGE_INTEGER CreationTime;
29 LARGE_INTEGER LastAccessTime;
30 LARGE_INTEGER LastWriteTime;
31 LARGE_INTEGER ChangeTime;
32 LARGE_INTEGER AllocationSize;
33 LARGE_INTEGER EndOfFile;
34 ULONG FileAttributes;
35 ULONG ReparseTag;
36 ULONG NumberOfLinks;
37 ACCESS_MASK EffectiveAccess;
38 ULONG LxFlags;
39 ULONG LxUid;
40 ULONG LxGid;
41 ULONG LxMode;
42 ULONG LxDeviceIdMajor;
43 ULONG LxDeviceIdMinor;
44 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
45
46 #define LX_FILE_METADATA_HAS_UID 0x01
47 #define LX_FILE_METADATA_HAS_GID 0x02
48 #define LX_FILE_METADATA_HAS_MODE 0x04
49 #define LX_FILE_METADATA_HAS_DEVICE_ID 0x08
50 #define LX_FILE_CASE_SENSITIVE_DIR 0x10
51
52 #endif
53 #endif
54
55 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
56 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
57 fcb* fcb = FileObject->FsContext;
58 ccb* ccb = FileObject->FsContext2;
59 file_ref* fileref = ccb ? ccb->fileref : NULL;
60 ULONG defda, filter = 0;
61 BOOL inode_item_changed = FALSE;
62 NTSTATUS Status;
63
64 if (fcb->ads) {
65 if (fileref && fileref->parent)
66 fcb = fileref->parent->fcb;
67 else {
68 ERR("stream did not have fileref\n");
69 return STATUS_INTERNAL_ERROR;
70 }
71 }
72
73 if (!ccb) {
74 ERR("ccb was NULL\n");
75 return STATUS_INVALID_PARAMETER;
76 }
77
78 TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes);
79
80 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
81
82 if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
83 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
84 Status = STATUS_INVALID_PARAMETER;
85 goto end;
86 }
87
88 if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) &&
89 (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
90 Status = STATUS_ACCESS_DENIED;
91 goto end;
92 }
93
94 // don't allow readonly subvol to be made r/w if send operation running on it
95 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY &&
96 fcb->subvol->send_ops > 0) {
97 Status = STATUS_DEVICE_NOT_READY;
98 goto end;
99 }
100
101 // times of -2 are some sort of undocumented behaviour to do with LXSS
102
103 if (fbi->CreationTime.QuadPart == -2)
104 fbi->CreationTime.QuadPart = 0;
105
106 if (fbi->LastAccessTime.QuadPart == -2)
107 fbi->LastAccessTime.QuadPart = 0;
108
109 if (fbi->LastWriteTime.QuadPart == -2)
110 fbi->LastWriteTime.QuadPart = 0;
111
112 if (fbi->ChangeTime.QuadPart == -2)
113 fbi->ChangeTime.QuadPart = 0;
114
115 if (fbi->CreationTime.QuadPart == -1)
116 ccb->user_set_creation_time = TRUE;
117 else if (fbi->CreationTime.QuadPart != 0) {
118 win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
119 inode_item_changed = TRUE;
120 filter |= FILE_NOTIFY_CHANGE_CREATION;
121
122 ccb->user_set_creation_time = TRUE;
123 }
124
125 if (fbi->LastAccessTime.QuadPart == -1)
126 ccb->user_set_access_time = TRUE;
127 else if (fbi->LastAccessTime.QuadPart != 0) {
128 win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
129 inode_item_changed = TRUE;
130 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
131
132 ccb->user_set_access_time = TRUE;
133 }
134
135 if (fbi->LastWriteTime.QuadPart == -1)
136 ccb->user_set_write_time = TRUE;
137 else if (fbi->LastWriteTime.QuadPart != 0) {
138 win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
139 inode_item_changed = TRUE;
140 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
141
142 ccb->user_set_write_time = TRUE;
143 }
144
145 if (fbi->ChangeTime.QuadPart == -1)
146 ccb->user_set_change_time = TRUE;
147 else if (fbi->ChangeTime.QuadPart != 0) {
148 win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
149 inode_item_changed = TRUE;
150 // no filter for this
151
152 ccb->user_set_change_time = TRUE;
153 }
154
155 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
156 if (fbi->FileAttributes != 0) {
157 LARGE_INTEGER time;
158 BTRFS_TIME now;
159
160 fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL;
161
162 defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.',
163 TRUE, Irp);
164
165 if (fcb->type == BTRFS_TYPE_DIRECTORY)
166 fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
167 else if (fcb->type == BTRFS_TYPE_SYMLINK)
168 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
169
170 fcb->atts_changed = TRUE;
171
172 if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)
173 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
174
175 if (defda == fbi->FileAttributes)
176 fcb->atts_deleted = TRUE;
177 else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY))
178 fcb->atts_deleted = TRUE;
179
180 fcb->atts = fbi->FileAttributes;
181
182 KeQuerySystemTime(&time);
183 win_time_to_unix(time, &now);
184
185 if (!ccb->user_set_change_time)
186 fcb->inode_item.st_ctime = now;
187
188 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
189 fcb->subvol->root_item.ctime = now;
190
191 if (fcb->inode == SUBVOL_ROOT_INODE) {
192 if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)
193 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY;
194 else
195 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY;
196 }
197
198 inode_item_changed = TRUE;
199
200 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
201 }
202
203 if (inode_item_changed) {
204 fcb->inode_item.transid = Vcb->superblock.generation;
205 fcb->inode_item.sequence++;
206 fcb->inode_item_changed = TRUE;
207
208 mark_fcb_dirty(fcb);
209 }
210
211 if (filter != 0)
212 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
213
214 Status = STATUS_SUCCESS;
215
216 end:
217 ExReleaseResourceLite(fcb->Header.Resource);
218
219 return Status;
220 }
221
222 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
223 FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
224 fcb* fcb = FileObject->FsContext;
225 ccb* ccb = FileObject->FsContext2;
226 file_ref* fileref = ccb ? ccb->fileref : NULL;
227 ULONG atts;
228 NTSTATUS Status;
229
230 if (!fileref)
231 return STATUS_INVALID_PARAMETER;
232
233 acquire_fcb_lock_exclusive(Vcb);
234
235 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
236
237 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
238
239 if (fcb->ads) {
240 if (fileref->parent)
241 atts = fileref->parent->fcb->atts;
242 else {
243 ERR("no fileref for stream\n");
244 Status = STATUS_INTERNAL_ERROR;
245 goto end;
246 }
247 } else
248 atts = fcb->atts;
249
250 TRACE("atts = %x\n", atts);
251
252 if (atts & FILE_ATTRIBUTE_READONLY) {
253 TRACE("not allowing readonly file to be deleted\n");
254 Status = STATUS_CANNOT_DELETE;
255 goto end;
256 }
257
258 // FIXME - can we skip this bit for subvols?
259 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) {
260 TRACE("directory not empty\n");
261 Status = STATUS_DIRECTORY_NOT_EMPTY;
262 goto end;
263 }
264
265 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
266 TRACE("trying to delete file which is being mapped as an image\n");
267 Status = STATUS_CANNOT_DELETE;
268 goto end;
269 }
270
271 ccb->fileref->delete_on_close = fdi->DeleteFile;
272
273 FileObject->DeletePending = fdi->DeleteFile;
274
275 Status = STATUS_SUCCESS;
276
277 end:
278 ExReleaseResourceLite(fcb->Header.Resource);
279
280 release_fcb_lock(Vcb);
281
282 // send notification that directory is about to be deleted
283 if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
284 FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
285 NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
286 }
287
288 return Status;
289 }
290
291 BOOL has_open_children(file_ref* fileref) {
292 LIST_ENTRY* le = fileref->children.Flink;
293
294 if (IsListEmpty(&fileref->children))
295 return FALSE;
296
297 while (le != &fileref->children) {
298 file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
299
300 if (c->open_count > 0)
301 return TRUE;
302
303 if (has_open_children(c))
304 return TRUE;
305
306 le = le->Flink;
307 }
308
309 return FALSE;
310 }
311
312 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
313 device_extension* Vcb = oldfcb->Vcb;
314 fcb* fcb;
315 LIST_ENTRY* le;
316
317 // FIXME - we can skip a lot of this if the inode is about to be deleted
318
319 fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file?
320 if (!fcb) {
321 ERR("out of memory\n");
322 return STATUS_INSUFFICIENT_RESOURCES;
323 }
324
325 fcb->Vcb = Vcb;
326
327 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
328 fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
329 fcb->Header.FileSize = oldfcb->Header.FileSize;
330 fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength;
331
332 fcb->type = oldfcb->type;
333
334 if (oldfcb->ads) {
335 fcb->ads = TRUE;
336 fcb->adshash = oldfcb->adshash;
337 fcb->adsmaxlen = oldfcb->adsmaxlen;
338
339 if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
340 fcb->adsxattr.Length = oldfcb->adsxattr.Length;
341 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
342 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
343
344 if (!fcb->adsxattr.Buffer) {
345 ERR("out of memory\n");
346 free_fcb(Vcb, fcb);
347 return STATUS_INSUFFICIENT_RESOURCES;
348 }
349
350 RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
351 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
352 }
353
354 if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
355 fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
356 fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
357
358 if (!fcb->adsdata.Buffer) {
359 ERR("out of memory\n");
360 free_fcb(Vcb, fcb);
361 return STATUS_INSUFFICIENT_RESOURCES;
362 }
363
364 RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
365 }
366
367 goto end;
368 }
369
370 RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM));
371 fcb->inode_item_changed = TRUE;
372
373 if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) {
374 fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
375 if (!fcb->sd) {
376 ERR("out of memory\n");
377 free_fcb(Vcb, fcb);
378 return STATUS_INSUFFICIENT_RESOURCES;
379 }
380
381 RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd));
382 }
383
384 fcb->atts = oldfcb->atts;
385
386 le = oldfcb->extents.Flink;
387 while (le != &oldfcb->extents) {
388 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
389
390 if (!ext->ignore) {
391 extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
392
393 if (!ext2) {
394 ERR("out of memory\n");
395 free_fcb(Vcb, fcb);
396 return STATUS_INSUFFICIENT_RESOURCES;
397 }
398
399 ext2->offset = ext->offset;
400 ext2->datalen = ext->datalen;
401
402 if (ext2->datalen > 0)
403 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen);
404
405 ext2->unique = FALSE;
406 ext2->ignore = FALSE;
407 ext2->inserted = TRUE;
408
409 if (ext->csum) {
410 ULONG len;
411 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
412
413 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
414 len = (ULONG)ed2->num_bytes;
415 else
416 len = (ULONG)ed2->size;
417
418 len = len * sizeof(UINT32) / Vcb->superblock.sector_size;
419
420 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
421 if (!ext2->csum) {
422 ERR("out of memory\n");
423 free_fcb(Vcb, fcb);
424 return STATUS_INSUFFICIENT_RESOURCES;
425 }
426
427 RtlCopyMemory(ext2->csum, ext->csum, len);
428 } else
429 ext2->csum = NULL;
430
431 InsertTailList(&fcb->extents, &ext2->list_entry);
432 }
433
434 le = le->Flink;
435 }
436
437 le = oldfcb->hardlinks.Flink;
438 while (le != &oldfcb->hardlinks) {
439 hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2;
440
441 hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
442
443 if (!hl2) {
444 ERR("out of memory\n");
445 free_fcb(Vcb, fcb);
446 return STATUS_INSUFFICIENT_RESOURCES;
447 }
448
449 hl2->parent = hl->parent;
450 hl2->index = hl->index;
451
452 hl2->name.Length = hl2->name.MaximumLength = hl->name.Length;
453 hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG);
454
455 if (!hl2->name.Buffer) {
456 ERR("out of memory\n");
457 ExFreePool(hl2);
458 free_fcb(Vcb, fcb);
459 return STATUS_INSUFFICIENT_RESOURCES;
460 }
461
462 RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length);
463
464 hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length;
465 hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG);
466
467 if (!hl2->utf8.Buffer) {
468 ERR("out of memory\n");
469 ExFreePool(hl2->name.Buffer);
470 ExFreePool(hl2);
471 free_fcb(Vcb, fcb);
472 return STATUS_INSUFFICIENT_RESOURCES;
473 }
474
475 RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length);
476
477 InsertTailList(&fcb->hardlinks, &hl2->list_entry);
478
479 le = le->Flink;
480 }
481
482 if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) {
483 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length;
484
485 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
486 if (!fcb->reparse_xattr.Buffer) {
487 ERR("out of memory\n");
488 free_fcb(Vcb, fcb);
489 return STATUS_INSUFFICIENT_RESOURCES;
490 }
491
492 RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
493 }
494
495 if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) {
496 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length;
497
498 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
499 if (!fcb->ea_xattr.Buffer) {
500 ERR("out of memory\n");
501 free_fcb(Vcb, fcb);
502 return STATUS_INSUFFICIENT_RESOURCES;
503 }
504
505 RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
506 }
507
508 fcb->prop_compression = oldfcb->prop_compression;
509
510 le = oldfcb->xattrs.Flink;
511 while (le != &oldfcb->xattrs) {
512 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
513
514 if (xa->valuelen > 0) {
515 xattr* xa2;
516
517 xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG);
518
519 if (!xa2) {
520 ERR("out of memory\n");
521 free_fcb(Vcb, fcb);
522 return STATUS_INSUFFICIENT_RESOURCES;
523 }
524
525 xa2->namelen = xa->namelen;
526 xa2->valuelen = xa->valuelen;
527 xa2->dirty = xa->dirty;
528 memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen);
529
530 InsertTailList(&fcb->xattrs, &xa2->list_entry);
531 }
532
533 le = le->Flink;
534 }
535
536 end:
537 *pfcb = fcb;
538
539 return STATUS_SUCCESS;
540 }
541
542 typedef struct _move_entry {
543 file_ref* fileref;
544 fcb* dummyfcb;
545 file_ref* dummyfileref;
546 struct _move_entry* parent;
547 LIST_ENTRY list_entry;
548 } move_entry;
549
550 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) {
551 NTSTATUS Status;
552 LIST_ENTRY* le;
553
554 ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, TRUE);
555
556 le = me->fileref->fcb->dir_children_index.Flink;
557
558 while (le != &me->fileref->fcb->dir_children_index) {
559 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
560 file_ref* fr;
561 move_entry* me2;
562
563 Status = open_fileref_child(Vcb, me->fileref, &dc->name, TRUE, TRUE, dc->index == 0 ? TRUE : FALSE, PagedPool, &fr, Irp);
564
565 if (!NT_SUCCESS(Status)) {
566 ERR("open_fileref_child returned %08x\n", Status);
567 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
568 return Status;
569 }
570
571 me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
572 if (!me2) {
573 ERR("out of memory\n");
574 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
575 return STATUS_INSUFFICIENT_RESOURCES;
576 }
577
578 me2->fileref = fr;
579 me2->dummyfcb = NULL;
580 me2->dummyfileref = NULL;
581 me2->parent = me;
582
583 InsertHeadList(&me->list_entry, &me2->list_entry);
584
585 le = le->Flink;
586 }
587
588 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
589
590 return STATUS_SUCCESS;
591 }
592
593 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) {
594 UINT8 c;
595
596 c = dc->hash >> 24;
597
598 if (fcb->hash_ptrs[c] == &dc->list_entry_hash) {
599 if (dc->list_entry_hash.Flink == &fcb->dir_children_hash)
600 fcb->hash_ptrs[c] = NULL;
601 else {
602 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash);
603
604 if (dc2->hash >> 24 == c)
605 fcb->hash_ptrs[c] = &dc2->list_entry_hash;
606 else
607 fcb->hash_ptrs[c] = NULL;
608 }
609 }
610
611 RemoveEntryList(&dc->list_entry_hash);
612
613 c = dc->hash_uc >> 24;
614
615 if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) {
616 if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc)
617 fcb->hash_ptrs_uc[c] = NULL;
618 else {
619 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc);
620
621 if (dc2->hash_uc >> 24 == c)
622 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc;
623 else
624 fcb->hash_ptrs_uc[c] = NULL;
625 }
626 }
627
628 RemoveEntryList(&dc->list_entry_hash_uc);
629 }
630
631 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) {
632 NTSTATUS Status;
633 fcb* fcb;
634 SECURITY_SUBJECT_CONTEXT subjcont;
635 PSID owner;
636 BOOLEAN defaulted;
637 LARGE_INTEGER time;
638 BTRFS_TIME now;
639
640 fcb = create_fcb(Vcb, PagedPool);
641 if (!fcb) {
642 ERR("out of memory\n");
643 return STATUS_INSUFFICIENT_RESOURCES;
644 }
645
646 KeQuerySystemTime(&time);
647 win_time_to_unix(time, &now);
648
649 fcb->Vcb = Vcb;
650
651 fcb->subvol = r;
652 fcb->inode = InterlockedIncrement64(&r->lastinode);
653 fcb->type = BTRFS_TYPE_DIRECTORY;
654
655 fcb->inode_item.generation = Vcb->superblock.generation;
656 fcb->inode_item.transid = Vcb->superblock.generation;
657 fcb->inode_item.st_nlink = 1;
658 fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, TRUE);
659 fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now;
660 fcb->inode_item.st_gid = GID_NOBODY;
661
662 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, FALSE, TRUE, NULL);
663
664 SeCaptureSubjectContext(&subjcont);
665
666 Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
667
668 if (!NT_SUCCESS(Status)) {
669 ERR("SeAssignSecurity returned %08x\n", Status);
670 return Status;
671 }
672
673 if (!fcb->sd) {
674 ERR("SeAssignSecurity returned NULL security descriptor\n");
675 return STATUS_INTERNAL_ERROR;
676 }
677
678 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
679 if (!NT_SUCCESS(Status)) {
680 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
681 fcb->inode_item.st_uid = UID_NOBODY;
682 fcb->sd_dirty = TRUE;
683 } else {
684 fcb->inode_item.st_uid = sid_to_uid(owner);
685 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
686 }
687
688 find_gid(fcb, parfcb, &subjcont);
689
690 fcb->inode_item_changed = TRUE;
691
692 InsertTailList(&r->fcbs, &fcb->list_entry);
693 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
694
695 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
696 fcb->Header.AllocationSize.QuadPart = 0;
697 fcb->Header.FileSize.QuadPart = 0;
698 fcb->Header.ValidDataLength.QuadPart = 0;
699
700 fcb->created = TRUE;
701 mark_fcb_dirty(fcb);
702
703 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
704 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
705
706 fcb->prop_compression = parfcb->prop_compression;
707 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
708
709 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
710 if (!fcb->hash_ptrs) {
711 ERR("out of memory\n");
712 return STATUS_INSUFFICIENT_RESOURCES;
713 }
714
715 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
716
717 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
718 if (!fcb->hash_ptrs_uc) {
719 ERR("out of memory\n");
720 return STATUS_INSUFFICIENT_RESOURCES;
721 }
722
723 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
724
725 *pfcb = fcb;
726
727 return STATUS_SUCCESS;
728 }
729
730 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
731 NTSTATUS Status;
732 LIST_ENTRY move_list, *le;
733 move_entry* me;
734 LARGE_INTEGER time;
735 BTRFS_TIME now;
736 file_ref* origparent;
737
738 InitializeListHead(&move_list);
739
740 KeQuerySystemTime(&time);
741 win_time_to_unix(time, &now);
742
743 me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
744
745 if (!me) {
746 ERR("out of memory\n");
747 Status = STATUS_INSUFFICIENT_RESOURCES;
748 goto end;
749 }
750
751 origparent = fileref->parent;
752
753 me->fileref = fileref;
754 increase_fileref_refcount(me->fileref);
755 me->dummyfcb = NULL;
756 me->dummyfileref = NULL;
757 me->parent = NULL;
758
759 InsertTailList(&move_list, &me->list_entry);
760
761 le = move_list.Flink;
762 while (le != &move_list) {
763 me = CONTAINING_RECORD(le, move_entry, list_entry);
764
765 ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE);
766
767 if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
768 Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp);
769
770 if (!NT_SUCCESS(Status)) {
771 ERR("add_children_to_move_list returned %08x\n", Status);
772 goto end;
773 }
774 }
775
776 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
777
778 le = le->Flink;
779 }
780
781 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
782
783 // loop through list and create new inodes
784
785 le = move_list.Flink;
786 while (le != &move_list) {
787 me = CONTAINING_RECORD(le, move_entry, list_entry);
788
789 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) {
790 if (!me->dummyfcb) {
791 ULONG defda;
792 BOOL inserted = FALSE;
793 LIST_ENTRY* le3;
794
795 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
796
797 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb);
798 if (!NT_SUCCESS(Status)) {
799 ERR("duplicate_fcb returned %08x\n", Status);
800 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
801 goto end;
802 }
803
804 me->dummyfcb->subvol = me->fileref->fcb->subvol;
805 me->dummyfcb->inode = me->fileref->fcb->inode;
806
807 if (!me->dummyfcb->ads) {
808 me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty;
809 me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed;
810 me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted;
811 me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed;
812 me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed;
813 me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed;
814 }
815
816 me->dummyfcb->created = me->fileref->fcb->created;
817 me->dummyfcb->deleted = me->fileref->fcb->deleted;
818 mark_fcb_dirty(me->dummyfcb);
819
820 if (!me->fileref->fcb->ads) {
821 LIST_ENTRY* le2;
822
823 me->fileref->fcb->subvol = destdir->fcb->subvol;
824 me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode);
825 me->fileref->fcb->inode_item.st_nlink = 1;
826
827 defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode,
828 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.',
829 TRUE, Irp);
830
831 me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
832 me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
833 me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents);
834 me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer;
835 me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer;
836 me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs);
837 me->fileref->fcb->inode_item_changed = TRUE;
838
839 le2 = me->fileref->fcb->xattrs.Flink;
840 while (le2 != &me->fileref->fcb->xattrs) {
841 xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry);
842
843 xa->dirty = TRUE;
844
845 le2 = le2->Flink;
846 }
847
848 if (le == move_list.Flink) { // first entry
849 me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
850 me->fileref->fcb->inode_item.sequence++;
851
852 if (!ccb->user_set_change_time)
853 me->fileref->fcb->inode_item.st_ctime = now;
854 }
855
856 le2 = me->fileref->fcb->extents.Flink;
857 while (le2 != &me->fileref->fcb->extents) {
858 extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
859
860 if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
861 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
862
863 if (ed2->size != 0) {
864 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address);
865
866 if (!c) {
867 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
868 } else {
869 Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
870 ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
871
872 if (!NT_SUCCESS(Status)) {
873 ERR("update_changed_extent_ref returned %08x\n", Status);
874 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
875 goto end;
876 }
877 }
878
879 }
880 }
881
882 le2 = le2->Flink;
883 }
884 } else {
885 me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol;
886 me->fileref->fcb->inode = me->parent->fileref->fcb->inode;
887 }
888
889 me->fileref->fcb->created = TRUE;
890
891 InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry);
892 RemoveEntryList(&me->fileref->fcb->list_entry);
893
894 le3 = destdir->fcb->subvol->fcbs.Flink;
895 while (le3 != &destdir->fcb->subvol->fcbs) {
896 fcb* fcb = CONTAINING_RECORD(le3, struct _fcb, list_entry);
897
898 if (fcb->inode > me->fileref->fcb->inode) {
899 InsertHeadList(le3->Blink, &me->fileref->fcb->list_entry);
900 inserted = TRUE;
901 break;
902 }
903
904 le3 = le3->Flink;
905 }
906
907 if (!inserted)
908 InsertTailList(&destdir->fcb->subvol->fcbs, &me->fileref->fcb->list_entry);
909
910 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all);
911
912 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) {
913 hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry);
914
915 if (hl->name.Buffer)
916 ExFreePool(hl->name.Buffer);
917
918 if (hl->utf8.Buffer)
919 ExFreePool(hl->utf8.Buffer);
920
921 ExFreePool(hl);
922 }
923
924 me->fileref->fcb->inode_item_changed = TRUE;
925 mark_fcb_dirty(me->fileref->fcb);
926
927 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) {
928 LIST_ENTRY* le2 = le->Flink;
929
930 while (le2 != &move_list) {
931 move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry);
932
933 if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) {
934 me2->dummyfcb = me->dummyfcb;
935 InterlockedIncrement(&me->dummyfcb->refcount);
936 }
937
938 le2 = le2->Flink;
939 }
940 }
941
942 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
943 } else {
944 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
945 me->fileref->fcb->inode_item.st_nlink++;
946 me->fileref->fcb->inode_item_changed = TRUE;
947 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
948 }
949 }
950
951 le = le->Flink;
952 }
953
954 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
955 fileref->fcb->subvol->root_item.ctime = now;
956
957 // loop through list and create new filerefs
958
959 le = move_list.Flink;
960 while (le != &move_list) {
961 hardlink* hl;
962 BOOL name_changed = FALSE;
963
964 me = CONTAINING_RECORD(le, move_entry, list_entry);
965
966 me->dummyfileref = create_fileref(fileref->fcb->Vcb);
967 if (!me->dummyfileref) {
968 ERR("out of memory\n");
969 Status = STATUS_INSUFFICIENT_RESOURCES;
970 goto end;
971 }
972
973 if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) {
974 root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol;
975
976 Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb);
977 if (!NT_SUCCESS(Status)) {
978 ERR("create_directory_fcb returnd %08x\n", Status);
979 goto end;
980 }
981
982 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
983 me->fileref->dc->key.obj_type = TYPE_INODE_ITEM;
984
985 me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb;
986 } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) {
987 me->dummyfileref->fcb = me->fileref->fcb;
988
989 me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id;
990 } else
991 me->dummyfileref->fcb = me->dummyfcb;
992
993 InterlockedIncrement(&me->dummyfileref->fcb->refcount);
994
995 me->dummyfileref->oldutf8 = me->fileref->oldutf8;
996 me->dummyfileref->oldindex = me->fileref->dc->index;
997
998 if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length))
999 name_changed = TRUE;
1000
1001 if ((le == move_list.Flink || me->fileref->fcb->inode == SUBVOL_ROOT_INODE) && !me->dummyfileref->oldutf8.Buffer) {
1002 me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG);
1003 if (!me->dummyfileref->oldutf8.Buffer) {
1004 ERR("out of memory\n");
1005 Status = STATUS_INSUFFICIENT_RESOURCES;
1006 goto end;
1007 }
1008
1009 RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1010
1011 me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length;
1012 }
1013
1014 me->dummyfileref->delete_on_close = me->fileref->delete_on_close;
1015 me->dummyfileref->deleted = me->fileref->deleted;
1016
1017 me->dummyfileref->created = me->fileref->created;
1018 me->fileref->created = TRUE;
1019
1020 me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
1021 increase_fileref_refcount(me->dummyfileref->parent);
1022
1023 ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->nonpaged->children_lock, TRUE);
1024 InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
1025 ExReleaseResourceLite(&me->dummyfileref->parent->nonpaged->children_lock);
1026
1027 me->dummyfileref->debug_desc = me->fileref->debug_desc;
1028
1029 if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY)
1030 me->dummyfileref->fcb->fileref = me->dummyfileref;
1031
1032 if (!me->parent) {
1033 RemoveEntryList(&me->fileref->list_entry);
1034
1035 increase_fileref_refcount(destdir);
1036
1037 if (me->fileref->dc) {
1038 // remove from old parent
1039 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1040 RemoveEntryList(&me->fileref->dc->list_entry_index);
1041 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1042 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1043
1044 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2;
1045 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1046 me->fileref->parent->fcb->inode_item.sequence++;
1047 me->fileref->parent->fcb->inode_item.st_ctime = now;
1048 me->fileref->parent->fcb->inode_item.st_mtime = now;
1049 me->fileref->parent->fcb->inode_item_changed = TRUE;
1050 mark_fcb_dirty(me->fileref->parent->fcb);
1051
1052 if (name_changed) {
1053 ExFreePool(me->fileref->dc->utf8.Buffer);
1054 ExFreePool(me->fileref->dc->name.Buffer);
1055 ExFreePool(me->fileref->dc->name_uc.Buffer);
1056
1057 me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1058 if (!me->fileref->dc->utf8.Buffer) {
1059 ERR("out of memory\n");
1060 Status = STATUS_INSUFFICIENT_RESOURCES;
1061 goto end;
1062 }
1063
1064 me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
1065 RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1066
1067 me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG);
1068 if (!me->fileref->dc->name.Buffer) {
1069 ERR("out of memory\n");
1070 Status = STATUS_INSUFFICIENT_RESOURCES;
1071 goto end;
1072 }
1073
1074 me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length;
1075 RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length);
1076
1077 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1078 if (!NT_SUCCESS(Status)) {
1079 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1080 goto end;
1081 }
1082
1083 me->fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1084 me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length);
1085 }
1086
1087 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM)
1088 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1089
1090 // add to new parent
1091
1092 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, TRUE);
1093
1094 if (IsListEmpty(&destdir->fcb->dir_children_index))
1095 me->fileref->dc->index = 2;
1096 else {
1097 dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1098
1099 me->fileref->dc->index = max(2, dc2->index + 1);
1100 }
1101
1102 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1103 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc);
1104 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
1105 }
1106
1107 free_fileref(fileref->fcb->Vcb, me->fileref->parent);
1108 me->fileref->parent = destdir;
1109
1110 ExAcquireResourceExclusiveLite(&me->fileref->parent->nonpaged->children_lock, TRUE);
1111 InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
1112 ExReleaseResourceLite(&me->fileref->parent->nonpaged->children_lock);
1113
1114 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1115 me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
1116 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1117 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1118 me->fileref->parent->fcb->inode_item.sequence++;
1119 me->fileref->parent->fcb->inode_item.st_ctime = now;
1120 me->fileref->parent->fcb->inode_item.st_mtime = now;
1121 me->fileref->parent->fcb->inode_item_changed = TRUE;
1122 mark_fcb_dirty(me->fileref->parent->fcb);
1123 } else {
1124 if (me->fileref->dc) {
1125 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1126 RemoveEntryList(&me->fileref->dc->list_entry_index);
1127
1128 if (!me->fileref->fcb->ads)
1129 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1130
1131 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1132
1133 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, TRUE);
1134
1135 if (me->fileref->fcb->ads)
1136 InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1137 else {
1138 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE)
1139 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1140
1141 if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index))
1142 me->fileref->dc->index = 2;
1143 else {
1144 dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1145
1146 me->fileref->dc->index = max(2, dc2->index + 1);
1147 }
1148
1149 InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1150 insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc);
1151 }
1152
1153 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock);
1154 }
1155 }
1156
1157 if (!me->dummyfileref->fcb->ads) {
1158 Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
1159 if (!NT_SUCCESS(Status)) {
1160 ERR("delete_fileref returned %08x\n", Status);
1161 goto end;
1162 }
1163 }
1164
1165 if (me->fileref->fcb->inode_item.st_nlink > 1) {
1166 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1167 if (!hl) {
1168 ERR("out of memory\n");
1169 Status = STATUS_INSUFFICIENT_RESOURCES;
1170 goto end;
1171 }
1172
1173 hl->parent = me->fileref->parent->fcb->inode;
1174 hl->index = me->fileref->dc->index;
1175
1176 hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length;
1177 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1178 if (!hl->utf8.Buffer) {
1179 ERR("out of memory\n");
1180 Status = STATUS_INSUFFICIENT_RESOURCES;
1181 ExFreePool(hl);
1182 goto end;
1183 }
1184
1185 RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1186
1187 hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length;
1188 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1189 if (!hl->name.Buffer) {
1190 ERR("out of memory\n");
1191 Status = STATUS_INSUFFICIENT_RESOURCES;
1192 ExFreePool(hl->utf8.Buffer);
1193 ExFreePool(hl);
1194 goto end;
1195 }
1196
1197 RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1198
1199 InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry);
1200 }
1201
1202 mark_fileref_dirty(me->fileref);
1203
1204 le = le->Flink;
1205 }
1206
1207 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1208
1209 le = move_list.Flink;
1210 while (le != &move_list) {
1211 me = CONTAINING_RECORD(le, move_entry, list_entry);
1212
1213 if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
1214 Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
1215 if (!NT_SUCCESS(Status)) {
1216 ERR("delete_fileref returned %08x\n", Status);
1217 goto end;
1218 }
1219 }
1220
1221 le = le->Flink;
1222 }
1223
1224 destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation;
1225 destdir->fcb->subvol->root_item.ctime = now;
1226
1227 me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry);
1228 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1229 send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1230 send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1231
1232 Status = STATUS_SUCCESS;
1233
1234 end:
1235 while (!IsListEmpty(&move_list)) {
1236 le = RemoveHeadList(&move_list);
1237 me = CONTAINING_RECORD(le, move_entry, list_entry);
1238
1239 if (me->dummyfcb)
1240 free_fcb(fileref->fcb->Vcb, me->dummyfcb);
1241
1242 if (me->dummyfileref)
1243 free_fileref(fileref->fcb->Vcb, me->dummyfileref);
1244
1245 free_fileref(fileref->fcb->Vcb, me->fileref);
1246
1247 ExFreePool(me);
1248 }
1249
1250 return Status;
1251 }
1252
1253 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
1254 BOOL inserted;
1255 LIST_ENTRY* le;
1256 UINT8 c, d;
1257
1258 c = dc->hash >> 24;
1259
1260 inserted = FALSE;
1261
1262 d = c;
1263 do {
1264 le = fcb->hash_ptrs[d];
1265
1266 if (d == 0)
1267 break;
1268
1269 d--;
1270 } while (!le);
1271
1272 if (!le)
1273 le = fcb->dir_children_hash.Flink;
1274
1275 while (le != &fcb->dir_children_hash) {
1276 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
1277
1278 if (dc2->hash > dc->hash) {
1279 InsertHeadList(le->Blink, &dc->list_entry_hash);
1280 inserted = TRUE;
1281 break;
1282 }
1283
1284 le = le->Flink;
1285 }
1286
1287 if (!inserted)
1288 InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash);
1289
1290 if (!fcb->hash_ptrs[c])
1291 fcb->hash_ptrs[c] = &dc->list_entry_hash;
1292 else {
1293 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash);
1294
1295 if (dc2->hash > dc->hash)
1296 fcb->hash_ptrs[c] = &dc->list_entry_hash;
1297 }
1298
1299 c = dc->hash_uc >> 24;
1300
1301 inserted = FALSE;
1302
1303 d = c;
1304 do {
1305 le = fcb->hash_ptrs_uc[d];
1306
1307 if (d == 0)
1308 break;
1309
1310 d--;
1311 } while (!le);
1312
1313 if (!le)
1314 le = fcb->dir_children_hash_uc.Flink;
1315
1316 while (le != &fcb->dir_children_hash_uc) {
1317 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
1318
1319 if (dc2->hash_uc > dc->hash_uc) {
1320 InsertHeadList(le->Blink, &dc->list_entry_hash_uc);
1321 inserted = TRUE;
1322 break;
1323 }
1324
1325 le = le->Flink;
1326 }
1327
1328 if (!inserted)
1329 InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc);
1330
1331 if (!fcb->hash_ptrs_uc[c])
1332 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1333 else {
1334 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc);
1335
1336 if (dc2->hash_uc > dc->hash_uc)
1337 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1338 }
1339 }
1340
1341 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
1342 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1343 FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
1344 fcb *fcb = FileObject->FsContext;
1345 ccb* ccb = FileObject->FsContext2;
1346 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
1347 WCHAR* fn;
1348 ULONG fnlen, utf8len, origutf8len;
1349 UNICODE_STRING fnus;
1350 ANSI_STRING utf8;
1351 NTSTATUS Status;
1352 LARGE_INTEGER time;
1353 BTRFS_TIME now;
1354 LIST_ENTRY rollback, *le;
1355 hardlink* hl;
1356 SECURITY_SUBJECT_CONTEXT subjcont;
1357 ACCESS_MASK access;
1358
1359 InitializeListHead(&rollback);
1360
1361 TRACE("tfo = %p\n", tfo);
1362 TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
1363 TRACE("RootDirectory = %p\n", fri->RootDirectory);
1364 TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
1365
1366 fn = fri->FileName;
1367 fnlen = fri->FileNameLength / sizeof(WCHAR);
1368
1369 if (!tfo) {
1370 if (!fileref || !fileref->parent) {
1371 ERR("no fileref set and no directory given\n");
1372 return STATUS_INVALID_PARAMETER;
1373 }
1374 } else {
1375 LONG i;
1376
1377 while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
1378 fnlen--;
1379
1380 if (fnlen == 0)
1381 return STATUS_INVALID_PARAMETER;
1382
1383 for (i = fnlen - 1; i >= 0; i--) {
1384 if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
1385 fn = &fri->FileName[i+1];
1386 fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
1387 break;
1388 }
1389 }
1390 }
1391
1392 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1393 acquire_fcb_lock_exclusive(Vcb);
1394 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1395
1396 if (fcb->ads) {
1397 // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
1398 // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
1399 // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
1400 Status = STATUS_INVALID_PARAMETER;
1401 goto end;
1402 }
1403
1404 fnus.Buffer = fn;
1405 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
1406
1407 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
1408
1409 origutf8len = fileref->dc->utf8.Length;
1410
1411 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1412 if (!NT_SUCCESS(Status))
1413 goto end;
1414
1415 utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
1416 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
1417 if (!utf8.Buffer) {
1418 ERR("out of memory\n");
1419 Status = STATUS_INSUFFICIENT_RESOURCES;
1420 goto end;
1421 }
1422
1423 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1424 if (!NT_SUCCESS(Status))
1425 goto end;
1426
1427 if (tfo && tfo->FsContext2) {
1428 struct _ccb* relatedccb = tfo->FsContext2;
1429
1430 related = relatedccb->fileref;
1431 increase_fileref_refcount(related);
1432 } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') {
1433 related = fileref->parent;
1434 increase_fileref_refcount(related);
1435 }
1436
1437 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
1438
1439 if (NT_SUCCESS(Status)) {
1440 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
1441
1442 if (fileref != oldfileref && !oldfileref->deleted) {
1443 if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
1444 Status = STATUS_OBJECT_NAME_COLLISION;
1445 goto end;
1446 } else if ((oldfileref->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
1447 WARN("trying to overwrite open file\n");
1448 Status = STATUS_ACCESS_DENIED;
1449 goto end;
1450 }
1451
1452 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
1453 WARN("trying to overwrite directory\n");
1454 Status = STATUS_ACCESS_DENIED;
1455 goto end;
1456 }
1457 }
1458
1459 if (fileref == oldfileref || oldfileref->deleted) {
1460 free_fileref(Vcb, oldfileref);
1461 oldfileref = NULL;
1462 }
1463 }
1464
1465 if (!related) {
1466 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
1467
1468 if (!NT_SUCCESS(Status)) {
1469 ERR("open_fileref returned %08x\n", Status);
1470 goto end;
1471 }
1472 }
1473
1474 if (related->fcb == Vcb->dummy_fcb) {
1475 Status = STATUS_ACCESS_DENIED;
1476 goto end;
1477 }
1478
1479 SeCaptureSubjectContext(&subjcont);
1480
1481 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
1482 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
1483 SeReleaseSubjectContext(&subjcont);
1484 TRACE("SeAccessCheck failed, returning %08x\n", Status);
1485 goto end;
1486 }
1487
1488 SeReleaseSubjectContext(&subjcont);
1489
1490 if (has_open_children(fileref)) {
1491 WARN("trying to rename file with open children\n");
1492 Status = STATUS_ACCESS_DENIED;
1493 goto end;
1494 }
1495
1496 if (oldfileref) {
1497 SeCaptureSubjectContext(&subjcont);
1498
1499 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
1500 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
1501 SeReleaseSubjectContext(&subjcont);
1502 TRACE("SeAccessCheck failed, returning %08x\n", Status);
1503 goto end;
1504 }
1505
1506 SeReleaseSubjectContext(&subjcont);
1507
1508 Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
1509 if (!NT_SUCCESS(Status)) {
1510 ERR("delete_fileref returned %08x\n", Status);
1511 goto end;
1512 }
1513 }
1514
1515 if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) {
1516 Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback);
1517 if (!NT_SUCCESS(Status)) {
1518 ERR("move_across_subvols returned %08x\n", Status);
1519 }
1520 goto end;
1521 }
1522
1523 if (related == fileref->parent) { // keeping file in same directory
1524 UNICODE_STRING oldfn, newfn;
1525 USHORT name_offset;
1526 ULONG reqlen, oldutf8len;
1527
1528 oldfn.Length = oldfn.MaximumLength = 0;
1529
1530 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
1531 if (Status != STATUS_BUFFER_OVERFLOW) {
1532 ERR("fileref_get_filename returned %08x\n", Status);
1533 goto end;
1534 }
1535
1536 oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1537 if (!oldfn.Buffer) {
1538 ERR("out of memory\n");
1539 Status = STATUS_INSUFFICIENT_RESOURCES;
1540 goto end;
1541 }
1542
1543 oldfn.MaximumLength = (UINT16)reqlen;
1544
1545 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
1546 if (!NT_SUCCESS(Status)) {
1547 ERR("fileref_get_filename returned %08x\n", Status);
1548 ExFreePool(oldfn.Buffer);
1549 goto end;
1550 }
1551
1552 oldutf8len = fileref->dc->utf8.Length;
1553
1554 if (!fileref->created && !fileref->oldutf8.Buffer) {
1555 fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
1556 if (!fileref->oldutf8.Buffer) {
1557 ERR("out of memory\n");
1558 Status = STATUS_INSUFFICIENT_RESOURCES;
1559 goto end;
1560 }
1561
1562 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length;
1563 RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1564 }
1565
1566 TRACE("renaming %.*S to %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer, fnus.Length / sizeof(WCHAR), fnus.Buffer);
1567
1568 mark_fileref_dirty(fileref);
1569
1570 if (fileref->dc) {
1571 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1572
1573 ExFreePool(fileref->dc->utf8.Buffer);
1574 ExFreePool(fileref->dc->name.Buffer);
1575 ExFreePool(fileref->dc->name_uc.Buffer);
1576
1577 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
1578 if (!fileref->dc->utf8.Buffer) {
1579 ERR("out of memory\n");
1580 Status = STATUS_INSUFFICIENT_RESOURCES;
1581 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1582 ExFreePool(oldfn.Buffer);
1583 goto end;
1584 }
1585
1586 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
1587 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
1588
1589 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
1590 if (!fileref->dc->name.Buffer) {
1591 ERR("out of memory\n");
1592 Status = STATUS_INSUFFICIENT_RESOURCES;
1593 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1594 ExFreePool(oldfn.Buffer);
1595 goto end;
1596 }
1597
1598 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
1599 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
1600
1601 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1602 if (!NT_SUCCESS(Status)) {
1603 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1604 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1605 ExFreePool(oldfn.Buffer);
1606 goto end;
1607 }
1608
1609 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
1610
1611 fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
1612 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
1613
1614 insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc);
1615
1616 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1617 }
1618
1619 newfn.Length = newfn.MaximumLength = 0;
1620
1621 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
1622 if (Status != STATUS_BUFFER_OVERFLOW) {
1623 ERR("fileref_get_filename returned %08x\n", Status);
1624 ExFreePool(oldfn.Buffer);
1625 goto end;
1626 }
1627
1628 newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1629 if (!newfn.Buffer) {
1630 ERR("out of memory\n");
1631 Status = STATUS_INSUFFICIENT_RESOURCES;
1632 ExFreePool(oldfn.Buffer);
1633 goto end;
1634 }
1635
1636 newfn.MaximumLength = (UINT16)reqlen;
1637
1638 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
1639 if (!NT_SUCCESS(Status)) {
1640 ERR("fileref_get_filename returned %08x\n", Status);
1641 ExFreePool(oldfn.Buffer);
1642 ExFreePool(newfn.Buffer);
1643 goto end;
1644 }
1645
1646 KeQuerySystemTime(&time);
1647 win_time_to_unix(time, &now);
1648
1649 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
1650 fcb->inode_item.transid = Vcb->superblock.generation;
1651 fcb->inode_item.sequence++;
1652
1653 if (!ccb->user_set_change_time)
1654 fcb->inode_item.st_ctime = now;
1655
1656 fcb->inode_item_changed = TRUE;
1657 mark_fcb_dirty(fcb);
1658 }
1659
1660 // update parent's INODE_ITEM
1661
1662 related->fcb->inode_item.transid = Vcb->superblock.generation;
1663 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1664 related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len);
1665 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1666 related->fcb->inode_item.sequence++;
1667 related->fcb->inode_item.st_ctime = now;
1668 related->fcb->inode_item.st_mtime = now;
1669
1670 related->fcb->inode_item_changed = TRUE;
1671 mark_fcb_dirty(related->fcb);
1672 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1673
1674 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL,
1675 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL);
1676 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL,
1677 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL);
1678
1679 ExFreePool(oldfn.Buffer);
1680 ExFreePool(newfn.Buffer);
1681
1682 Status = STATUS_SUCCESS;
1683 goto end;
1684 }
1685
1686 // We move files by moving the existing fileref to the new directory, and
1687 // replacing it with a dummy fileref with the same original values, but marked as deleted.
1688
1689 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
1690
1691 fr2 = create_fileref(Vcb);
1692
1693 fr2->fcb = fileref->fcb;
1694 fr2->fcb->refcount++;
1695
1696 fr2->oldutf8 = fileref->oldutf8;
1697 fr2->oldindex = fileref->dc->index;
1698 fr2->delete_on_close = fileref->delete_on_close;
1699 fr2->deleted = TRUE;
1700 fr2->created = fileref->created;
1701 fr2->parent = fileref->parent;
1702 fr2->dc = NULL;
1703
1704 if (!fr2->oldutf8.Buffer) {
1705 fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
1706 if (!fr2->oldutf8.Buffer) {
1707 ERR("out of memory\n");
1708 Status = STATUS_INSUFFICIENT_RESOURCES;
1709 goto end;
1710 }
1711
1712 RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1713
1714 fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length;
1715 }
1716
1717 if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
1718 fr2->fcb->fileref = fr2;
1719
1720 if (fileref->fcb->inode == SUBVOL_ROOT_INODE)
1721 fileref->fcb->subvol->parent = related->fcb->subvol->id;
1722
1723 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
1724 fileref->oldutf8.Buffer = NULL;
1725 fileref->deleted = FALSE;
1726 fileref->created = TRUE;
1727 fileref->parent = related;
1728
1729 ExAcquireResourceExclusiveLite(&fileref->parent->nonpaged->children_lock, TRUE);
1730 InsertHeadList(&fileref->list_entry, &fr2->list_entry);
1731 RemoveEntryList(&fileref->list_entry);
1732 ExReleaseResourceLite(&fileref->parent->nonpaged->children_lock);
1733
1734 mark_fileref_dirty(fr2);
1735 mark_fileref_dirty(fileref);
1736
1737 if (fileref->dc) {
1738 // remove from old parent
1739 ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, TRUE);
1740 RemoveEntryList(&fileref->dc->list_entry_index);
1741 remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc);
1742 ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock);
1743
1744 if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) {
1745 // handle changed name
1746
1747 ExFreePool(fileref->dc->utf8.Buffer);
1748 ExFreePool(fileref->dc->name.Buffer);
1749 ExFreePool(fileref->dc->name_uc.Buffer);
1750
1751 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
1752 if (!fileref->dc->utf8.Buffer) {
1753 ERR("out of memory\n");
1754 Status = STATUS_INSUFFICIENT_RESOURCES;
1755 goto end;
1756 }
1757
1758 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
1759 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
1760
1761 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
1762 if (!fileref->dc->name.Buffer) {
1763 ERR("out of memory\n");
1764 Status = STATUS_INSUFFICIENT_RESOURCES;
1765 goto end;
1766 }
1767
1768 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
1769 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
1770
1771 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1772 if (!NT_SUCCESS(Status)) {
1773 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1774 goto end;
1775 }
1776
1777 fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
1778 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
1779 }
1780
1781 // add to new parent
1782 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
1783
1784 if (IsListEmpty(&related->fcb->dir_children_index))
1785 fileref->dc->index = 2;
1786 else {
1787 dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1788
1789 fileref->dc->index = max(2, dc2->index + 1);
1790 }
1791
1792 InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index);
1793 insert_dir_child_into_hash_lists(related->fcb, fileref->dc);
1794 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
1795 }
1796
1797 ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
1798 InsertTailList(&related->children, &fileref->list_entry);
1799 ExReleaseResourceLite(&related->nonpaged->children_lock);
1800
1801 if (fcb->inode_item.st_nlink > 1) {
1802 // add new hardlink entry to fcb
1803
1804 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1805 if (!hl) {
1806 ERR("out of memory\n");
1807 Status = STATUS_INSUFFICIENT_RESOURCES;
1808 goto end;
1809 }
1810
1811 hl->parent = related->fcb->inode;
1812 hl->index = fileref->dc->index;
1813
1814 hl->name.Length = hl->name.MaximumLength = fnus.Length;
1815 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1816
1817 if (!hl->name.Buffer) {
1818 ERR("out of memory\n");
1819 ExFreePool(hl);
1820 Status = STATUS_INSUFFICIENT_RESOURCES;
1821 goto end;
1822 }
1823
1824 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
1825
1826 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
1827 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1828
1829 if (!hl->utf8.Buffer) {
1830 ERR("out of memory\n");
1831 ExFreePool(hl->name.Buffer);
1832 ExFreePool(hl);
1833 Status = STATUS_INSUFFICIENT_RESOURCES;
1834 goto end;
1835 }
1836
1837 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1838
1839 InsertTailList(&fcb->hardlinks, &hl->list_entry);
1840 }
1841
1842 // delete old hardlink entry from fcb
1843
1844 le = fcb->hardlinks.Flink;
1845 while (le != &fcb->hardlinks) {
1846 hl = CONTAINING_RECORD(le, hardlink, list_entry);
1847
1848 if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) {
1849 RemoveEntryList(&hl->list_entry);
1850
1851 if (hl->utf8.Buffer)
1852 ExFreePool(hl->utf8.Buffer);
1853
1854 if (hl->name.Buffer)
1855 ExFreePool(hl->name.Buffer);
1856
1857 ExFreePool(hl);
1858 break;
1859 }
1860
1861 le = le->Flink;
1862 }
1863
1864 // update inode's INODE_ITEM
1865
1866 KeQuerySystemTime(&time);
1867 win_time_to_unix(time, &now);
1868
1869 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
1870 fcb->inode_item.transid = Vcb->superblock.generation;
1871 fcb->inode_item.sequence++;
1872
1873 if (!ccb->user_set_change_time)
1874 fcb->inode_item.st_ctime = now;
1875
1876 fcb->inode_item_changed = TRUE;
1877 mark_fcb_dirty(fcb);
1878 }
1879
1880 // update new parent's INODE_ITEM
1881
1882 related->fcb->inode_item.transid = Vcb->superblock.generation;
1883 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1884 related->fcb->inode_item.st_size += 2 * utf8len;
1885 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1886 related->fcb->inode_item.sequence++;
1887 related->fcb->inode_item.st_ctime = now;
1888 related->fcb->inode_item.st_mtime = now;
1889
1890 related->fcb->inode_item_changed = TRUE;
1891 mark_fcb_dirty(related->fcb);
1892
1893 // update old parent's INODE_ITEM
1894
1895 fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation;
1896 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
1897 fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len;
1898 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
1899 fr2->parent->fcb->inode_item.sequence++;
1900 fr2->parent->fcb->inode_item.st_ctime = now;
1901 fr2->parent->fcb->inode_item.st_mtime = now;
1902
1903 free_fileref(Vcb, fr2);
1904
1905 fr2->parent->fcb->inode_item_changed = TRUE;
1906 mark_fcb_dirty(fr2->parent->fcb);
1907
1908 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1909 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1910 send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1911
1912 Status = STATUS_SUCCESS;
1913
1914 end:
1915 if (oldfileref)
1916 free_fileref(Vcb, oldfileref);
1917
1918 if (!NT_SUCCESS(Status) && related)
1919 free_fileref(Vcb, related);
1920
1921 if (!NT_SUCCESS(Status) && fr2)
1922 free_fileref(Vcb, fr2);
1923
1924 if (NT_SUCCESS(Status))
1925 clear_rollback(&rollback);
1926 else
1927 do_rollback(Vcb, &rollback);
1928
1929 ExReleaseResourceLite(fcb->Header.Resource);
1930 release_fcb_lock(Vcb);
1931 ExReleaseResourceLite(&Vcb->tree_lock);
1932
1933 return Status;
1934 }
1935
1936 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, UINT16 end, fcb* fcb, file_ref* fileref, BOOL advance_only) {
1937 LARGE_INTEGER time;
1938 BTRFS_TIME now;
1939
1940 TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adsdata.Length);
1941
1942 if (!fileref || !fileref->parent) {
1943 ERR("no fileref for stream\n");
1944 return STATUS_INTERNAL_ERROR;
1945 }
1946
1947 if (end < fcb->adsdata.Length) {
1948 if (advance_only)
1949 return STATUS_SUCCESS;
1950
1951 TRACE("truncating stream to %llx bytes\n", end);
1952
1953 fcb->adsdata.Length = end;
1954 } else if (end > fcb->adsdata.Length) {
1955 TRACE("extending stream to %llx bytes\n", end);
1956
1957 if (end > fcb->adsmaxlen) {
1958 ERR("error - xattr too long (%u > %u)\n", end, fcb->adsmaxlen);
1959 return STATUS_DISK_FULL;
1960 }
1961
1962 if (end > fcb->adsdata.MaximumLength) {
1963 char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
1964 if (!data) {
1965 ERR("out of memory\n");
1966 ExFreePool(data);
1967 return STATUS_INSUFFICIENT_RESOURCES;
1968 }
1969
1970 if (fcb->adsdata.Buffer) {
1971 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length);
1972 ExFreePool(fcb->adsdata.Buffer);
1973 }
1974
1975 fcb->adsdata.Buffer = data;
1976 fcb->adsdata.MaximumLength = end;
1977 }
1978
1979 RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length);
1980
1981 fcb->adsdata.Length = end;
1982 }
1983
1984 mark_fcb_dirty(fcb);
1985
1986 fcb->Header.AllocationSize.QuadPart = end;
1987 fcb->Header.FileSize.QuadPart = end;
1988 fcb->Header.ValidDataLength.QuadPart = end;
1989
1990 KeQuerySystemTime(&time);
1991 win_time_to_unix(time, &now);
1992
1993 fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
1994 fileref->parent->fcb->inode_item.sequence++;
1995 fileref->parent->fcb->inode_item.st_ctime = now;
1996
1997 fileref->parent->fcb->inode_item_changed = TRUE;
1998 mark_fcb_dirty(fileref->parent->fcb);
1999
2000 fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2001 fileref->parent->fcb->subvol->root_item.ctime = now;
2002
2003 return STATUS_SUCCESS;
2004 }
2005
2006 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, BOOL prealloc) {
2007 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
2008 fcb* fcb = FileObject->FsContext;
2009 ccb* ccb = FileObject->FsContext2;
2010 file_ref* fileref = ccb ? ccb->fileref : NULL;
2011 NTSTATUS Status;
2012 LARGE_INTEGER time;
2013 CC_FILE_SIZES ccfs;
2014 LIST_ENTRY rollback;
2015 BOOL set_size = FALSE;
2016 ULONG filter;
2017
2018 if (!fileref) {
2019 ERR("fileref is NULL\n");
2020 return STATUS_INVALID_PARAMETER;
2021 }
2022
2023 InitializeListHead(&rollback);
2024
2025 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2026
2027 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2028
2029 if (fileref ? fileref->deleted : fcb->deleted) {
2030 Status = STATUS_FILE_CLOSED;
2031 goto end;
2032 }
2033
2034 if (fcb->ads) {
2035 if (feofi->EndOfFile.QuadPart > 0xffff) {
2036 Status = STATUS_DISK_FULL;
2037 goto end;
2038 }
2039
2040 if (feofi->EndOfFile.QuadPart < 0) {
2041 Status = STATUS_INVALID_PARAMETER;
2042 goto end;
2043 }
2044
2045 Status = stream_set_end_of_file_information(Vcb, (UINT16)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only);
2046
2047 if (NT_SUCCESS(Status)) {
2048 ccfs.AllocationSize = fcb->Header.AllocationSize;
2049 ccfs.FileSize = fcb->Header.FileSize;
2050 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2051 set_size = TRUE;
2052 }
2053
2054 filter = FILE_NOTIFY_CHANGE_STREAM_SIZE;
2055
2056 if (!ccb->user_set_write_time) {
2057 KeQuerySystemTime(&time);
2058 win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime);
2059 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2060
2061 fileref->parent->fcb->inode_item_changed = TRUE;
2062 mark_fcb_dirty(fileref->parent->fcb);
2063 }
2064
2065 send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
2066
2067 goto end;
2068 }
2069
2070 TRACE("file: %S\n", file_desc(FileObject));
2071 TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
2072 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2073 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2074
2075 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
2076
2077 if ((UINT64)feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
2078 if (advance_only) {
2079 Status = STATUS_SUCCESS;
2080 goto end;
2081 }
2082
2083 TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2084
2085 if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) {
2086 Status = STATUS_USER_MAPPED_FILE;
2087 goto end;
2088 }
2089
2090 Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
2091 if (!NT_SUCCESS(Status)) {
2092 ERR("error - truncate_file failed\n");
2093 goto end;
2094 }
2095 } else if ((UINT64)feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
2096 if (Irp->Flags & IRP_PAGING_IO) {
2097 TRACE("paging IO tried to extend file size\n");
2098 Status = STATUS_SUCCESS;
2099 goto end;
2100 }
2101
2102 TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2103
2104 Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback);
2105 if (!NT_SUCCESS(Status)) {
2106 ERR("error - extend_file failed\n");
2107 goto end;
2108 }
2109 } else if ((UINT64)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
2110 Status = STATUS_SUCCESS;
2111 goto end;
2112 }
2113
2114 ccfs.AllocationSize = fcb->Header.AllocationSize;
2115 ccfs.FileSize = fcb->Header.FileSize;
2116 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2117 set_size = TRUE;
2118
2119 filter = FILE_NOTIFY_CHANGE_SIZE;
2120
2121 if (!ccb->user_set_write_time) {
2122 KeQuerySystemTime(&time);
2123 win_time_to_unix(time, &fcb->inode_item.st_mtime);
2124 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2125 }
2126
2127 fcb->inode_item_changed = TRUE;
2128 mark_fcb_dirty(fcb);
2129 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2130
2131 Status = STATUS_SUCCESS;
2132
2133 end:
2134 if (NT_SUCCESS(Status))
2135 clear_rollback(&rollback);
2136 else
2137 do_rollback(Vcb, &rollback);
2138
2139 ExReleaseResourceLite(fcb->Header.Resource);
2140
2141 if (set_size) {
2142 _SEH2_TRY {
2143 CcSetFileSizes(FileObject, &ccfs);
2144 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2145 Status = _SEH2_GetExceptionCode();
2146 } _SEH2_END;
2147
2148 if (!NT_SUCCESS(Status))
2149 ERR("CcSetFileSizes threw exception %08x\n", Status);
2150 }
2151
2152 ExReleaseResourceLite(&Vcb->tree_lock);
2153
2154 return Status;
2155 }
2156
2157 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
2158 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2159
2160 TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
2161
2162 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2163
2164 FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
2165
2166 return STATUS_SUCCESS;
2167 }
2168
2169 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
2170 FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
2171 fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
2172 ccb* ccb = FileObject->FsContext2;
2173 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
2174 WCHAR* fn;
2175 ULONG fnlen, utf8len;
2176 UNICODE_STRING fnus;
2177 ANSI_STRING utf8;
2178 NTSTATUS Status;
2179 LARGE_INTEGER time;
2180 BTRFS_TIME now;
2181 LIST_ENTRY rollback;
2182 hardlink* hl;
2183 ACCESS_MASK access;
2184 SECURITY_SUBJECT_CONTEXT subjcont;
2185 dir_child* dc = NULL;
2186
2187 InitializeListHead(&rollback);
2188
2189 // FIXME - check fli length
2190 // FIXME - don't ignore fli->RootDirectory
2191
2192 TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists);
2193 TRACE("RootDirectory = %p\n", fli->RootDirectory);
2194 TRACE("FileNameLength = %x\n", fli->FileNameLength);
2195 TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
2196
2197 fn = fli->FileName;
2198 fnlen = fli->FileNameLength / sizeof(WCHAR);
2199
2200 if (!tfo) {
2201 if (!fileref || !fileref->parent) {
2202 ERR("no fileref set and no directory given\n");
2203 return STATUS_INVALID_PARAMETER;
2204 }
2205
2206 parfcb = fileref->parent->fcb;
2207 tfofcb = NULL;
2208 } else {
2209 LONG i;
2210
2211 tfofcb = tfo->FsContext;
2212 parfcb = tfofcb;
2213
2214 while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
2215 fnlen--;
2216
2217 if (fnlen == 0)
2218 return STATUS_INVALID_PARAMETER;
2219
2220 for (i = fnlen - 1; i >= 0; i--) {
2221 if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
2222 fn = &fli->FileName[i+1];
2223 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
2224 break;
2225 }
2226 }
2227 }
2228
2229 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2230 acquire_fcb_lock_exclusive(Vcb);
2231 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2232
2233 if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2234 WARN("tried to create hard link on directory\n");
2235 Status = STATUS_FILE_IS_A_DIRECTORY;
2236 goto end;
2237 }
2238
2239 if (fcb->ads) {
2240 WARN("tried to create hard link on stream\n");
2241 Status = STATUS_INVALID_PARAMETER;
2242 goto end;
2243 }
2244
2245 if (fcb->inode_item.st_nlink >= 65535) {
2246 Status = STATUS_TOO_MANY_LINKS;
2247 goto end;
2248 }
2249
2250 fnus.Buffer = fn;
2251 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
2252
2253 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
2254
2255 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2256 if (!NT_SUCCESS(Status))
2257 goto end;
2258
2259 utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
2260 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2261 if (!utf8.Buffer) {
2262 ERR("out of memory\n");
2263 Status = STATUS_INSUFFICIENT_RESOURCES;
2264 goto end;
2265 }
2266
2267 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2268 if (!NT_SUCCESS(Status))
2269 goto end;
2270
2271 if (tfo && tfo->FsContext2) {
2272 struct _ccb* relatedccb = tfo->FsContext2;
2273
2274 related = relatedccb->fileref;
2275 increase_fileref_refcount(related);
2276 }
2277
2278 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2279
2280 if (NT_SUCCESS(Status)) {
2281 if (!oldfileref->deleted) {
2282 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
2283
2284 if (!fli->ReplaceIfExists) {
2285 Status = STATUS_OBJECT_NAME_COLLISION;
2286 goto end;
2287 } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) {
2288 WARN("trying to overwrite open file\n");
2289 Status = STATUS_ACCESS_DENIED;
2290 goto end;
2291 } else if (fileref == oldfileref) {
2292 Status = STATUS_ACCESS_DENIED;
2293 goto end;
2294 }
2295
2296 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
2297 WARN("trying to overwrite directory\n");
2298 Status = STATUS_ACCESS_DENIED;
2299 goto end;
2300 }
2301 } else {
2302 free_fileref(Vcb, oldfileref);
2303 oldfileref = NULL;
2304 }
2305 }
2306
2307 if (!related) {
2308 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2309
2310 if (!NT_SUCCESS(Status)) {
2311 ERR("open_fileref returned %08x\n", Status);
2312 goto end;
2313 }
2314 }
2315
2316 SeCaptureSubjectContext(&subjcont);
2317
2318 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL,
2319 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2320 SeReleaseSubjectContext(&subjcont);
2321 TRACE("SeAccessCheck failed, returning %08x\n", Status);
2322 goto end;
2323 }
2324
2325 SeReleaseSubjectContext(&subjcont);
2326
2327 if (fcb->subvol != parfcb->subvol) {
2328 WARN("can't create hard link over subvolume boundary\n");
2329 Status = STATUS_INVALID_PARAMETER;
2330 goto end;
2331 }
2332
2333 if (oldfileref) {
2334 SeCaptureSubjectContext(&subjcont);
2335
2336 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
2337 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2338 SeReleaseSubjectContext(&subjcont);
2339 TRACE("SeAccessCheck failed, returning %08x\n", Status);
2340 goto end;
2341 }
2342
2343 SeReleaseSubjectContext(&subjcont);
2344
2345 Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
2346 if (!NT_SUCCESS(Status)) {
2347 ERR("delete_fileref returned %08x\n", Status);
2348 goto end;
2349 }
2350 }
2351
2352 fr2 = create_fileref(Vcb);
2353
2354 fr2->fcb = fcb;
2355 fcb->refcount++;
2356
2357 fr2->created = TRUE;
2358 fr2->parent = related;
2359
2360 Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc);
2361 if (!NT_SUCCESS(Status))
2362 WARN("add_dir_child returned %08x\n", Status);
2363
2364 fr2->dc = dc;
2365 dc->fileref = fr2;
2366
2367 ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
2368 InsertTailList(&related->children, &fr2->list_entry);
2369 ExReleaseResourceLite(&related->nonpaged->children_lock);
2370
2371 // add hardlink for existing fileref, if it's not there already
2372 if (IsListEmpty(&fcb->hardlinks)) {
2373 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2374 if (!hl) {
2375 ERR("out of memory\n");
2376 Status = STATUS_INSUFFICIENT_RESOURCES;
2377 goto end;
2378 }
2379
2380 hl->parent = fileref->parent->fcb->inode;
2381 hl->index = fileref->dc->index;
2382
2383 hl->name.Length = hl->name.MaximumLength = fnus.Length;
2384 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2385
2386 if (!hl->name.Buffer) {
2387 ERR("out of memory\n");
2388 ExFreePool(hl);
2389 Status = STATUS_INSUFFICIENT_RESOURCES;
2390 goto end;
2391 }
2392
2393 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2394
2395 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
2396 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2397
2398 if (!hl->utf8.Buffer) {
2399 ERR("out of memory\n");
2400 ExFreePool(hl->name.Buffer);
2401 ExFreePool(hl);
2402 Status = STATUS_INSUFFICIENT_RESOURCES;
2403 goto end;
2404 }
2405
2406 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2407
2408 InsertTailList(&fcb->hardlinks, &hl->list_entry);
2409 }
2410
2411 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2412 if (!hl) {
2413 ERR("out of memory\n");
2414 Status = STATUS_INSUFFICIENT_RESOURCES;
2415 goto end;
2416 }
2417
2418 hl->parent = related->fcb->inode;
2419 hl->index = dc->index;
2420
2421 hl->name.Length = hl->name.MaximumLength = fnus.Length;
2422 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
2423
2424 if (!hl->name.Buffer) {
2425 ERR("out of memory\n");
2426 ExFreePool(hl);
2427 Status = STATUS_INSUFFICIENT_RESOURCES;
2428 goto end;
2429 }
2430
2431 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2432
2433 hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
2434 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2435
2436 if (!hl->utf8.Buffer) {
2437 ERR("out of memory\n");
2438 ExFreePool(hl->name.Buffer);
2439 ExFreePool(hl);
2440 Status = STATUS_INSUFFICIENT_RESOURCES;
2441 goto end;
2442 }
2443
2444 RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
2445 ExFreePool(utf8.Buffer);
2446
2447 InsertTailList(&fcb->hardlinks, &hl->list_entry);
2448
2449 mark_fileref_dirty(fr2);
2450 free_fileref(Vcb, fr2);
2451
2452 // update inode's INODE_ITEM
2453
2454 KeQuerySystemTime(&time);
2455 win_time_to_unix(time, &now);
2456
2457 fcb->inode_item.transid = Vcb->superblock.generation;
2458 fcb->inode_item.sequence++;
2459 fcb->inode_item.st_nlink++;
2460
2461 if (!ccb->user_set_change_time)
2462 fcb->inode_item.st_ctime = now;
2463
2464 fcb->inode_item_changed = TRUE;
2465 mark_fcb_dirty(fcb);
2466
2467 // update parent's INODE_ITEM
2468
2469 parfcb->inode_item.transid = Vcb->superblock.generation;
2470 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2471 parfcb->inode_item.st_size += 2 * utf8len;
2472 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2473 parfcb->inode_item.sequence++;
2474 parfcb->inode_item.st_ctime = now;
2475
2476 parfcb->inode_item_changed = TRUE;
2477 mark_fcb_dirty(parfcb);
2478
2479 send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
2480
2481 Status = STATUS_SUCCESS;
2482
2483 end:
2484 if (oldfileref)
2485 free_fileref(Vcb, oldfileref);
2486
2487 if (!NT_SUCCESS(Status) && related)
2488 free_fileref(Vcb, related);
2489
2490 if (!NT_SUCCESS(Status) && fr2)
2491 free_fileref(Vcb, fr2);
2492
2493 if (NT_SUCCESS(Status))
2494 clear_rollback(&rollback);
2495 else
2496 do_rollback(Vcb, &rollback);
2497
2498 ExReleaseResourceLite(fcb->Header.Resource);
2499 release_fcb_lock(Vcb);
2500 ExReleaseResourceLite(&Vcb->tree_lock);
2501
2502 return Status;
2503 }
2504
2505 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2506 FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
2507 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2508 fcb* fcb = FileObject->FsContext;
2509 ccb* ccb = FileObject->FsContext2;
2510 file_ref* fileref = ccb ? ccb->fileref : NULL;
2511 NTSTATUS Status;
2512 LARGE_INTEGER time;
2513 CC_FILE_SIZES ccfs;
2514 LIST_ENTRY rollback;
2515 BOOL set_size = FALSE;
2516 ULONG filter;
2517
2518 if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
2519 ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
2520 return STATUS_INVALID_PARAMETER;
2521 }
2522
2523 if (!fileref) {
2524 ERR("fileref is NULL\n");
2525 return STATUS_INVALID_PARAMETER;
2526 }
2527
2528 InitializeListHead(&rollback);
2529
2530 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2531
2532 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2533
2534 if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
2535 Status = STATUS_INVALID_PARAMETER;
2536 goto end;
2537 }
2538
2539 if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
2540 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli->ValidDataLength.QuadPart,
2541 fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
2542 Status = STATUS_INVALID_PARAMETER;
2543 goto end;
2544 }
2545
2546 if (fileref ? fileref->deleted : fcb->deleted) {
2547 Status = STATUS_FILE_CLOSED;
2548 goto end;
2549 }
2550
2551 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2552 // and we set it to the max anyway.
2553
2554 ccfs.AllocationSize = fcb->Header.AllocationSize;
2555 ccfs.FileSize = fcb->Header.FileSize;
2556 ccfs.ValidDataLength = fvdli->ValidDataLength;
2557 set_size = TRUE;
2558
2559 filter = FILE_NOTIFY_CHANGE_SIZE;
2560
2561 if (!ccb->user_set_write_time) {
2562 KeQuerySystemTime(&time);
2563 win_time_to_unix(time, &fcb->inode_item.st_mtime);
2564 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2565 }
2566
2567 fcb->inode_item_changed = TRUE;
2568 mark_fcb_dirty(fcb);
2569
2570 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2571
2572 Status = STATUS_SUCCESS;
2573
2574 end:
2575 if (NT_SUCCESS(Status))
2576 clear_rollback(&rollback);
2577 else
2578 do_rollback(Vcb, &rollback);
2579
2580 ExReleaseResourceLite(fcb->Header.Resource);
2581
2582 if (set_size) {
2583 _SEH2_TRY {
2584 CcSetFileSizes(FileObject, &ccfs);
2585 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2586 Status = _SEH2_GetExceptionCode();
2587 } _SEH2_END;
2588
2589 if (!NT_SUCCESS(Status))
2590 ERR("CcSetFileSizes threw exception %08x\n", Status);
2591 else
2592 fcb->Header.AllocationSize = ccfs.AllocationSize;
2593 }
2594
2595 ExReleaseResourceLite(&Vcb->tree_lock);
2596
2597 return Status;
2598 }
2599
2600 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
2601 _Function_class_(DRIVER_DISPATCH)
2602 NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2603 NTSTATUS Status;
2604 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2605 device_extension* Vcb = DeviceObject->DeviceExtension;
2606 fcb* fcb = IrpSp->FileObject->FsContext;
2607 ccb* ccb = IrpSp->FileObject->FsContext2;
2608 BOOL top_level;
2609
2610 FsRtlEnterFileSystem();
2611
2612 top_level = is_top_level(Irp);
2613
2614 Irp->IoStatus.Information = 0;
2615
2616 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2617 Status = vol_set_information(DeviceObject, Irp);
2618 goto end;
2619 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2620 Status = STATUS_INVALID_PARAMETER;
2621 goto end;
2622 }
2623
2624 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
2625 Status = STATUS_ACCESS_DENIED;
2626 goto end;
2627 }
2628
2629 if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
2630 Status = STATUS_MEDIA_WRITE_PROTECTED;
2631 goto end;
2632 }
2633
2634 if (!fcb) {
2635 ERR("no fcb\n");
2636 Status = STATUS_INVALID_PARAMETER;
2637 goto end;
2638 }
2639
2640 if (!ccb) {
2641 ERR("no ccb\n");
2642 Status = STATUS_INVALID_PARAMETER;
2643 goto end;
2644 }
2645
2646 if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
2647 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
2648 Status = STATUS_ACCESS_DENIED;
2649 goto end;
2650 }
2651
2652 Status = STATUS_NOT_IMPLEMENTED;
2653
2654 TRACE("set information\n");
2655
2656 switch (IrpSp->Parameters.SetFile.FileInformationClass) {
2657 case FileAllocationInformation:
2658 {
2659 TRACE("FileAllocationInformation\n");
2660
2661 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
2662 WARN("insufficient privileges\n");
2663 Status = STATUS_ACCESS_DENIED;
2664 break;
2665 }
2666
2667 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, TRUE);
2668 break;
2669 }
2670
2671 case FileBasicInformation:
2672 {
2673 TRACE("FileBasicInformation\n");
2674
2675 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
2676 WARN("insufficient privileges\n");
2677 Status = STATUS_ACCESS_DENIED;
2678 break;
2679 }
2680
2681 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
2682
2683 break;
2684 }
2685
2686 case FileDispositionInformation:
2687 {
2688 TRACE("FileDispositionInformation\n");
2689
2690 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
2691 WARN("insufficient privileges\n");
2692 Status = STATUS_ACCESS_DENIED;
2693 break;
2694 }
2695
2696 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
2697
2698 break;
2699 }
2700
2701 case FileEndOfFileInformation:
2702 {
2703 TRACE("FileEndOfFileInformation\n");
2704
2705 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2706 WARN("insufficient privileges\n");
2707 Status = STATUS_ACCESS_DENIED;
2708 break;
2709 }
2710
2711 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, FALSE);
2712
2713 break;
2714 }
2715
2716 case FileLinkInformation:
2717 TRACE("FileLinkInformation\n");
2718 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2719 break;
2720
2721 case FilePositionInformation:
2722 TRACE("FilePositionInformation\n");
2723 Status = set_position_information(IrpSp->FileObject, Irp);
2724 break;
2725
2726 case FileRenameInformation:
2727 TRACE("FileRenameInformation\n");
2728 // FIXME - make this work with streams
2729 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2730 break;
2731
2732 case FileValidDataLengthInformation:
2733 {
2734 TRACE("FileValidDataLengthInformation\n");
2735
2736 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2737 WARN("insufficient privileges\n");
2738 Status = STATUS_ACCESS_DENIED;
2739 break;
2740 }
2741
2742 Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
2743
2744 break;
2745 }
2746
2747 default:
2748 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
2749 }
2750
2751 end:
2752 Irp->IoStatus.Status = Status;
2753
2754 TRACE("returning %08x\n", Status);
2755
2756 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2757
2758 if (top_level)
2759 IoSetTopLevelIrp(NULL);
2760
2761 FsRtlExitFileSystem();
2762
2763 return Status;
2764 }
2765
2766 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
2767 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
2768
2769 *length -= sizeof(FILE_BASIC_INFORMATION);
2770
2771 if (fcb == fcb->Vcb->dummy_fcb) {
2772 LARGE_INTEGER time;
2773
2774 KeQuerySystemTime(&time);
2775 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
2776 } else {
2777 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2778 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2779 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2780 fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2781 }
2782
2783 if (fcb->ads) {
2784 if (!fileref || !fileref->parent) {
2785 ERR("no fileref for stream\n");
2786 return STATUS_INTERNAL_ERROR;
2787 } else
2788 fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2789 } else
2790 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2791
2792 return STATUS_SUCCESS;
2793 }
2794
2795 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
2796 INODE_ITEM* ii;
2797
2798 if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
2799 WARN("overflow\n");
2800 return STATUS_BUFFER_OVERFLOW;
2801 }
2802
2803 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
2804
2805 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
2806
2807 if (fcb->ads) {
2808 if (!fileref || !fileref->parent) {
2809 ERR("no fileref for stream\n");
2810 return STATUS_INTERNAL_ERROR;
2811 }
2812
2813 ii = &fileref->parent->fcb->inode_item;
2814 } else
2815 ii = &fcb->inode_item;
2816
2817 if (fcb == fcb->Vcb->dummy_fcb) {
2818 LARGE_INTEGER time;
2819
2820 KeQuerySystemTime(&time);
2821 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
2822 } else {
2823 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2824 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2825 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2826 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2827 }
2828
2829 if (fcb->ads) {
2830 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
2831 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2832 } else {
2833 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2834 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2835 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2836 }
2837
2838 return STATUS_SUCCESS;
2839 }
2840
2841 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
2842 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
2843
2844 *length -= sizeof(FILE_STANDARD_INFORMATION);
2845
2846 if (fcb->ads) {
2847 if (!fileref || !fileref->parent) {
2848 ERR("no fileref for stream\n");
2849 return STATUS_INTERNAL_ERROR;
2850 }
2851
2852 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
2853 fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
2854 fsi->Directory = FALSE;
2855 } else {
2856 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2857 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2858 fsi->NumberOfLinks = fcb->inode_item.st_nlink;
2859 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
2860 }
2861
2862 TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
2863
2864 fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE;
2865
2866 return STATUS_SUCCESS;
2867 }
2868
2869 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
2870 *length -= sizeof(FILE_INTERNAL_INFORMATION);
2871
2872 fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
2873
2874 return STATUS_SUCCESS;
2875 }
2876
2877 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
2878 *length -= sizeof(FILE_EA_INFORMATION);
2879
2880 /* This value appears to be the size of the structure NTFS stores on disk, and not,
2881 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2882 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2883 * item. */
2884
2885 eai->EaSize = fcb->ealen;
2886
2887 return STATUS_SUCCESS;
2888 }
2889
2890 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
2891 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
2892
2893 *length -= sizeof(FILE_POSITION_INFORMATION);
2894
2895 fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
2896
2897 return STATUS_SUCCESS;
2898 }
2899
2900 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
2901 file_ref* fr;
2902 NTSTATUS Status;
2903 ULONG reqlen = 0;
2904 USHORT offset;
2905 BOOL overflow = FALSE;
2906
2907 // FIXME - we need a lock on filerefs' filepart
2908
2909 if (fileref == fileref->fcb->Vcb->root_fileref) {
2910 if (fn->MaximumLength >= sizeof(WCHAR)) {
2911 fn->Buffer[0] = '\\';
2912 fn->Length = sizeof(WCHAR);
2913
2914 if (name_offset)
2915 *name_offset = 0;
2916
2917 return STATUS_SUCCESS;
2918 } else {
2919 if (preqlen)
2920 *preqlen = sizeof(WCHAR);
2921 fn->Length = 0;
2922
2923 return STATUS_BUFFER_OVERFLOW;
2924 }
2925 }
2926
2927 fr = fileref;
2928 offset = 0;
2929
2930 while (fr->parent) {
2931 USHORT movelen;
2932
2933 if (!fr->dc)
2934 return STATUS_INTERNAL_ERROR;
2935
2936 if (!overflow) {
2937 if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
2938 overflow = TRUE;
2939 }
2940
2941 if (overflow)
2942 movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
2943 else
2944 movelen = fn->Length;
2945
2946 if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
2947 RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
2948 offset += fr->dc->name.Length + sizeof(WCHAR);
2949 }
2950
2951 if (fn->MaximumLength >= sizeof(WCHAR)) {
2952 fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
2953 fn->Length += sizeof(WCHAR);
2954
2955 if (fn->MaximumLength > sizeof(WCHAR)) {
2956 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
2957 fn->Length += fr->dc->name.Length;
2958 }
2959
2960 if (fn->Length > fn->MaximumLength) {
2961 fn->Length = fn->MaximumLength;
2962 overflow = TRUE;
2963 }
2964 }
2965
2966 reqlen += sizeof(WCHAR) + fr->dc->name.Length;
2967
2968 fr = fr->parent;
2969 }
2970
2971 offset += sizeof(WCHAR);
2972
2973 if (overflow) {
2974 if (preqlen)
2975 *preqlen = reqlen;
2976 Status = STATUS_BUFFER_OVERFLOW;
2977 } else {
2978 if (name_offset)
2979 *name_offset = offset;
2980
2981 Status = STATUS_SUCCESS;
2982 }
2983
2984 return Status;
2985 }
2986
2987 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
2988 ULONG reqlen;
2989 UNICODE_STRING fn;
2990 NTSTATUS Status;
2991 static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
2992 UINT16 datasuflen = sizeof(datasuf) - sizeof(WCHAR);
2993
2994 if (!fileref) {
2995 ERR("called without fileref\n");
2996 return STATUS_INVALID_PARAMETER;
2997 }
2998
2999 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
3000
3001 TRACE("maximum length is %u\n", *length);
3002 fni->FileNameLength = 0;
3003
3004 fni->FileName[0] = 0;
3005
3006 fn.Buffer = fni->FileName;
3007 fn.Length = 0;
3008 fn.MaximumLength = (UINT16)*length;
3009
3010 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
3011 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
3012 ERR("fileref_get_filename returned %08x\n", Status);
3013 return Status;
3014 }
3015
3016 if (fcb->ads) {
3017 if (Status == STATUS_BUFFER_OVERFLOW)
3018 reqlen += datasuflen;
3019 else {
3020 if (fn.Length + datasuflen > fn.MaximumLength) {
3021 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
3022 reqlen += datasuflen;
3023 Status = STATUS_BUFFER_OVERFLOW;
3024 } else {
3025 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
3026 fn.Length += datasuflen;
3027 }
3028 }
3029 }
3030
3031 if (Status == STATUS_BUFFER_OVERFLOW) {
3032 *length = -1;
3033 fni->FileNameLength = reqlen;
3034 TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
3035 } else {
3036 *length -= fn.Length;
3037 fni->FileNameLength = fn.Length;
3038 TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
3039 }
3040
3041 return Status;
3042 }
3043
3044 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) {
3045 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
3046
3047 if (fcb->ads) {
3048 if (!ccb->fileref || !ccb->fileref->parent) {
3049 ERR("no fileref for stream\n");
3050 return STATUS_INTERNAL_ERROR;
3051 }
3052
3053 ati->FileAttributes = ccb->fileref->parent->fcb->atts;
3054 } else
3055 ati->FileAttributes = fcb->atts;
3056
3057 if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3058 ati->ReparseTag = 0;
3059 else
3060 ati->ReparseTag = get_reparse_tag_fcb(fcb);
3061
3062 return STATUS_SUCCESS;
3063 }
3064
3065 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
3066 LONG reqsize;
3067 LIST_ENTRY* le;
3068 FILE_STREAM_INFORMATION *entry, *lastentry;
3069 NTSTATUS Status;
3070
3071 static const WCHAR datasuf[] = L":$DATA";
3072 UNICODE_STRING suf;
3073
3074 if (!fileref) {
3075 ERR("fileref was NULL\n");
3076 return STATUS_INVALID_PARAMETER;
3077 }
3078
3079 suf.Buffer = (WCHAR*)datasuf;
3080 suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
3081
3082 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
3083 reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
3084 else
3085 reqsize = 0;
3086
3087 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
3088
3089 le = fileref->fcb->dir_children_index.Flink;
3090 while (le != &fileref->fcb->dir_children_index) {
3091 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3092
3093 if (dc->index == 0) {
3094 reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
3095 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
3096 } else
3097 break;
3098
3099 le = le->Flink;
3100 }
3101
3102 TRACE("length = %i, reqsize = %u\n", *length, reqsize);
3103
3104 if (reqsize > *length) {
3105 Status = STATUS_BUFFER_OVERFLOW;
3106 goto end;
3107 }
3108
3109 entry = fsi;
3110 lastentry = NULL;
3111
3112 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
3113 ULONG off;
3114
3115 entry->NextEntryOffset = 0;
3116 entry->StreamNameLength = suf.Length + sizeof(WCHAR);
3117 entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
3118 entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
3119
3120 entry->StreamName[0] = ':';
3121 RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
3122
3123 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
3124
3125 lastentry = entry;
3126 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3127 }
3128
3129 le = fileref->fcb->dir_children_index.Flink;
3130 while (le != &fileref->fcb->dir_children_index) {
3131 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3132
3133 if (dc->index == 0) {
3134 ULONG off;
3135
3136 entry->NextEntryOffset = 0;
3137 entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
3138
3139 if (dc->fileref)
3140 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
3141 else
3142 entry->StreamSize.QuadPart = dc->size;
3143
3144 entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
3145
3146 entry->StreamName[0] = ':';
3147
3148 RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
3149 RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
3150
3151 if (lastentry)
3152 lastentry->NextEntryOffset = (UINT32)((UINT8*)entry - (UINT8*)lastentry);
3153
3154 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
3155
3156 lastentry = entry;
3157 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3158 } else
3159 break;
3160
3161 le = le->Flink;
3162 }
3163
3164 *length -= reqsize;
3165
3166 Status = STATUS_SUCCESS;
3167
3168 end:
3169 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
3170
3171 return Status;
3172 }
3173
3174 #ifndef __REACTOS__
3175 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
3176 TRACE("FileStandardLinkInformation\n");
3177
3178 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3179
3180 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
3181 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
3182 fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE;
3183 fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE;
3184
3185 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
3186
3187 return STATUS_SUCCESS;
3188 }
3189 #endif /* __REACTOS__ */
3190
3191 NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
3192 root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
3193 NTSTATUS Status;
3194 fcb* fcb;
3195 UINT64 parent = 0;
3196 UNICODE_STRING name;
3197 BOOL hl_alloc = FALSE;
3198 file_ref *parfr, *fr;
3199
3200 Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
3201 if (!NT_SUCCESS(Status)) {
3202 ERR("open_fcb returned %08x\n", Status);
3203 return Status;
3204 }
3205
3206 if (fcb->fileref) {
3207 *pfr = fcb->fileref;
3208 increase_fileref_refcount(fcb->fileref);
3209 return STATUS_SUCCESS;
3210 }
3211
3212 // find hardlink if fcb doesn't have any loaded
3213 if (IsListEmpty(&fcb->hardlinks)) {
3214 KEY searchkey;
3215 traverse_ptr tp;
3216
3217 searchkey.obj_id = fcb->inode;
3218 searchkey.obj_type = TYPE_INODE_EXTREF;
3219 searchkey.offset = 0xffffffffffffffff;
3220
3221 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
3222 if (!NT_SUCCESS(Status)) {
3223 ERR("find_item returned %08x\n", Status);
3224 free_fcb(Vcb, fcb);
3225 return Status;
3226 }
3227
3228 if (tp.item->key.obj_id == fcb->inode) {
3229 if (tp.item->key.obj_type == TYPE_INODE_REF) {
3230 INODE_REF* ir;
3231 ULONG stringlen;
3232
3233 ir = (INODE_REF*)tp.item->data;
3234
3235 parent = tp.item->key.offset;
3236
3237 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
3238 if (!NT_SUCCESS(Status)) {
3239 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3240 free_fcb(Vcb, fcb);
3241 return Status;
3242 }
3243
3244 name.Length = name.MaximumLength = (UINT16)stringlen;
3245
3246 if (stringlen == 0)
3247 name.Buffer = NULL;
3248 else {
3249 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3250
3251 if (!name.Buffer) {
3252 ERR("out of memory\n");
3253 free_fcb(Vcb, fcb);
3254 return STATUS_INSUFFICIENT_RESOURCES;
3255 }
3256
3257 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
3258 if (!NT_SUCCESS(Status)) {
3259 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3260 ExFreePool(name.Buffer);
3261 free_fcb(Vcb, fcb);
3262 return Status;
3263 }
3264
3265 hl_alloc = TRUE;
3266 }
3267 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3268 INODE_EXTREF* ier;
3269 ULONG stringlen;
3270
3271 ier = (INODE_EXTREF*)tp.item->data;
3272
3273 parent = ier->dir;
3274
3275 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
3276 if (!NT_SUCCESS(Status)) {
3277 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3278 free_fcb(Vcb, fcb);
3279 return Status;
3280 }
3281
3282 name.Length = name.MaximumLength = (UINT16)stringlen;
3283
3284 if (stringlen == 0)
3285 name.Buffer = NULL;
3286 else {
3287 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3288
3289 if (!name.Buffer) {
3290 ERR("out of memory\n");
3291 free_fcb(Vcb, fcb);
3292 return STATUS_INSUFFICIENT_RESOURCES;
3293 }
3294
3295 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
3296 if (!NT_SUCCESS(Status)) {
3297 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3298 ExFreePool(name.Buffer);
3299 free_fcb(Vcb, fcb);
3300 return Status;
3301 }
3302
3303 hl_alloc = TRUE;
3304 }
3305
3306 }
3307 }
3308 } else {
3309 hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
3310
3311 name = hl->name;
3312 parent = hl->parent;
3313 }
3314
3315 if (parent == 0) {
3316 ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode);
3317 free_fcb(Vcb, fcb);
3318 if (hl_alloc) ExFreePool(name.Buffer);
3319 return STATUS_INVALID_PARAMETER;
3320 }
3321
3322 if (parent == inode) { // subvolume root
3323 KEY searchkey;
3324 traverse_ptr tp;
3325
3326 searchkey.obj_id = subvol->id;
3327 searchkey.obj_type = TYPE_ROOT_BACKREF;
3328 searchkey.offset = 0xffffffffffffffff;
3329
3330 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3331 if (!NT_SUCCESS(Status)) {
3332 ERR("find_item returned %08x\n", Status);
3333 free_fcb(Vcb, fcb);
3334 if (hl_alloc) ExFreePool(name.Buffer);
3335 return Status;
3336 }
3337
3338 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
3339 ROOT_REF* rr = (ROOT_REF*)tp.item->data;
3340 LIST_ENTRY* le;
3341 root* r = NULL;
3342 ULONG stringlen;
3343
3344 if (tp.item->size < sizeof(ROOT_REF)) {
3345 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
3346 free_fcb(Vcb, fcb);
3347 if (hl_alloc) ExFreePool(name.Buffer);
3348 return STATUS_INTERNAL_ERROR;
3349 }
3350
3351 if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) {
3352 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_REF, name[0]) + rr->n);
3353 free_fcb(Vcb, fcb);
3354 if (hl_alloc) ExFreePool(name.Buffer);
3355 return STATUS_INTERNAL_ERROR;
3356 }
3357
3358 le = Vcb->roots.Flink;
3359 while (le != &Vcb->roots) {
3360 root* r2 = CONTAINING_RECORD(le, root, list_entry);
3361
3362 if (r2->id == tp.item->key.offset) {
3363 r = r2;
3364 break;
3365 }
3366
3367 le = le->Flink;
3368 }
3369
3370 if (!r) {
3371 ERR("couldn't find subvol %llx\n", tp.item->key.offset);
3372 free_fcb(Vcb, fcb);
3373 if (hl_alloc) ExFreePool(name.Buffer);
3374 return STATUS_INTERNAL_ERROR;
3375 }
3376
3377 Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
3378 if (!NT_SUCCESS(Status)) {
3379 ERR("open_fileref_by_inode returned %08x\n", Status);
3380 free_fcb(Vcb, fcb);
3381 if (hl_alloc) ExFreePool(name.Buffer);
3382 return Status;
3383 }
3384
3385 if (hl_alloc) {
3386 ExFreePool(name.Buffer);
3387 hl_alloc = FALSE;
3388 }
3389
3390 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
3391 if (!NT_SUCCESS(Status)) {
3392 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3393 free_fcb(Vcb, fcb);
3394 return Status;
3395 }
3396
3397 name.Length = name.MaximumLength = (UINT16)stringlen;
3398
3399 if (stringlen == 0)
3400 name.Buffer = NULL;
3401 else {
3402 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3403
3404 if (!name.Buffer) {
3405 ERR("out of memory\n");
3406 free_fcb(Vcb, fcb);
3407 return STATUS_INSUFFICIENT_RESOURCES;
3408 }
3409
3410 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
3411 if (!NT_SUCCESS(Status)) {
3412 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3413 ExFreePool(name.Buffer);
3414 free_fcb(Vcb, fcb);
3415 return Status;
3416 }
3417
3418 hl_alloc = TRUE;
3419 }
3420 } else {
3421 ERR("couldn't find parent for subvol %llx\n", subvol->id);
3422 free_fcb(Vcb, fcb);
3423 if (hl_alloc) ExFreePool(name.Buffer);
3424 return STATUS_INTERNAL_ERROR;
3425 }
3426 } else {
3427 Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
3428 if (!NT_SUCCESS(Status)) {
3429 ERR("open_fileref_by_inode returned %08x\n", Status);
3430 free_fcb(Vcb, fcb);
3431
3432 if (hl_alloc)
3433 ExFreePool(name.Buffer);
3434
3435 return Status;
3436 }
3437 }
3438
3439 Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
3440
3441 if (!NT_SUCCESS(Status)) {
3442 ERR("open_fileref_child returned %08x\n", Status);
3443
3444 if (hl_alloc)
3445 ExFreePool(name.Buffer);
3446
3447 free_fcb(Vcb, fcb);
3448 free_fileref(Vcb, parfr);
3449
3450 return Status;
3451 }
3452
3453 *pfr = fr;
3454
3455 if (hl_alloc)
3456 ExFreePool(name.Buffer);
3457
3458 free_fcb(Vcb, fcb);
3459 free_fileref(Vcb, parfr);
3460
3461 return STATUS_SUCCESS;
3462 }
3463
3464 #ifndef __REACTOS__
3465 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
3466 NTSTATUS Status;
3467 LIST_ENTRY* le;
3468 LONG bytes_needed;
3469 FILE_LINK_ENTRY_INFORMATION* feli;
3470 BOOL overflow = FALSE;
3471 fcb* fcb = fileref->fcb;
3472 ULONG len;
3473
3474 if (fcb->ads)
3475 return STATUS_INVALID_PARAMETER;
3476
3477 if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
3478 return STATUS_INVALID_PARAMETER;
3479
3480 RtlZeroMemory(fli, *length);
3481
3482 bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
3483 len = bytes_needed;
3484 feli = NULL;
3485
3486 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
3487
3488 if (fcb->inode == SUBVOL_ROOT_INODE) {
3489 ULONG namelen;
3490
3491 if (fcb == fcb->Vcb->root_fileref->fcb)
3492 namelen = sizeof(WCHAR);
3493 else
3494 namelen = fileref->dc->name.Length;
3495
3496 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
3497
3498 if (bytes_needed > *length)
3499 overflow = TRUE;
3500
3501 if (!overflow) {
3502 feli = &fli->Entry;
3503
3504 feli->NextEntryOffset = 0;
3505 feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
3506
3507 if (fcb == fcb->Vcb->root_fileref->fcb) {
3508 feli->FileNameLength = 1;
3509 feli->FileName[0] = '.';
3510 } else {
3511 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3512 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3513 }
3514
3515 fli->EntriesReturned++;
3516
3517 len = bytes_needed;
3518 }
3519 } else {
3520 acquire_fcb_lock_exclusive(fcb->Vcb);
3521
3522 if (IsListEmpty(&fcb->hardlinks)) {
3523 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
3524
3525 if (bytes_needed > *length)
3526 overflow = TRUE;
3527
3528 if (!overflow) {
3529 feli = &fli->Entry;
3530
3531 feli->NextEntryOffset = 0;
3532 feli->ParentFileId = fileref->parent->fcb->inode;
3533 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3534 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3535
3536 fli->EntriesReturned++;
3537
3538 len = bytes_needed;
3539 }
3540 } else {
3541 le = fcb->hardlinks.Flink;
3542 while (le != &fcb->hardlinks) {
3543 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
3544 file_ref* parfr;
3545
3546 TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
3547
3548 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
3549
3550 if (!NT_SUCCESS(Status)) {
3551 ERR("open_fileref_by_inode returned %08x\n", Status);
3552 } else if (!parfr->deleted) {
3553 LIST_ENTRY* le2;
3554 BOOL found = FALSE, deleted = FALSE;
3555 UNICODE_STRING* fn = NULL;
3556
3557 le2 = parfr->children.Flink;
3558 while (le2 != &parfr->children) {
3559 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
3560
3561 if (fr2->dc->index == hl->index) {
3562 found = TRUE;
3563 deleted = fr2->deleted;
3564
3565 if (!deleted)
3566 fn = &fr2->dc->name;
3567
3568 break;
3569 }
3570
3571 le2 = le2->Flink;
3572 }
3573
3574 if (!found)
3575 fn = &hl->name;
3576
3577 if (!deleted) {
3578 TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
3579
3580 if (feli)
3581 bytes_needed = (LONG)sector_align(bytes_needed, 8);
3582
3583 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
3584
3585 if (bytes_needed > *length)
3586 overflow = TRUE;
3587
3588 if (!overflow) {
3589 if (feli) {
3590 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
3591 feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset);
3592 } else
3593 feli = &fli->Entry;
3594
3595 feli->NextEntryOffset = 0;
3596 feli->ParentFileId = parfr->fcb->inode;
3597 feli->FileNameLength = fn->Length / sizeof(WCHAR);
3598 RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
3599
3600 fli->EntriesReturned++;
3601
3602 len = bytes_needed;
3603 }
3604 }
3605
3606 free_fileref(fcb->Vcb, parfr);
3607 }
3608
3609 le = le->Flink;
3610 }
3611 }
3612
3613 release_fcb_lock(fcb->Vcb);
3614 }
3615
3616 fli->BytesNeeded = bytes_needed;
3617
3618 *length -= len;
3619
3620 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
3621
3622 ExReleaseResourceLite(fcb->Header.Resource);
3623
3624 return Status;
3625 }
3626 #endif /* __REACTOS__ */
3627
3628 #if (NTDDI_VERSION >= NTDDI_WIN10)
3629 #ifdef __MINGW32__
3630 typedef struct _FILE_ID_128 {
3631 UCHAR Identifier[16];
3632 } FILE_ID_128, *PFILE_ID_128;
3633
3634 typedef struct _FILE_ID_INFORMATION {
3635 ULONGLONG VolumeSerialNumber;
3636 FILE_ID_128 FileId;
3637 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
3638 #endif
3639
3640 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
3641 RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64));
3642 RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64));
3643 RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
3644
3645 *length -= sizeof(FILE_ID_INFORMATION);
3646
3647 return STATUS_SUCCESS;
3648 }
3649 #endif
3650
3651 #ifndef __REACTOS__
3652 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
3653 INODE_ITEM* ii;
3654
3655 fsli->FileId.LowPart = (UINT32)fcb->inode;
3656 fsli->FileId.HighPart = (UINT32)fcb->subvol->id;
3657
3658 if (fcb->ads)
3659 ii = &ccb->fileref->parent->fcb->inode_item;
3660 else
3661 ii = &fcb->inode_item;
3662
3663 if (fcb == fcb->Vcb->dummy_fcb) {
3664 LARGE_INTEGER time;
3665
3666 KeQuerySystemTime(&time);
3667 fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time;
3668 } else {
3669 fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
3670 fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
3671 fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
3672 fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
3673 }
3674
3675 if (fcb->ads) {
3676 fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length;
3677 fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
3678 } else {
3679 fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
3680 fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
3681 fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
3682 }
3683
3684 if (fcb->type == BTRFS_TYPE_SOCKET)
3685 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
3686 else if (fcb->type == BTRFS_TYPE_FIFO)
3687 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
3688 else if (fcb->type == BTRFS_TYPE_CHARDEV)
3689 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
3690 else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
3691 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
3692 else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3693 fsli->ReparseTag = 0;
3694 else
3695 fsli->ReparseTag = get_reparse_tag_fcb(fcb);
3696
3697 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
3698 fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
3699
3700 if (fcb->ads)
3701 fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
3702 else
3703 fsli->NumberOfLinks = fcb->inode_item.st_nlink;
3704
3705 fsli->EffectiveAccess = ccb->access;
3706 fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; // FIXME - LX_FILE_CASE_SENSITIVE_DIR
3707 fsli->LxUid = ii->st_uid;
3708 fsli->LxGid = ii->st_gid;
3709 fsli->LxMode = ii->st_mode;
3710
3711 if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
3712 fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
3713 fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
3714 } else {
3715 fsli->LxDeviceIdMajor = 0;
3716 fsli->LxDeviceIdMinor = 0;
3717 }
3718
3719 *length -= sizeof(FILE_STAT_LX_INFORMATION);
3720
3721 return STATUS_SUCCESS;
3722 }
3723 #endif
3724
3725 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
3726 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3727 LONG length = IrpSp->Parameters.QueryFile.Length;
3728 fcb* fcb = FileObject->FsContext;
3729 ccb* ccb = FileObject->FsContext2;
3730 file_ref* fileref = ccb ? ccb->fileref : NULL;
3731 NTSTATUS Status;
3732
3733 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
3734 TRACE("fcb = %p\n", fcb);
3735
3736 if (fcb == Vcb->volume_fcb)
3737 return STATUS_INVALID_PARAMETER;
3738
3739 if (!ccb) {
3740 ERR("ccb is NULL\n");
3741 return STATUS_INVALID_PARAMETER;
3742 }
3743
3744 switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
3745 case FileAllInformation:
3746 {
3747 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
3748 INODE_ITEM* ii;
3749
3750 TRACE("FileAllInformation\n");
3751
3752 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3753 WARN("insufficient privileges\n");
3754 Status = STATUS_ACCESS_DENIED;
3755 goto exit;
3756 }
3757
3758 if (fcb->ads) {
3759 if (!fileref || !fileref->parent) {
3760 ERR("no fileref for stream\n");
3761 Status = STATUS_INTERNAL_ERROR;
3762 goto exit;
3763 }
3764
3765 ii = &fileref->parent->fcb->inode_item;
3766 } else
3767 ii = &fcb->inode_item;
3768
3769 // Access, mode, and alignment are all filled in by the kernel
3770
3771 if (length > 0)
3772 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
3773
3774 if (length > 0)
3775 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
3776
3777 if (length > 0)
3778 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
3779
3780 if (length > 0)
3781 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
3782
3783 length -= sizeof(FILE_ACCESS_INFORMATION);
3784
3785 if (length > 0)
3786 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
3787
3788 length -= sizeof(FILE_MODE_INFORMATION);
3789
3790 length -= sizeof(FILE_ALIGNMENT_INFORMATION);
3791
3792 if (length > 0)
3793 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
3794
3795 Status = STATUS_SUCCESS;
3796
3797 break;
3798 }
3799
3800 case FileAttributeTagInformation:
3801 {
3802 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
3803
3804 TRACE("FileAttributeTagInformation\n");
3805
3806 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3807 WARN("insufficient privileges\n");
3808 Status = STATUS_ACCESS_DENIED;
3809 goto exit;
3810 }
3811
3812 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3813 Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
3814 ExReleaseResourceLite(&Vcb->tree_lock);
3815
3816 break;
3817 }
3818
3819 case FileBasicInformation:
3820 {
3821 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
3822 INODE_ITEM* ii;
3823
3824 TRACE("FileBasicInformation\n");
3825
3826 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3827 WARN("insufficient privileges\n");
3828 Status = STATUS_ACCESS_DENIED;
3829 goto exit;
3830 }
3831
3832 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
3833 WARN("overflow\n");
3834 Status = STATUS_BUFFER_OVERFLOW;
3835 goto exit;
3836 }
3837
3838 if (fcb->ads) {
3839 if (!fileref || !fileref->parent) {
3840 ERR("no fileref for stream\n");
3841 Status = STATUS_INTERNAL_ERROR;
3842 goto exit;
3843 }
3844
3845 ii = &fileref->parent->fcb->inode_item;
3846 } else
3847 ii = &fcb->inode_item;
3848
3849 Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
3850 break;
3851 }
3852
3853 case FileCompressionInformation:
3854 FIXME("STUB: FileCompressionInformation\n");
3855 Status = STATUS_INVALID_PARAMETER;
3856 goto exit;
3857
3858 case FileEaInformation:
3859 {
3860 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
3861
3862 TRACE("FileEaInformation\n");
3863
3864 Status = fill_in_file_ea_information(eai, fcb, &length);
3865
3866 break;
3867 }
3868
3869 case FileInternalInformation:
3870 {
3871 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3872
3873 TRACE("FileInternalInformation\n");
3874
3875 Status = fill_in_file_internal_information(fii, fcb, &length);
3876
3877 break;
3878 }
3879
3880 case FileNameInformation:
3881 {
3882 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3883
3884 TRACE("FileNameInformation\n");
3885
3886 Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3887
3888 break;
3889 }
3890
3891 case FileNetworkOpenInformation:
3892 {
3893 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
3894
3895 TRACE("FileNetworkOpenInformation\n");
3896
3897 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3898 WARN("insufficient privileges\n");
3899 Status = STATUS_ACCESS_DENIED;
3900 goto exit;
3901 }
3902
3903 Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
3904
3905 break;
3906 }
3907
3908 case FilePositionInformation:
3909 {
3910 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
3911
3912 TRACE("FilePositionInformation\n");
3913
3914 Status = fill_in_file_position_information(fpi, FileObject, &length);
3915
3916 break;
3917 }
3918
3919 case FileStandardInformation:
3920 {
3921 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3922
3923 TRACE("FileStandardInformation\n");
3924
3925 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
3926 WARN("overflow\n");
3927 Status = STATUS_BUFFER_OVERFLOW;
3928 goto exit;
3929 }
3930
3931 Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
3932
3933 break;
3934 }
3935
3936 case FileStreamInformation:
3937 {
3938 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3939
3940 TRACE("FileStreamInformation\n");
3941
3942 Status = fill_in_file_stream_information(fsi, fileref, &length);
3943
3944 break;
3945 }
3946
3947 #if (NTDDI_VERSION >= NTDDI_VISTA)
3948 case FileHardLinkInformation:
3949 {
3950 FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
3951
3952 TRACE("FileHardLinkInformation\n");
3953
3954 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3955 Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
3956 ExReleaseResourceLite(&Vcb->tree_lock);
3957
3958 break;
3959 }
3960
3961 case FileNormalizedNameInformation:
3962 {
3963 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3964
3965 TRACE("FileNormalizedNameInformation\n");
3966
3967 Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3968
3969 break;
3970 }
3971 #endif
3972
3973 #if (NTDDI_VERSION >= NTDDI_WIN7)
3974 case FileStandardLinkInformation:
3975 {
3976 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
3977
3978 TRACE("FileStandardLinkInformation\n");
3979
3980 Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
3981
3982 break;
3983 }
3984
3985 case FileRemoteProtocolInformation:
3986 TRACE("FileRemoteProtocolInformation\n");
3987 Status = STATUS_INVALID_PARAMETER;
3988 goto exit;
3989 #endif
3990
3991 #if (NTDDI_VERSION >= NTDDI_WIN10)
3992 #ifndef _MSC_VER
3993 #pragma GCC diagnostic push
3994 #pragma GCC diagnostic ignored "-Wswitch"
3995 #endif
3996 case FileIdInformation:
3997 {
3998 FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3999
4000 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
4001 WARN("overflow\n");
4002 Status = STATUS_BUFFER_OVERFLOW;
4003 goto exit;
4004 }
4005
4006 TRACE("FileIdInformation\n");
4007
4008 Status = fill_in_file_id_information(fii, fcb, &length);
4009
4010 break;
4011 }
4012
4013 case FileStatLxInformation:
4014 {
4015 FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
4016
4017 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
4018 WARN("overflow\n");
4019 Status = STATUS_BUFFER_OVERFLOW;
4020 goto exit;
4021 }
4022
4023 TRACE("FileStatLxInformation\n");
4024
4025 Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
4026
4027 break;
4028 }
4029 #ifndef _MSC_VER
4030 #pragma GCC diagnostic pop
4031 #endif
4032 #endif
4033
4034 default:
4035 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
4036 Status = STATUS_INVALID_PARAMETER;
4037 goto exit;
4038 }
4039
4040 if (length < 0) {
4041 length = 0;
4042 Status = STATUS_BUFFER_OVERFLOW;
4043 }
4044
4045 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
4046
4047 exit:
4048 TRACE("query_info returning %08x\n", Status);
4049
4050 return Status;
4051 }
4052
4053 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
4054 _Function_class_(DRIVER_DISPATCH)
4055 NTSTATUS NTAPI drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4056 PIO_STACK_LOCATION IrpSp;
4057 NTSTATUS Status;
4058 fcb* fcb;
4059 device_extension* Vcb = DeviceObject->DeviceExtension;
4060 BOOL top_level;
4061
4062 FsRtlEnterFileSystem();
4063
4064 top_level = is_top_level(Irp);
4065
4066 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4067 Status = vol_query_information(DeviceObject, Irp);
4068 goto end;
4069 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4070 Status = STATUS_INVALID_PARAMETER;
4071 goto end;
4072 }
4073
4074 Irp->IoStatus.Information = 0;
4075
4076 TRACE("query information\n");
4077
4078 IrpSp = IoGetCurrentIrpStackLocation(Irp);
4079
4080 fcb = IrpSp->FileObject->FsContext;
4081 TRACE("fcb = %p\n", fcb);
4082 TRACE("fcb->subvol = %p\n", fcb->subvol);
4083
4084 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
4085
4086 end:
4087 TRACE("returning %08x\n", Status);
4088
4089 Irp->IoStatus.Status = Status;
4090
4091 IoCompleteRequest( Irp, IO_NO_INCREMENT );
4092
4093 if (top_level)
4094 IoSetTopLevelIrp(NULL);
4095
4096 FsRtlExitFileSystem();
4097
4098 return Status;
4099 }
4100
4101 _Dispatch_type_(IRP_MJ_QUERY_EA)
4102 _Function_class_(DRIVER_DISPATCH)
4103 NTSTATUS NTAPI drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4104 NTSTATUS Status;
4105 BOOL top_level;
4106 device_extension* Vcb = DeviceObject->DeviceExtension;
4107 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4108 PFILE_OBJECT FileObject = IrpSp->FileObject;
4109 fcb* fcb;
4110 ccb* ccb;
4111 FILE_FULL_EA_INFORMATION* ffei;
4112 ULONG retlen = 0;
4113
4114 FsRtlEnterFileSystem();
4115
4116 TRACE("(%p, %p)\n", DeviceObject, Irp);
4117
4118 top_level = is_top_level(Irp);
4119
4120 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4121 Status = vol_query_ea(DeviceObject, Irp);
4122 goto end;
4123 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4124 Status = STATUS_INVALID_PARAMETER;
4125 goto end;
4126 }
4127
4128 ffei = map_user_buffer(Irp, NormalPagePriority);
4129 if (!ffei) {
4130 ERR("could not get output buffer\n");
4131 Status = STATUS_INVALID_PARAMETER;
4132 goto end;
4133 }
4134
4135 if (!FileObject) {
4136 ERR("no file object\n");
4137 Status = STATUS_INVALID_PARAMETER;
4138 goto end;
4139 }
4140
4141 fcb = FileObject->FsContext;
4142
4143 if (!fcb) {
4144 ERR("no fcb\n");
4145 Status = STATUS_INVALID_PARAMETER;
4146 goto end;
4147 }
4148
4149 ccb = FileObject->FsContext2;
4150
4151 if (!ccb) {
4152 ERR("no ccb\n");
4153 Status = STATUS_INVALID_PARAMETER;
4154 goto end;
4155 }
4156
4157 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
4158 WARN("insufficient privileges\n");
4159 Status = STATUS_ACCESS_DENIED;
4160 goto end;
4161 }
4162
4163 if (fcb->ads)
4164 fcb = ccb->fileref->parent->fcb;
4165
4166 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
4167
4168 Status = STATUS_SUCCESS;
4169
4170 if (fcb->ea_xattr.Length == 0)
4171 goto end2;
4172
4173 if (IrpSp->Parameters.QueryEa.EaList) {
4174 FILE_FULL_EA_INFORMATION *ea, *out;
4175 FILE_GET_EA_INFORMATION* in;
4176
4177 in = IrpSp->Parameters.QueryEa.EaList;
4178 do {
4179 STRING s;
4180
4181 s.Length = s.MaximumLength = in->EaNameLength;
4182 s.Buffer = in->EaName;
4183
4184 RtlUpperString(&s, &s);
4185
4186 if (in->NextEntryOffset == 0)
4187 break;
4188
4189 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
4190 } while (TRUE);
4191
4192 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4193 out = NULL;
4194
4195 do {
4196 BOOL found = FALSE;
4197
4198 in = IrpSp->Parameters.QueryEa.EaList;
4199 do {
4200 if (in->EaNameLength == ea->EaNameLength &&
4201 RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
4202 found = TRUE;
4203 break;
4204 }
4205
4206 if (in->NextEntryOffset == 0)
4207 break;
4208
4209 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
4210 } while (TRUE);
4211
4212 if (found) {
4213 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4214
4215 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4216 Status = STATUS_BUFFER_OVERFLOW;
4217 retlen = 0;
4218 goto end2;
4219 }
4220
4221 retlen += padding;
4222
4223 if (out) {
4224 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4225 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4226 } else
4227 out = ffei;
4228
4229 out->NextEntryOffset = 0;
4230 out->Flags = ea->Flags;
4231 out->EaNameLength = ea->EaNameLength;
4232 out->EaValueLength = ea->EaValueLength;
4233 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4234
4235 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4236
4237 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4238 break;
4239 }
4240
4241 if (ea->NextEntryOffset == 0)
4242 break;
4243
4244 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4245 } while (TRUE);
4246 } else {
4247 FILE_FULL_EA_INFORMATION *ea, *out;
4248 ULONG index;
4249
4250 if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
4251 // The index is 1-based
4252 if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
4253 Status = STATUS_NONEXISTENT_EA_ENTRY;
4254 goto end2;
4255 } else
4256 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
4257 } else if (IrpSp->Flags & SL_RESTART_SCAN)
4258 index = ccb->ea_index = 0;
4259 else
4260 index = ccb->ea_index;
4261
4262 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4263
4264 if (index > 0) {
4265 ULONG i;
4266
4267 for (i = 0; i < index; i++) {
4268 if (ea->NextEntryOffset == 0) // last item
4269 goto end2;
4270
4271 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4272 }
4273 }
4274
4275 out = NULL;
4276
4277 do {
4278 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4279
4280 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4281 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
4282 goto end2;
4283 }
4284
4285 retlen += padding;
4286
4287 if (out) {
4288 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4289 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4290 } else
4291 out = ffei;
4292
4293 out->NextEntryOffset = 0;
4294 out->Flags = ea->Flags;
4295 out->EaNameLength = ea->EaNameLength;
4296 out->EaValueLength = ea->EaValueLength;
4297 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4298
4299 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4300
4301 if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
4302 ccb->ea_index++;
4303
4304 if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4305 break;
4306
4307 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4308 } while (TRUE);
4309 }
4310
4311 end2:
4312 ExReleaseResourceLite(fcb->Header.Resource);
4313
4314 end:
4315 TRACE("returning %08x\n", Status);
4316
4317 Irp->IoStatus.Status = Status;
4318 Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
4319
4320 IoCompleteRequest( Irp, IO_NO_INCREMENT );
4321
4322 if (top_level)
4323 IoSetTopLevelIrp(NULL);
4324
4325 FsRtlExitFileSystem();
4326
4327 return Status;
4328 }
4329
4330 _Dispatch_type_(IRP_MJ_SET_EA)
4331 _Function_class_(DRIVER_DISPATCH)
4332 NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4333 device_extension* Vcb = DeviceObject->DeviceExtension;
4334 NTSTATUS Status;
4335 BOOL top_level;
4336 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4337 PFILE_OBJECT FileObject = IrpSp->FileObject;
4338 fcb* fcb;
4339 ccb* ccb;
4340 file_ref* fileref;
4341 FILE_FULL_EA_INFORMATION* ffei;
4342 ULONG offset;
4343 LIST_ENTRY ealist;
4344 ea_item* item;
4345 FILE_FULL_EA_INFORMATION* ea;
4346 LIST_ENTRY* le;
4347 LARGE_INTEGER time;
4348 BTRFS_TIME now;
4349
4350 FsRtlEnterFileSystem();
4351
4352 TRACE("(%p, %p)\n", DeviceObject, Irp);
4353
4354 top_level = is_top_level(Irp);
4355
4356 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4357 Status = vol_set_ea(DeviceObject, Irp);
4358 goto end;
4359 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4360 Status = STATUS_INVALID_PARAMETER;
4361 goto end;
4362 }
4363
4364 if (Vcb->readonly) {
4365 Status = STATUS_MEDIA_WRITE_PROTECTED;
4366 goto end;
4367 }
4368
4369 ffei = map_user_buffer(Irp, NormalPagePriority);
4370 if (!ffei) {
4371 ERR("could not get output buffer\n");
4372 Status = STATUS_INVALID_PARAMETER;
4373 goto end;
4374 }
4375
4376 Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
4377 if (!NT_SUCCESS(Status)) {
4378 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
4379 goto end;
4380 }
4381
4382 if (!FileObject) {
4383 ERR("no file object\n");
4384 Status = STATUS_INVALID_PARAMETER;
4385 goto end;
4386 }
4387
4388 fcb = FileObject->FsContext;
4389
4390 if (!fcb) {
4391 ERR("no fcb\n");
4392 Status = STATUS_INVALID_PARAMETER;
4393 goto end;
4394 }
4395
4396 ccb = FileObject->FsContext2;
4397
4398 if (!ccb) {
4399 ERR("no ccb\n");
4400 Status = STATUS_INVALID_PARAMETER;
4401 goto end;
4402 }
4403
4404 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
4405 WARN("insufficient privileges\n");
4406 Status = STATUS_ACCESS_DENIED;
4407 goto end;
4408 }
4409
4410 if (fcb->ads) {
4411 fileref = ccb->fileref->parent;
4412 fcb = fileref->fcb;
4413 } else
4414 fileref = ccb->fileref;
4415
4416 InitializeListHead(&ealist);
4417
4418 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4419
4420 if (fcb->ea_xattr.Length > 0) {
4421 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4422
4423 do {
4424 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4425 if (!item) {
4426 ERR("out of memory\n");
4427 Status = STATUS_INSUFFICIENT_RESOURCES;
4428 goto end2;
4429 }
4430
4431 item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4432 item->name.Buffer = ea->EaName;
4433
4434 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4435 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4436
4437 item->flags = ea->Flags;
4438
4439 InsertTailList(&ealist, &item->list_entry);
4440
4441 if (ea->NextEntryOffset == 0)
4442 break;
4443
4444 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4445 } while (TRUE);
4446 }
4447
4448 ea = ffei;
4449
4450 do {
4451 STRING s;
4452 BOOL found = FALSE;
4453
4454 s.Length = s.MaximumLength = ea->EaNameLength;
4455 s.Buffer = ea->EaName;
4456
4457 RtlUpperString(&s, &s);
4458
4459 le = ealist.Flink;
4460 while (le != &ealist) {
4461 item = CONTAINING_RECORD(le, ea_item, list_entry);
4462
4463 if (item->name.Length == s.Length &&
4464 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
4465 item->flags = ea->Flags;
4466 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4467 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4468 found = TRUE;
4469 break;
4470 }
4471
4472 le = le->Flink;
4473 }
4474
4475 if (!found) {
4476 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4477 if (!item) {
4478 ERR("out of memory\n");
4479 Status = STATUS_INSUFFICIENT_RESOURCES;
4480 goto end2;
4481 }
4482
4483 item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4484 item->name.Buffer = ea->EaName;
4485
4486 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4487 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4488
4489 item->flags = ea->Flags;
4490
4491 InsertTailList(&ealist, &item->list_entry);
4492 }
4493
4494 if (ea->NextEntryOffset == 0)
4495 break;
4496
4497 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4498 } while (TRUE);
4499
4500 // remove entries with zero-length value
4501 le = ealist.Flink;
4502 while (le != &ealist) {
4503 LIST_ENTRY* le2 = le->Flink;
4504
4505 item = CONTAINING_RECORD(le, ea_item, list_entry);
4506
4507 if (item->value.Length == 0) {
4508 RemoveEntryList(&item->list_entry);
4509 ExFreePool(item);
4510 }
4511
4512 le = le2;
4513 }
4514
4515 // handle LXSS values
4516 le = ealist.Flink;
4517 while (le != &ealist) {
4518 LIST_ENTRY* le2 = le->Flink;
4519
4520 item = CONTAINING_RECORD(le, ea_item, list_entry);
4521
4522 if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
4523 if (item->value.Length < sizeof(UINT32)) {
4524 ERR("uid value was shorter than expected\n");
4525 Status = STATUS_INVALID_PARAMETER;
4526 goto end2;
4527 }
4528
4529 if (Irp->RequestorMode == KernelMode) {
4530 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(UINT32));
4531 fcb->sd_dirty = TRUE;
4532 fcb->sd_deleted = FALSE;
4533 }
4534
4535 RemoveEntryList(&item->list_entry);
4536 ExFreePool(item);
4537 } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
4538 if (item->value.Length < sizeof(UINT32)) {
4539 ERR("gid value was shorter than expected\n");
4540 Status = STATUS_INVALID_PARAMETER;
4541 goto end2;
4542 }
4543
4544 if (Irp->RequestorMode == KernelMode)
4545 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(UINT32));
4546
4547 RemoveEntryList(&item->list_entry);
4548 ExFreePool(item);
4549 } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
4550 if (item->value.Length < sizeof(UINT32)) {
4551 ERR("mode value was shorter than expected\n");
4552 Status = STATUS_INVALID_PARAMETER;
4553 goto end2;
4554 }
4555
4556 if (Irp->RequestorMode == KernelMode) {
4557 UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
4558 UINT32 val;
4559
4560 RtlCopyMemory(&val, item->value.Buffer, sizeof(UINT32));
4561
4562 fcb->inode_item.st_mode &= ~allowed;
4563 fcb->inode_item.st_mode |= val & allowed;
4564 }
4565
4566 RemoveEntryList(&item->list_entry);
4567 ExFreePool(item);
4568 }
4569
4570 le = le2;
4571 }
4572
4573 if (IsListEmpty(&ealist)) {
4574 fcb->ealen = 0;
4575
4576 if (fcb->ea_xattr.Buffer)
4577 ExFreePool(fcb->ea_xattr.Buffer);
4578
4579 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
4580 fcb->ea_xattr.Buffer = NULL;
4581 } else {
4582 UINT16 size = 0;
4583 char *buf, *oldbuf;
4584
4585 le = ealist.Flink;
4586 while (le != &ealist) {
4587 item = CONTAINING_RECORD(le, ea_item, list_entry);
4588
4589 if (size % 4 > 0)
4590 size += 4 - (size % 4);
4591
4592 size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
4593
4594 le = le->Flink;
4595 }
4596
4597 buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
4598 if (!buf) {
4599 ERR("out of memory\n");
4600 Status = STATUS_INSUFFICIENT_RESOURCES;
4601 goto end2;
4602 }
4603
4604 oldbuf = fcb->ea_xattr.Buffer;
4605
4606 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
4607 fcb->ea_xattr.Buffer = buf;
4608
4609 fcb->ealen = 4;
4610 ea = NULL;
4611
4612 le = ealist.Flink;
4613 while (le != &ealist) {
4614 item = CONTAINING_RECORD(le, ea_item, list_entry);
4615
4616 if (ea) {
4617 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
4618
4619 if (ea->NextEntryOffset % 4 > 0)
4620 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
4621
4622 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4623 } else
4624 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4625
4626 ea->NextEntryOffset = 0;
4627 ea->Flags = item->flags;
4628 ea->EaNameLength = (UCHAR)item->name.Length;
4629 ea->EaValueLength = item->value.Length;
4630
4631 RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
4632 ea->EaName[item->name.Length] = 0;
4633 RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
4634
4635 fcb->ealen += 5 + item->name.Length + item->value.Length;
4636
4637 le = le->Flink;
4638 }
4639
4640 if (oldbuf)
4641 ExFreePool(oldbuf);
4642 }
4643
4644 fcb->ea_changed = TRUE;
4645
4646 KeQuerySystemTime(&time);
4647 win_time_to_unix(time, &now);
4648
4649 fcb->inode_item.transid = Vcb->superblock.generation;
4650 fcb->inode_item.sequence++;
4651
4652 if (!ccb->user_set_change_time)
4653 fcb->inode_item.st_ctime = now;
4654
4655 fcb->inode_item_changed = TRUE;
4656 mark_fcb_dirty(fcb);
4657
4658 send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
4659
4660 Status = STATUS_SUCCESS;
4661
4662 end2:
4663 ExReleaseResourceLite(fcb->Header.Resource);
4664
4665 while (!IsListEmpty(&ealist)) {
4666 le = RemoveHeadList(&ealist);
4667
4668 item = CONTAINING_RECORD(le, ea_item, list_entry);
4669
4670 ExFreePool(item);
4671 }
4672
4673 end:
4674 TRACE("returning %08x\n", Status);
4675
4676 Irp->IoStatus.Status = Status;
4677 Irp->IoStatus.Information = 0;
4678
4679 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4680
4681 if (top_level)
4682 IoSetTopLevelIrp(NULL);
4683
4684 FsRtlExitFileSystem();
4685
4686 return Status;
4687 }