Something went wrong. Try again.
Reactos
Something went wrong. Try again.
23 kB · 672 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673/* Copyright (c) Mark Harmstone 2016-17 * * This file is part of WinBtrfs. * * WinBtrfs is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public Licence as published by * the Free Software Foundation, either version 3 of the Licence, or * (at your option) any later version. * * WinBtrfs is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public Licence for more details. * * You should have received a copy of the GNU Lesser General Public Licence * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
extern tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
typedef struct { uint32_t unknown; char name[1];} REPARSE_DATA_BUFFER_LX_SYMLINK;
NTSTATUS get_reparse_point(PFILE_OBJECT FileObject, void* buffer, DWORD buflen, ULONG_PTR* retlen) { USHORT subnamelen, printnamelen, i; ULONG stringlen; DWORD reqlen; REPARSE_DATA_BUFFER* rdb = buffer; fcb* fcb = FileObject->FsContext; ccb* ccb = FileObject->FsContext2; NTSTATUS Status;
TRACE("(%p, %p, %lx, %p)\n", FileObject, buffer, buflen, retlen);
if (!ccb) return STATUS_INVALID_PARAMETER;
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true); ExAcquireResourceSharedLite(fcb->Header.Resource, true);
if (fcb->type == BTRFS_TYPE_SYMLINK) { if (ccb->lxss) { reqlen = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + sizeof(uint32_t);
if (buflen < reqlen) { Status = STATUS_BUFFER_OVERFLOW; goto end; }
rdb->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK; rdb->ReparseDataLength = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + sizeof(uint32_t); rdb->Reserved = 0;
*((uint32_t*)rdb->GenericReparseBuffer.DataBuffer) = 1;
*retlen = reqlen; } else { char* data;
if (fcb->inode_item.st_size == 0 || fcb->inode_item.st_size > 0xffff) { Status = STATUS_INVALID_PARAMETER; goto end; }
data = ExAllocatePoolWithTag(PagedPool, (ULONG)fcb->inode_item.st_size, ALLOC_TAG); if (!data) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
TRACE("data = %p, size = %I64x\n", data, fcb->inode_item.st_size); Status = read_file(fcb, (uint8_t*)data, 0, fcb->inode_item.st_size, NULL, NULL);
if (!NT_SUCCESS(Status)) { ERR("read_file returned %08lx\n", Status); ExFreePool(data); goto end; }
Status = utf8_to_utf16(NULL, 0, &stringlen, data, (ULONG)fcb->inode_item.st_size); if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 1 returned %08lx\n", Status); ExFreePool(data); goto end; }
subnamelen = (uint16_t)stringlen; printnamelen = (uint16_t)stringlen;
reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
if (buflen >= offsetof(REPARSE_DATA_BUFFER, ReparseDataLength)) rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
if (buflen >= offsetof(REPARSE_DATA_BUFFER, Reserved)) rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
if (buflen >= offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset)) rdb->Reserved = 0;
if (buflen < reqlen) { ExFreePool(data); Status = STATUS_BUFFER_OVERFLOW; *retlen = min(buflen, offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset)); goto end; }
rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen; rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen; rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen; rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
Status = utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], stringlen, &stringlen, data, (ULONG)fcb->inode_item.st_size);
if (!NT_SUCCESS(Status)) { ERR("utf8_to_utf16 2 returned %08lx\n", Status); ExFreePool(data); goto end; }
for (i = 0; i < stringlen / sizeof(WCHAR); i++) { if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/') rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\'; }
RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
*retlen = reqlen;
ExFreePool(data); }
Status = STATUS_SUCCESS; } else if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) { if (fcb->type == BTRFS_TYPE_FILE) { ULONG len;
Status = read_file(fcb, buffer, 0, buflen, &len, NULL);
if (!NT_SUCCESS(Status)) { ERR("read_file returned %08lx\n", Status); }
*retlen = len; } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG)) { Status = STATUS_NOT_A_REPARSE_POINT; goto end; }
if (buflen > 0) { *retlen = min(buflen, fcb->reparse_xattr.Length); RtlCopyMemory(buffer, fcb->reparse_xattr.Buffer, *retlen); } else *retlen = 0;
Status = *retlen == fcb->reparse_xattr.Length ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW; } else Status = STATUS_NOT_A_REPARSE_POINT; } else { Status = STATUS_NOT_A_REPARSE_POINT; }
end: ExReleaseResourceLite(fcb->Header.Resource); ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;}
static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, fcb* fcb, ccb* ccb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) { NTSTATUS Status; ULONG tlength; ANSI_STRING target; bool target_alloc = false; LARGE_INTEGER offset, time; BTRFS_TIME now;
if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) { UNICODE_STRING subname; ULONG minlen, len;
minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR); if (buflen < minlen) { WARN("buffer was less than minimum length (%lu < %lu)\n", buflen, minlen); return STATUS_INVALID_PARAMETER; }
if (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength < sizeof(WCHAR)) { WARN("rdb->SymbolicLinkReparseBuffer.SubstituteNameLength was too short\n"); return STATUS_INVALID_PARAMETER; }
subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)]; subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
TRACE("substitute name = %.*S\n", (int)(subname.Length / sizeof(WCHAR)), subname.Buffer);
Status = utf16_to_utf8(NULL, 0, &len, subname.Buffer, subname.Length); if (!NT_SUCCESS(Status)) { ERR("utf16_to_utf8 1 failed with error %08lx\n", Status); return Status; }
target.MaximumLength = target.Length = (USHORT)len; target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG); if (!target.Buffer) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; }
target_alloc = true;
Status = utf16_to_utf8(target.Buffer, target.Length, &len, subname.Buffer, subname.Length); if (!NT_SUCCESS(Status)) { ERR("utf16_to_utf8 2 failed with error %08lx\n", Status); ExFreePool(target.Buffer); return Status; }
for (USHORT i = 0; i < target.Length; i++) { if (target.Buffer[i] == '\\') target.Buffer[i] = '/'; } } else if (rdb->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK) { REPARSE_DATA_BUFFER_LX_SYMLINK* buf;
if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + rdb->ReparseDataLength) { WARN("buffer was less than expected length (%lu < %lu)\n", buflen, (unsigned long)(offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + rdb->ReparseDataLength)); return STATUS_INVALID_PARAMETER; }
buf = (REPARSE_DATA_BUFFER_LX_SYMLINK*)rdb->GenericReparseBuffer.DataBuffer;
if (buflen < offsetof(REPARSE_DATA_BUFFER_LX_SYMLINK, name)) { WARN("buffer was less than minimum length (%u < %lu)\n", rdb->ReparseDataLength, (unsigned long)(offsetof(REPARSE_DATA_BUFFER_LX_SYMLINK, name))); return STATUS_INVALID_PARAMETER; }
target.Buffer = buf->name; target.Length = target.MaximumLength = rdb->ReparseDataLength - offsetof(REPARSE_DATA_BUFFER_LX_SYMLINK, name); } else { ERR("unexpected reparse tag %08lx\n", rdb->ReparseTag); return STATUS_INTERNAL_ERROR; }
fcb->type = BTRFS_TYPE_SYMLINK; fcb->inode_item.st_mode &= ~__S_IFMT; fcb->inode_item.st_mode |= __S_IFLNK; fcb->inode_item.generation = fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux
if (fileref && fileref->dc) fileref->dc->type = fcb->type;
Status = truncate_file(fcb, 0, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08lx\n", Status);
if (target_alloc) ExFreePool(target.Buffer);
return Status; }
offset.QuadPart = 0; tlength = target.Length; Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, &tlength, false, true, true, false, false, rollback);
if (target_alloc) ExFreePool(target.Buffer);
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation; fcb->inode_item.sequence++;
if (!ccb || !ccb->user_set_change_time) fcb->inode_item.st_ctime = now;
if (!ccb || !ccb->user_set_write_time) fcb->inode_item.st_mtime = now;
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fcb->subvol->root_item.ctime = now;
fcb->inode_item_changed = true; mark_fcb_dirty(fcb);
if (fileref) mark_fileref_dirty(fileref);
return Status;}
NTSTATUS set_reparse_point2(fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, ccb* ccb, file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; ULONG tag;
if (fcb->type == BTRFS_TYPE_SYMLINK) { WARN("tried to set a reparse point on an existing symlink\n"); return STATUS_INVALID_PARAMETER; }
// FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
// FIXME - die if not file or directory
if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) { TRACE("directory not empty\n"); return STATUS_DIRECTORY_NOT_EMPTY; }
if (buflen < sizeof(ULONG)) { WARN("buffer was not long enough to hold tag\n"); return STATUS_INVALID_BUFFER_SIZE; }
Status = fFsRtlValidateReparsePointBuffer(buflen, rdb); if (!NT_SUCCESS(Status)) { ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status); return Status; }
tag = *(ULONG*)rdb;
if (tag == IO_REPARSE_TAG_MOUNT_POINT && fcb->type != BTRFS_TYPE_DIRECTORY) return STATUS_NOT_A_DIRECTORY;
if (fcb->type == BTRFS_TYPE_FILE && ((tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) || tag == IO_REPARSE_TAG_LX_SYMLINK)) { Status = set_symlink(Irp, fileref, fcb, ccb, rdb, buflen, rollback); fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; } else { LARGE_INTEGER offset, time; BTRFS_TIME now;
if (fcb->type == BTRFS_TYPE_DIRECTORY || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV) { // store as xattr ANSI_STRING buf;
buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG); if (!buf.Buffer) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } buf.Length = buf.MaximumLength = (uint16_t)buflen;
if (fcb->reparse_xattr.Buffer) ExFreePool(fcb->reparse_xattr.Buffer);
fcb->reparse_xattr = buf; RtlCopyMemory(buf.Buffer, rdb, buflen);
fcb->reparse_xattr_changed = true;
Status = STATUS_SUCCESS; } else { // otherwise, store as file data Status = truncate_file(fcb, 0, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08lx\n", Status); return Status; }
offset.QuadPart = 0;
Status = write_file2(fcb->Vcb, Irp, offset, rdb, &buflen, false, true, true, false, false, rollback); if (!NT_SUCCESS(Status)) { ERR("write_file2 returned %08lx\n", Status); return Status; } }
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation; fcb->inode_item.sequence++;
if (!ccb || !ccb->user_set_change_time) fcb->inode_item.st_ctime = now;
if (!ccb || !ccb->user_set_write_time) fcb->inode_item.st_mtime = now;
fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; fcb->atts_changed = true;
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fcb->subvol->root_item.ctime = now;
fcb->inode_item_changed = true; mark_fcb_dirty(fcb); }
return STATUS_SUCCESS;}
NTSTATUS set_reparse_point(PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; void* buffer = Irp->AssociatedIrp.SystemBuffer; REPARSE_DATA_BUFFER* rdb = buffer; DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength; NTSTATUS Status = STATUS_SUCCESS; fcb* fcb; ccb* ccb; file_ref* fileref; LIST_ENTRY rollback;
TRACE("(%p)\n", Irp);
InitializeListHead(&rollback);
if (!FileObject) { ERR("FileObject was NULL\n"); return STATUS_INVALID_PARAMETER; }
// IFSTest insists on this, for some reason... if (Irp->UserBuffer) return STATUS_INVALID_PARAMETER;
fcb = FileObject->FsContext; ccb = FileObject->FsContext2;
if (!ccb) { ERR("ccb was NULL\n"); return STATUS_INVALID_PARAMETER; }
if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA))) { WARN("insufficient privileges\n"); return STATUS_ACCESS_DENIED; }
fileref = ccb->fileref;
if (!fileref) { ERR("fileref was NULL\n"); return STATUS_INVALID_PARAMETER; }
if (fcb->ads) { fileref = fileref->parent; fcb = fileref->fcb; }
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true); ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
Status = set_reparse_point2(fcb, rdb, buflen, ccb, fileref, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("set_reparse_point2 returned %08lx\n", Status); goto end; }
queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
end: if (NT_SUCCESS(Status)) clear_rollback(&rollback); else do_rollback(fcb->Vcb, &rollback);
ExReleaseResourceLite(fcb->Header.Resource); ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;}
NTSTATUS delete_reparse_point(PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; REPARSE_DATA_BUFFER* rdb = Irp->AssociatedIrp.SystemBuffer; DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength; NTSTATUS Status; fcb* fcb; ccb* ccb; file_ref* fileref; LIST_ENTRY rollback;
TRACE("(%p)\n", Irp);
InitializeListHead(&rollback);
if (!FileObject) { ERR("FileObject was NULL\n"); return STATUS_INVALID_PARAMETER; }
fcb = FileObject->FsContext;
if (!fcb) { ERR("fcb was NULL\n"); return STATUS_INVALID_PARAMETER; }
ccb = FileObject->FsContext2;
if (!ccb) { ERR("ccb was NULL\n"); return STATUS_INVALID_PARAMETER; }
if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { WARN("insufficient privileges\n"); return STATUS_ACCESS_DENIED; }
fileref = ccb->fileref;
if (!fileref) { ERR("fileref was NULL\n"); return STATUS_INVALID_PARAMETER; }
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true); ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) { ERR("buffer was too short\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
if (rdb->ReparseDataLength > 0) { WARN("rdb->ReparseDataLength was not zero\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
if (fcb->ads) { WARN("tried to delete reparse point on ADS\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
if (fcb->type == BTRFS_TYPE_SYMLINK) { LARGE_INTEGER time; BTRFS_TIME now;
if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) { WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n"); Status = STATUS_INVALID_PARAMETER; goto end; }
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
fileref->fcb->type = BTRFS_TYPE_FILE; fileref->fcb->inode_item.st_mode &= ~__S_IFLNK; fileref->fcb->inode_item.st_mode |= __S_IFREG; fileref->fcb->inode_item.generation = fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation; fileref->fcb->inode_item.sequence++;
if (!ccb->user_set_change_time) fileref->fcb->inode_item.st_ctime = now;
if (!ccb->user_set_write_time) fileref->fcb->inode_item.st_mtime = now;
fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
if (fileref->dc) fileref->dc->type = fileref->fcb->type;
mark_fileref_dirty(fileref);
fileref->fcb->inode_item_changed = true; mark_fcb_dirty(fileref->fcb);
fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fileref->fcb->subvol->root_item.ctime = now; } else if (fcb->type == BTRFS_TYPE_FILE) { LARGE_INTEGER time; BTRFS_TIME now;
// FIXME - do we need to check that the reparse tags match?
Status = truncate_file(fcb, 0, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08lx\n", Status); goto end; }
fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; fcb->atts_changed = true;
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation; fcb->inode_item.sequence++;
if (!ccb->user_set_change_time) fcb->inode_item.st_ctime = now;
if (!ccb->user_set_write_time) fcb->inode_item.st_mtime = now;
fcb->inode_item_changed = true; mark_fcb_dirty(fcb);
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fcb->subvol->root_item.ctime = now; } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { LARGE_INTEGER time; BTRFS_TIME now;
// FIXME - do we need to check that the reparse tags match?
fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; fcb->atts_changed = true;
if (fcb->reparse_xattr.Buffer) { ExFreePool(fcb->reparse_xattr.Buffer); fcb->reparse_xattr.Buffer = NULL; }
fcb->reparse_xattr_changed = true;
KeQuerySystemTime(&time); win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation; fcb->inode_item.sequence++;
if (!ccb->user_set_change_time) fcb->inode_item.st_ctime = now;
if (!ccb->user_set_write_time) fcb->inode_item.st_mtime = now;
fcb->inode_item_changed = true; mark_fcb_dirty(fcb);
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fcb->subvol->root_item.ctime = now; } else { ERR("unsupported file type %u\n", fcb->type); Status = STATUS_INVALID_PARAMETER; goto end; }
Status = STATUS_SUCCESS;
queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
end: if (NT_SUCCESS(Status)) clear_rollback(&rollback); else do_rollback(fcb->Vcb, &rollback);
ExReleaseResourceLite(fcb->Header.Resource); ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;}