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 } 631