[BTRFS] Upgrade to 1.4
[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 TRACE("%S\n", file_desc(FileObject));
418
419 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
420 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
421
422 Status = set_reparse_point2(fcb, rdb, buflen, ccb, fileref, Irp, &rollback);
423 if (!NT_SUCCESS(Status)) {
424 ERR("set_reparse_point2 returned %08x\n", Status);
425 goto end;
426 }
427
428 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
429
430 end:
431 if (NT_SUCCESS(Status))
432 clear_rollback(&rollback);
433 else
434 do_rollback(fcb->Vcb, &rollback);
435
436 ExReleaseResourceLite(fcb->Header.Resource);
437 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
438
439 return Status;
440 }
441
442 NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
443 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
444 PFILE_OBJECT FileObject = IrpSp->FileObject;
445 REPARSE_DATA_BUFFER* rdb = Irp->AssociatedIrp.SystemBuffer;
446 DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
447 NTSTATUS Status;
448 fcb* fcb;
449 ccb* ccb;
450 file_ref* fileref;
451 LIST_ENTRY rollback;
452
453 TRACE("(%p, %p)\n", DeviceObject, Irp);
454
455 InitializeListHead(&rollback);
456
457 if (!FileObject) {
458 ERR("FileObject was NULL\n");
459 return STATUS_INVALID_PARAMETER;
460 }
461
462 fcb = FileObject->FsContext;
463
464 if (!fcb) {
465 ERR("fcb was NULL\n");
466 return STATUS_INVALID_PARAMETER;
467 }
468
469 ccb = FileObject->FsContext2;
470
471 if (!ccb) {
472 ERR("ccb was NULL\n");
473 return STATUS_INVALID_PARAMETER;
474 }
475
476 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
477 WARN("insufficient privileges\n");
478 return STATUS_ACCESS_DENIED;
479 }
480
481 fileref = ccb->fileref;
482
483 if (!fileref) {
484 ERR("fileref was NULL\n");
485 return STATUS_INVALID_PARAMETER;
486 }
487
488 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
489 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
490
491 TRACE("%S\n", file_desc(FileObject));
492
493 if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
494 ERR("buffer was too short\n");
495 Status = STATUS_INVALID_PARAMETER;
496 goto end;
497 }
498
499 if (rdb->ReparseDataLength > 0) {
500 WARN("rdb->ReparseDataLength was not zero\n");
501 Status = STATUS_INVALID_PARAMETER;
502 goto end;
503 }
504
505 if (fcb->ads) {
506 WARN("tried to delete reparse point on ADS\n");
507 Status = STATUS_INVALID_PARAMETER;
508 goto end;
509 }
510
511 if (fcb->type == BTRFS_TYPE_SYMLINK) {
512 LARGE_INTEGER time;
513 BTRFS_TIME now;
514
515 if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
516 WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n");
517 Status = STATUS_INVALID_PARAMETER;
518 goto end;
519 }
520
521 KeQuerySystemTime(&time);
522 win_time_to_unix(time, &now);
523
524 fileref->fcb->type = BTRFS_TYPE_FILE;
525 fileref->fcb->inode_item.st_mode &= ~__S_IFLNK;
526 fileref->fcb->inode_item.st_mode |= __S_IFREG;
527 fileref->fcb->inode_item.generation = fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux
528 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
529 fileref->fcb->inode_item.sequence++;
530
531 if (!ccb->user_set_change_time)
532 fileref->fcb->inode_item.st_ctime = now;
533
534 if (!ccb->user_set_write_time)
535 fileref->fcb->inode_item.st_mtime = now;
536
537 fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
538
539 if (fileref->dc)
540 fileref->dc->type = fileref->fcb->type;
541
542 mark_fileref_dirty(fileref);
543
544 fileref->fcb->inode_item_changed = true;
545 mark_fcb_dirty(fileref->fcb);
546
547 fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
548 fileref->fcb->subvol->root_item.ctime = now;
549 } else if (fcb->type == BTRFS_TYPE_FILE) {
550 LARGE_INTEGER time;
551 BTRFS_TIME now;
552
553 // FIXME - do we need to check that the reparse tags match?
554
555 Status = truncate_file(fcb, 0, Irp, &rollback);
556 if (!NT_SUCCESS(Status)) {
557 ERR("truncate_file returned %08x\n", Status);
558 goto end;
559 }
560
561 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
562 fcb->atts_changed = true;
563
564 KeQuerySystemTime(&time);
565 win_time_to_unix(time, &now);
566
567 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
568 fcb->inode_item.sequence++;
569
570 if (!ccb->user_set_change_time)
571 fcb->inode_item.st_ctime = now;
572
573 if (!ccb->user_set_write_time)
574 fcb->inode_item.st_mtime = now;
575
576 fcb->inode_item_changed = true;
577 mark_fcb_dirty(fcb);
578
579 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
580 fcb->subvol->root_item.ctime = now;
581 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
582 LARGE_INTEGER time;
583 BTRFS_TIME now;
584
585 // FIXME - do we need to check that the reparse tags match?
586
587 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
588 fcb->atts_changed = true;
589
590 if (fcb->reparse_xattr.Buffer) {
591 ExFreePool(fcb->reparse_xattr.Buffer);
592 fcb->reparse_xattr.Buffer = NULL;
593 }
594
595 fcb->reparse_xattr_changed = true;
596
597 KeQuerySystemTime(&time);
598 win_time_to_unix(time, &now);
599
600 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
601 fcb->inode_item.sequence++;
602
603 if (!ccb->user_set_change_time)
604 fcb->inode_item.st_ctime = now;
605
606 if (!ccb->user_set_write_time)
607 fcb->inode_item.st_mtime = now;
608
609 fcb->inode_item_changed = true;
610 mark_fcb_dirty(fcb);
611
612 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
613 fcb->subvol->root_item.ctime = now;
614 } else {
615 ERR("unsupported file type %u\n", fcb->type);
616 Status = STATUS_INVALID_PARAMETER;
617 goto end;
618 }
619
620 Status = STATUS_SUCCESS;
621
622 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
623
624 end:
625 if (NT_SUCCESS(Status))
626 clear_rollback(&rollback);
627 else
628 do_rollback(fcb->Vcb, &rollback);
629
630 ExReleaseResourceLite(fcb->Header.Resource);
631 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
632
633 return Status;
634 }