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