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, fcb* fcb, 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 fcb->type = BTRFS_TYPE_SYMLINK; 201 fcb->inode_item.st_mode |= __S_IFLNK; 202 fcb->inode_item.generation = fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux 203 204 if (fileref && fileref->dc) 205 fileref->dc->type = fcb->type; 206 207 if (write) { 208 Status = truncate_file(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(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 fcb->inode_item.transid = fcb->Vcb->superblock.generation; 251 fcb->inode_item.sequence++; 252 253 if (!ccb || !ccb->user_set_change_time) 254 fcb->inode_item.st_ctime = now; 255 256 if (!ccb || !ccb->user_set_write_time) 257 fcb->inode_item.st_mtime = now; 258 259 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; 260 fcb->subvol->root_item.ctime = now; 261 262 fcb->inode_item_changed = TRUE; 263 mark_fcb_dirty(fcb); 264 265 if (fileref) 266 mark_fileref_dirty(fileref); 267 268 return Status; 269 } 270 271 NTSTATUS set_reparse_point2(fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, ccb* ccb, file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) { 272 NTSTATUS Status; 273 ULONG tag; 274 275 if (fcb->type == BTRFS_TYPE_SYMLINK) { 276 WARN("tried to set a reparse point on an existing symlink\n"); 277 return STATUS_INVALID_PARAMETER; 278 } 279 280 // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT 281 282 // FIXME - die if not file or directory 283 284 if (buflen < sizeof(ULONG)) { 285 WARN("buffer was not long enough to hold tag\n"); 286 return STATUS_INVALID_BUFFER_SIZE; 287 } 288 289 Status = FsRtlValidateReparsePointBuffer(buflen, rdb); 290 if (!NT_SUCCESS(Status)) { 291 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status); 292 return Status; 293 } 294 295 RtlCopyMemory(&tag, rdb, sizeof(ULONG)); 296 297 if (fcb->type == BTRFS_TYPE_FILE && 298 ((tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) || tag == IO_REPARSE_TAG_LXSS_SYMLINK)) { 299 Status = set_symlink(Irp, fileref, fcb, ccb, rdb, buflen, tag == IO_REPARSE_TAG_SYMLINK, rollback); 300 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; 301 } else { 302 LARGE_INTEGER offset, time; 303 BTRFS_TIME now; 304 305 if (fcb->type == BTRFS_TYPE_DIRECTORY || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV) { // store as xattr 306 ANSI_STRING buf; 307 308 buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG); 309 if (!buf.Buffer) { 310 ERR("out of memory\n"); 311 return STATUS_INSUFFICIENT_RESOURCES; 312 } 313 buf.Length = buf.MaximumLength = (UINT16)buflen; 314 315 if (fcb->reparse_xattr.Buffer) 316 ExFreePool(fcb->reparse_xattr.Buffer); 317 318 fcb->reparse_xattr = buf; 319 RtlCopyMemory(buf.Buffer, rdb, buflen); 320 321 fcb->reparse_xattr_changed = TRUE; 322 323 Status = STATUS_SUCCESS; 324 } else { // otherwise, store as file data 325 Status = truncate_file(fcb, 0, Irp, rollback); 326 if (!NT_SUCCESS(Status)) { 327 ERR("truncate_file returned %08x\n", Status); 328 return Status; 329 } 330 331 offset.QuadPart = 0; 332 333 Status = write_file2(fcb->Vcb, Irp, offset, rdb, &buflen, FALSE, TRUE, TRUE, FALSE, FALSE, rollback); 334 if (!NT_SUCCESS(Status)) { 335 ERR("write_file2 returned %08x\n", Status); 336 return Status; 337 } 338 } 339 340 KeQuerySystemTime(&time); 341 win_time_to_unix(time, &now); 342 343 fcb->inode_item.transid = fcb->Vcb->superblock.generation; 344 fcb->inode_item.sequence++; 345 346 if (!ccb || !ccb->user_set_change_time) 347 fcb->inode_item.st_ctime = now; 348 349 if (!ccb || !ccb->user_set_write_time) 350 fcb->inode_item.st_mtime = now; 351 352 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; 353 fcb->atts_changed = TRUE; 354 355 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; 356 fcb->subvol->root_item.ctime = now; 357 358 fcb->inode_item_changed = TRUE; 359 mark_fcb_dirty(fcb); 360 } 361 362 return STATUS_SUCCESS; 363 } 364 365 NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 366 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 367 PFILE_OBJECT FileObject = IrpSp->FileObject; 368 void* buffer = Irp->AssociatedIrp.SystemBuffer; 369 REPARSE_DATA_BUFFER* rdb = buffer; 370 DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength; 371 NTSTATUS Status = STATUS_SUCCESS; 372 fcb* fcb; 373 ccb* ccb; 374 file_ref* fileref; 375 LIST_ENTRY rollback; 376 377 TRACE("(%p, %p)\n", DeviceObject, Irp); 378 379 InitializeListHead(&rollback); 380 381 if (!FileObject) { 382 ERR("FileObject was NULL\n"); 383 return STATUS_INVALID_PARAMETER; 384 } 385 386 // IFSTest insists on this, for some reason... 387 if (Irp->UserBuffer) 388 return STATUS_INVALID_PARAMETER; 389 390 fcb = FileObject->FsContext; 391 ccb = FileObject->FsContext2; 392 393 if (!ccb) { 394 ERR("ccb was NULL\n"); 395 return STATUS_INVALID_PARAMETER; 396 } 397 398 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA))) { 399 WARN("insufficient privileges\n"); 400 return STATUS_ACCESS_DENIED; 401 } 402 403 fileref = ccb->fileref; 404 405 if (!fileref) { 406 ERR("fileref was NULL\n"); 407 return STATUS_INVALID_PARAMETER; 408 } 409 410 if (fcb->ads) { 411 fileref = fileref->parent; 412 fcb = fileref->fcb; 413 } 414 415 TRACE("%S\n", file_desc(FileObject)); 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 send_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 TRACE("%S\n", file_desc(FileObject)); 490 491 if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) { 492 ERR("buffer was too short\n"); 493 Status = STATUS_INVALID_PARAMETER; 494 goto end; 495 } 496 497 if (rdb->ReparseDataLength > 0) { 498 WARN("rdb->ReparseDataLength was not zero\n"); 499 Status = STATUS_INVALID_PARAMETER; 500 goto end; 501 } 502 503 if (fcb->ads) { 504 WARN("tried to delete reparse point on ADS\n"); 505 Status = STATUS_INVALID_PARAMETER; 506 goto end; 507 } 508 509 if (fcb->type == BTRFS_TYPE_SYMLINK) { 510 LARGE_INTEGER time; 511 BTRFS_TIME now; 512 513 if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) { 514 WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n"); 515 Status = STATUS_INVALID_PARAMETER; 516 goto end; 517 } 518 519 KeQuerySystemTime(&time); 520 win_time_to_unix(time, &now); 521 522 fileref->fcb->type = BTRFS_TYPE_FILE; 523 fileref->fcb->inode_item.st_mode &= ~__S_IFLNK; 524 fileref->fcb->inode_item.st_mode |= __S_IFREG; 525 fileref->fcb->inode_item.generation = fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux 526 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation; 527 fileref->fcb->inode_item.sequence++; 528 529 if (!ccb->user_set_change_time) 530 fileref->fcb->inode_item.st_ctime = now; 531 532 if (!ccb->user_set_write_time) 533 fileref->fcb->inode_item.st_mtime = now; 534 535 fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; 536 537 if (fileref->dc) 538 fileref->dc->type = fileref->fcb->type; 539 540 mark_fileref_dirty(fileref); 541 542 fileref->fcb->inode_item_changed = TRUE; 543 mark_fcb_dirty(fileref->fcb); 544 545 fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; 546 fileref->fcb->subvol->root_item.ctime = now; 547 } else if (fcb->type == BTRFS_TYPE_FILE) { 548 LARGE_INTEGER time; 549 BTRFS_TIME now; 550 551 // FIXME - do we need to check that the reparse tags match? 552 553 Status = truncate_file(fcb, 0, Irp, &rollback); 554 if (!NT_SUCCESS(Status)) { 555 ERR("truncate_file returned %08x\n", Status); 556 goto end; 557 } 558 559 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; 560 fcb->atts_changed = TRUE; 561 562 KeQuerySystemTime(&time); 563 win_time_to_unix(time, &now); 564 565 fcb->inode_item.transid = fcb->Vcb->superblock.generation; 566 fcb->inode_item.sequence++; 567 568 if (!ccb->user_set_change_time) 569 fcb->inode_item.st_ctime = now; 570 571 if (!ccb->user_set_write_time) 572 fcb->inode_item.st_mtime = now; 573 574 fcb->inode_item_changed = TRUE; 575 mark_fcb_dirty(fcb); 576 577 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; 578 fcb->subvol->root_item.ctime = now; 579 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { 580 LARGE_INTEGER time; 581 BTRFS_TIME now; 582 583 // FIXME - do we need to check that the reparse tags match? 584 585 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; 586 fcb->atts_changed = TRUE; 587 588 if (fcb->reparse_xattr.Buffer) { 589 ExFreePool(fcb->reparse_xattr.Buffer); 590 fcb->reparse_xattr.Buffer = NULL; 591 } 592 593 fcb->reparse_xattr_changed = TRUE; 594 595 KeQuerySystemTime(&time); 596 win_time_to_unix(time, &now); 597 598 fcb->inode_item.transid = fcb->Vcb->superblock.generation; 599 fcb->inode_item.sequence++; 600 601 if (!ccb->user_set_change_time) 602 fcb->inode_item.st_ctime = now; 603 604 if (!ccb->user_set_write_time) 605 fcb->inode_item.st_mtime = now; 606 607 fcb->inode_item_changed = TRUE; 608 mark_fcb_dirty(fcb); 609 610 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; 611 fcb->subvol->root_item.ctime = now; 612 } else { 613 ERR("unsupported file type %u\n", fcb->type); 614 Status = STATUS_INVALID_PARAMETER; 615 goto end; 616 } 617 618 Status = STATUS_SUCCESS; 619 620 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL); 621 622 end: 623 if (NT_SUCCESS(Status)) 624 clear_rollback(&rollback); 625 else 626 do_rollback(fcb->Vcb, &rollback); 627 628 ExReleaseResourceLite(fcb->Header.Resource); 629 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 630 631 return Status; 632 } 633