[IPHLPAPI] Implement getOwnerModUdpTable() and getOwnerModTcpTable()
[reactos.git] / drivers / filesystems / btrfs / reparse.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 NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, ULONG_PTR* retlen) {
21 USHORT subnamelen, printnamelen, i;
22 ULONG stringlen;
23 DWORD reqlen;
24 REPARSE_DATA_BUFFER* rdb = buffer;
25 fcb* fcb = FileObject->FsContext;
26 ccb* ccb = FileObject->FsContext2;
27 NTSTATUS Status;
28
29 TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
30
31 if (!ccb)
32 return STATUS_INVALID_PARAMETER;
33
34 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
35 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
36
37 if (fcb->type == BTRFS_TYPE_SYMLINK) {
38 if (ccb->lxss) {
39 reqlen = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + sizeof(UINT32);
40
41 if (buflen < reqlen) {
42 Status = STATUS_BUFFER_OVERFLOW;
43 goto end;
44 }
45
46 rdb->ReparseTag = IO_REPARSE_TAG_LXSS_SYMLINK;
47 rdb->ReparseDataLength = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + sizeof(UINT32);
48 rdb->Reserved = 0;
49
50 *((UINT32*)rdb->GenericReparseBuffer.DataBuffer) = 1;
51
52 *retlen = reqlen;
53 } else {
54 char* data;
55
56 if (fcb->inode_item.st_size == 0 || fcb->inode_item.st_size > 0xffff) {
57 Status = STATUS_INVALID_PARAMETER;
58 goto end;
59 }
60
61 data = ExAllocatePoolWithTag(PagedPool, (ULONG)fcb->inode_item.st_size, ALLOC_TAG);
62 if (!data) {
63 ERR("out of memory\n");
64 Status = STATUS_INSUFFICIENT_RESOURCES;
65 goto end;
66 }
67
68 TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
69 Status = read_file(fcb, (UINT8*)data, 0, fcb->inode_item.st_size, NULL, NULL);
70
71 if (!NT_SUCCESS(Status)) {
72 ERR("read_file returned %08x\n", Status);
73 ExFreePool(data);
74 goto end;
75 }
76
77 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, (ULONG)fcb->inode_item.st_size);
78 if (!NT_SUCCESS(Status)) {
79 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
80 ExFreePool(data);
81 goto end;
82 }
83
84 subnamelen = (UINT16)stringlen;
85 printnamelen = (UINT16)stringlen;
86
87 reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
88
89 if (buflen >= offsetof(REPARSE_DATA_BUFFER, ReparseDataLength))
90 rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
91
92 if (buflen >= offsetof(REPARSE_DATA_BUFFER, Reserved))
93 rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
94
95 if (buflen >= offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset))
96 rdb->Reserved = 0;
97
98 if (buflen < reqlen) {
99 ExFreePool(data);
100 Status = STATUS_BUFFER_OVERFLOW;
101 *retlen = min(buflen, offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset));
102 goto end;
103 }
104
105 rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
106 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
107 rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
108 rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
109 rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
110
111 Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
112 stringlen, &stringlen, data, (ULONG)fcb->inode_item.st_size);
113
114 if (!NT_SUCCESS(Status)) {
115 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
116 ExFreePool(data);
117 goto end;
118 }
119
120 for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
121 if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
122 rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
123 }
124
125 RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
126 &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
127 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
128
129 *retlen = reqlen;
130
131 ExFreePool(data);
132 }
133
134 Status = STATUS_SUCCESS;
135 } else if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
136 if (fcb->type == BTRFS_TYPE_FILE) {
137 ULONG len;
138
139 Status = read_file(fcb, buffer, 0, buflen, &len, NULL);
140
141 if (!NT_SUCCESS(Status)) {
142 ERR("read_file returned %08x\n", Status);
143 }
144
145 *retlen = len;
146 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
147 if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG)) {
148 Status = STATUS_NOT_A_REPARSE_POINT;
149 goto end;
150 }
151
152 if (buflen > 0) {
153 *retlen = min(buflen, fcb->reparse_xattr.Length);
154 RtlCopyMemory(buffer, fcb->reparse_xattr.Buffer, *retlen);
155 } else
156 *retlen = 0;
157
158 Status = *retlen == fcb->reparse_xattr.Length ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW;
159 } else
160 Status = STATUS_NOT_A_REPARSE_POINT;
161 } else {
162 Status = STATUS_NOT_A_REPARSE_POINT;
163 }
164
165 end:
166 ExReleaseResourceLite(fcb->Header.Resource);
167 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
168
169 return Status;
170 }
171
172 static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, ccb* ccb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, BOOL write, LIST_ENTRY* rollback) {
173 NTSTATUS Status;
174 ULONG minlen;
175 ULONG tlength;
176 UNICODE_STRING subname;
177 ANSI_STRING target;
178 LARGE_INTEGER offset, time;
179 BTRFS_TIME now;
180 USHORT i;
181
182 if (write) {
183 minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
184 if (buflen < minlen) {
185 WARN("buffer was less than minimum length (%u < %u)\n", buflen, minlen);
186 return STATUS_INVALID_PARAMETER;
187 }
188
189 if (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength < sizeof(WCHAR)) {
190 WARN("rdb->SymbolicLinkReparseBuffer.SubstituteNameLength was too short\n");
191 return STATUS_INVALID_PARAMETER;
192 }
193
194 subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
195 subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
196
197 TRACE("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
198 }
199
200 fileref->fcb->type = BTRFS_TYPE_SYMLINK;
201 fileref->fcb->inode_item.st_mode |= __S_IFLNK;
202 fileref->fcb->inode_item.generation = fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux
203
204 if (fileref->dc)
205 fileref->dc->type = fileref->fcb->type;
206
207 if (write) {
208 Status = truncate_file(fileref->fcb, 0, Irp, rollback);
209 if (!NT_SUCCESS(Status)) {
210 ERR("truncate_file returned %08x\n", Status);
211 return Status;
212 }
213
214 Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
215 if (!NT_SUCCESS(Status)) {
216 ERR("RtlUnicodeToUTF8N 1 failed with error %08x\n", Status);
217 return Status;
218 }
219
220 target.MaximumLength = target.Length;
221 target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
222 if (!target.Buffer) {
223 ERR("out of memory\n");
224 return STATUS_INSUFFICIENT_RESOURCES;
225 }
226
227 Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length);
228 if (!NT_SUCCESS(Status)) {
229 ERR("RtlUnicodeToUTF8N 2 failed with error %08x\n", Status);
230 ExFreePool(target.Buffer);
231 return Status;
232 }
233
234 for (i = 0; i < target.MaximumLength; i++) {
235 if (target.Buffer[i] == '\\')
236 target.Buffer[i] = '/';
237 }
238
239 offset.QuadPart = 0;
240 tlength = target.Length;
241 Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, &tlength, FALSE, TRUE,
242 TRUE, FALSE, FALSE, rollback);
243 ExFreePool(target.Buffer);
244 } else
245 Status = STATUS_SUCCESS;
246
247 KeQuerySystemTime(&time);
248 win_time_to_unix(time, &now);
249
250 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
251 fileref->fcb->inode_item.sequence++;
252
253 if (!ccb->user_set_change_time)
254 fileref->fcb->inode_item.st_ctime = now;
255
256 if (!ccb->user_set_write_time)
257 fileref->fcb->inode_item.st_mtime = now;
258
259 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
260 fileref->fcb->subvol->root_item.ctime = now;
261
262 fileref->fcb->inode_item_changed = TRUE;
263 mark_fcb_dirty(fileref->fcb);
264
265 mark_fileref_dirty(fileref);
266
267 return Status;
268 }
269
270 NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
271 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
272 PFILE_OBJECT FileObject = IrpSp->FileObject;
273 void* buffer = Irp->AssociatedIrp.SystemBuffer;
274 REPARSE_DATA_BUFFER* rdb = buffer;
275 DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
276 NTSTATUS Status = STATUS_SUCCESS;
277 fcb* fcb;
278 ccb* ccb;
279 file_ref* fileref;
280 ULONG tag;
281 LIST_ENTRY rollback;
282
283 TRACE("(%p, %p)\n", DeviceObject, Irp);
284
285 InitializeListHead(&rollback);
286
287 if (!FileObject) {
288 ERR("FileObject was NULL\n");
289 return STATUS_INVALID_PARAMETER;
290 }
291
292 // IFSTest insists on this, for some reason...
293 if (Irp->UserBuffer)
294 return STATUS_INVALID_PARAMETER;
295
296 fcb = FileObject->FsContext;
297 ccb = FileObject->FsContext2;
298
299 if (!ccb) {
300 ERR("ccb was NULL\n");
301 return STATUS_INVALID_PARAMETER;
302 }
303
304 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA))) {
305 WARN("insufficient privileges\n");
306 return STATUS_ACCESS_DENIED;
307 }
308
309 fileref = ccb->fileref;
310
311 if (!fileref) {
312 ERR("fileref was NULL\n");
313 return STATUS_INVALID_PARAMETER;
314 }
315
316 if (fcb->ads) {
317 fileref = fileref->parent;
318 fcb = fileref->fcb;
319 }
320
321 TRACE("%S\n", file_desc(FileObject));
322
323 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
324 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
325
326 if (fcb->type == BTRFS_TYPE_SYMLINK) {
327 WARN("tried to set a reparse point on an existing symlink\n");
328 Status = STATUS_INVALID_PARAMETER;
329 goto end;
330 }
331
332 // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
333
334 // FIXME - die if not file or directory
335
336 if (buflen < sizeof(ULONG)) {
337 WARN("buffer was not long enough to hold tag\n");
338 Status = STATUS_INVALID_BUFFER_SIZE;
339 goto end;
340 }
341
342 Status = FsRtlValidateReparsePointBuffer(buflen, rdb);
343 if (!NT_SUCCESS(Status)) {
344 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
345 goto end;
346 }
347
348 RtlCopyMemory(&tag, buffer, sizeof(ULONG));
349
350 if (fcb->type == BTRFS_TYPE_FILE &&
351 ((tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) || tag == IO_REPARSE_TAG_LXSS_SYMLINK)) {
352 Status = set_symlink(Irp, fileref, ccb, rdb, buflen, tag == IO_REPARSE_TAG_SYMLINK, &rollback);
353 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
354 } else {
355 LARGE_INTEGER offset, time;
356 BTRFS_TIME now;
357
358 if (fcb->type == BTRFS_TYPE_DIRECTORY) { // for directories, store as xattr
359 ANSI_STRING buf;
360
361 buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
362 if (!buf.Buffer) {
363 ERR("out of memory\n");
364 Status = STATUS_INSUFFICIENT_RESOURCES;
365 goto end;
366 }
367 buf.Length = buf.MaximumLength = (UINT16)buflen;
368
369 if (fcb->reparse_xattr.Buffer)
370 ExFreePool(fcb->reparse_xattr.Buffer);
371
372 fcb->reparse_xattr = buf;
373 RtlCopyMemory(buf.Buffer, buffer, buflen);
374
375 fcb->reparse_xattr_changed = TRUE;
376
377 Status = STATUS_SUCCESS;
378 } else { // otherwise, store as file data
379 Status = truncate_file(fcb, 0, Irp, &rollback);
380 if (!NT_SUCCESS(Status)) {
381 ERR("truncate_file returned %08x\n", Status);
382 goto end;
383 }
384
385 offset.QuadPart = 0;
386
387 Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, FALSE, TRUE, TRUE, FALSE, FALSE, &rollback);
388 if (!NT_SUCCESS(Status)) {
389 ERR("write_file2 returned %08x\n", Status);
390 goto end;
391 }
392 }
393
394 KeQuerySystemTime(&time);
395 win_time_to_unix(time, &now);
396
397 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
398 fcb->inode_item.sequence++;
399
400 if (!ccb->user_set_change_time)
401 fcb->inode_item.st_ctime = now;
402
403 if (!ccb->user_set_write_time)
404 fcb->inode_item.st_mtime = now;
405
406 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
407 fcb->atts_changed = TRUE;
408
409 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
410 fcb->subvol->root_item.ctime = now;
411
412 fcb->inode_item_changed = TRUE;
413 mark_fcb_dirty(fcb);
414 }
415
416 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
417
418 end:
419 if (NT_SUCCESS(Status))
420 clear_rollback(&rollback);
421 else
422 do_rollback(fcb->Vcb, &rollback);
423
424 ExReleaseResourceLite(fcb->Header.Resource);
425 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
426
427 return Status;
428 }
429
430 NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
431 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
432 PFILE_OBJECT FileObject = IrpSp->FileObject;
433 REPARSE_DATA_BUFFER* rdb = Irp->AssociatedIrp.SystemBuffer;
434 DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
435 NTSTATUS Status;
436 fcb* fcb;
437 ccb* ccb;
438 file_ref* fileref;
439 LIST_ENTRY rollback;
440
441 TRACE("(%p, %p)\n", DeviceObject, Irp);
442
443 InitializeListHead(&rollback);
444
445 if (!FileObject) {
446 ERR("FileObject was NULL\n");
447 return STATUS_INVALID_PARAMETER;
448 }
449
450 fcb = FileObject->FsContext;
451
452 if (!fcb) {
453 ERR("fcb was NULL\n");
454 return STATUS_INVALID_PARAMETER;
455 }
456
457 ccb = FileObject->FsContext2;
458
459 if (!ccb) {
460 ERR("ccb was NULL\n");
461 return STATUS_INVALID_PARAMETER;
462 }
463
464 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
465 WARN("insufficient privileges\n");
466 return STATUS_ACCESS_DENIED;
467 }
468
469 fileref = ccb->fileref;
470
471 if (!fileref) {
472 ERR("fileref was NULL\n");
473 return STATUS_INVALID_PARAMETER;
474 }
475
476 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
477 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
478
479 TRACE("%S\n", file_desc(FileObject));
480
481 if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
482 ERR("buffer was too short\n");
483 Status = STATUS_INVALID_PARAMETER;
484 goto end;
485 }
486
487 if (rdb->ReparseDataLength > 0) {
488 WARN("rdb->ReparseDataLength was not zero\n");
489 Status = STATUS_INVALID_PARAMETER;
490 goto end;
491 }
492
493 if (fcb->ads) {
494 WARN("tried to delete reparse point on ADS\n");
495 Status = STATUS_INVALID_PARAMETER;
496 goto end;
497 }
498
499 if (fcb->type == BTRFS_TYPE_SYMLINK) {
500 LARGE_INTEGER time;
501 BTRFS_TIME now;
502
503 if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
504 WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n");
505 Status = STATUS_INVALID_PARAMETER;
506 goto end;
507 }
508
509 KeQuerySystemTime(&time);
510 win_time_to_unix(time, &now);
511
512 fileref->fcb->type = BTRFS_TYPE_FILE;
513 fileref->fcb->inode_item.st_mode &= ~__S_IFLNK;
514 fileref->fcb->inode_item.st_mode |= __S_IFREG;
515 fileref->fcb->inode_item.generation = fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux
516 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
517 fileref->fcb->inode_item.sequence++;
518
519 if (!ccb->user_set_change_time)
520 fileref->fcb->inode_item.st_ctime = now;
521
522 if (!ccb->user_set_write_time)
523 fileref->fcb->inode_item.st_mtime = now;
524
525 fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
526
527 if (fileref->dc)
528 fileref->dc->type = fileref->fcb->type;
529
530 mark_fileref_dirty(fileref);
531
532 fileref->fcb->inode_item_changed = TRUE;
533 mark_fcb_dirty(fileref->fcb);
534
535 fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
536 fileref->fcb->subvol->root_item.ctime = now;
537 } else if (fcb->type == BTRFS_TYPE_FILE) {
538 LARGE_INTEGER time;
539 BTRFS_TIME now;
540
541 // FIXME - do we need to check that the reparse tags match?
542
543 Status = truncate_file(fcb, 0, Irp, &rollback);
544 if (!NT_SUCCESS(Status)) {
545 ERR("truncate_file returned %08x\n", Status);
546 goto end;
547 }
548
549 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
550 fcb->atts_changed = TRUE;
551
552 KeQuerySystemTime(&time);
553 win_time_to_unix(time, &now);
554
555 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
556 fcb->inode_item.sequence++;
557
558 if (!ccb->user_set_change_time)
559 fcb->inode_item.st_ctime = now;
560
561 if (!ccb->user_set_write_time)
562 fcb->inode_item.st_mtime = now;
563
564 fcb->inode_item_changed = TRUE;
565 mark_fcb_dirty(fcb);
566
567 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
568 fcb->subvol->root_item.ctime = now;
569 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
570 LARGE_INTEGER time;
571 BTRFS_TIME now;
572
573 // FIXME - do we need to check that the reparse tags match?
574
575 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
576 fcb->atts_changed = TRUE;
577
578 if (fcb->reparse_xattr.Buffer) {
579 ExFreePool(fcb->reparse_xattr.Buffer);
580 fcb->reparse_xattr.Buffer = NULL;
581 }
582
583 fcb->reparse_xattr_changed = TRUE;
584
585 KeQuerySystemTime(&time);
586 win_time_to_unix(time, &now);
587
588 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
589 fcb->inode_item.sequence++;
590
591 if (!ccb->user_set_change_time)
592 fcb->inode_item.st_ctime = now;
593
594 if (!ccb->user_set_write_time)
595 fcb->inode_item.st_mtime = now;
596
597 fcb->inode_item_changed = TRUE;
598 mark_fcb_dirty(fcb);
599
600 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
601 fcb->subvol->root_item.ctime = now;
602 } else {
603 ERR("unsupported file type %u\n", fcb->type);
604 Status = STATUS_INVALID_PARAMETER;
605 goto end;
606 }
607
608 Status = STATUS_SUCCESS;
609
610 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
611
612 end:
613 if (NT_SUCCESS(Status))
614 clear_rollback(&rollback);
615 else
616 do_rollback(fcb->Vcb, &rollback);
617
618 ExReleaseResourceLite(fcb->Header.Resource);
619 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
620
621 return Status;
622 }