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