Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

864 lines
25 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
fsutil.c
Abstract:
Implements interface to underlying filesystem
Author:
Ahmed Mohamed (ahmedm) 1-Feb-2000
Revision History:
--*/
#include <nt.h>
#include <ntdef.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <ntddvol.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
#include "fs.h"
#include "fsp.h"
#include "fsutil.h"
#include <strsafe.h>
#define XFS_ENUM_FIRST 0x1
#define XFS_ENUM_LAST 0x2
#define XFS_ENUM_DIR 0x4
typedef NTSTATUS (*PXFS_ENUM_CALLBACK)(PVOID,HANDLE,PFILE_DIRECTORY_INFORMATION);
NTSTATUS
xFsCreate(HANDLE *fd, HANDLE root, LPWSTR buf, int n, UINT32 flag,
UINT32 attrib, UINT32 share, UINT32 *disp, UINT32 access,
PVOID eabuf, int easz)
{
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
NTSTATUS status;
IO_STATUS_BLOCK iostatus;
n = n * sizeof(WCHAR);
cwspath.Buffer = buf;
cwspath.Length = (USHORT) n;
cwspath.MaximumLength = (USHORT) n;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
// if write access is enabled, we turn on write-through bit
if (access & FILE_WRITE_DATA) {
flag |= FILE_WRITE_THROUGH;
}
*fd = INVALID_HANDLE_VALUE;
status = NtCreateFile(fd,
SYNCHRONIZE |
access,
&objattrs, &iostatus,
0,
attrib,
share,
*disp,
FILE_SYNCHRONOUS_IO_ALERT |
flag,
eabuf,
easz);
*disp = (UINT32) iostatus.Information;
if (NT_SUCCESS(status)) {
status = STATUS_SUCCESS;
}
else {
*fd = INVALID_HANDLE_VALUE;
}
return status;
}
NTSTATUS
xFsOpen(HANDLE *fd, HANDLE root, LPWSTR buf, int n, UINT32 access,
UINT32 share, UINT32 flags)
{
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
IO_STATUS_BLOCK iostatus;
n = n * sizeof(WCHAR);
cwspath.Buffer = buf;
cwspath.Length = (USHORT) n;
cwspath.MaximumLength = (USHORT) n;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
*fd = INVALID_HANDLE_VALUE;
return NtOpenFile(fd,
SYNCHRONIZE |
access,
&objattrs,
&iostatus,
share,
flags | FILE_SYNCHRONOUS_IO_ALERT);
}
NTSTATUS
xFsOpenEx(HANDLE *fd, HANDLE root, LPWSTR buf, int n, UINT32 access,
UINT32 share, UINT32 flags)
{
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
IO_STATUS_BLOCK iostatus;
n = n * sizeof(WCHAR);
cwspath.Buffer = buf;
cwspath.Length = (USHORT) n;
cwspath.MaximumLength = (USHORT) n;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
*fd = INVALID_HANDLE_VALUE;
return NtOpenFile(fd,
access,
&objattrs,
&iostatus,
share,
flags);
}
NTSTATUS
xFsDelete(HANDLE root, LPWSTR buf, int n)
{
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
n = n * sizeof(WCHAR);
cwspath.Buffer = buf;
cwspath.Length = (USHORT) n;
cwspath.MaximumLength = (USHORT) n;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
return NtDeleteFile(&objattrs);
}
NTSTATUS
xFsQueryObjectId(HANDLE fd, PVOID id)
{
NTSTATUS status;
IO_STATUS_BLOCK iostatus;
fs_ea_t x;
fs_ea_name_t name;
FsInitEa(&x);
FsInitEaName(&name);
status = NtQueryEaFile(fd, &iostatus,
(PVOID) &x, sizeof(x),
TRUE, (PVOID) &name, sizeof(name),
NULL, TRUE);
if (status == STATUS_SUCCESS) {
fs_id_t *fid;
if (iostatus.Information > sizeof(x.hdr)) {
FsInitEaFid(&x, fid);
memcpy(id, fid, sizeof(fs_id_t));
} else {
memset(id, 0, sizeof(fs_id_t));
}
} else {
FsLog(("QueryEa failed %x\n", status));
}
return status;
}
NTSTATUS
xFsRename(HANDLE fh, HANDLE root, WCHAR *dname, int n)
{
NTSTATUS status;
IO_STATUS_BLOCK iostatus;
struct {
FILE_RENAME_INFORMATION x;
WCHAR buf[MAXPATH];
}info;
info.x.ReplaceIfExists = TRUE;
info.x.RootDirectory = root;
ASSERT(n == (int)wcslen(dname));
// convert to unicode
StringCchCopyW(info.x.FileName, MAXPATH, dname);
info.x.FileNameLength = n * sizeof(WCHAR);
status = NtSetInformationFile(fh, &iostatus, (PVOID) &info, sizeof(info),
FileRenameInformation);
return status;
}
NTSTATUS
xFsSetAttr(HANDLE fd, FILE_BASIC_INFORMATION *attr)
{
IO_STATUS_BLOCK iostatus;
return NtSetInformationFile(fd, &iostatus,
(PVOID) attr, sizeof(*attr),
FileBasicInformation);
}
NTSTATUS
xFsQueryAttr(HANDLE fd, FILE_NETWORK_OPEN_INFORMATION *attr)
{
IO_STATUS_BLOCK iostatus;
return NtQueryInformationFile(fd, &iostatus,
(PVOID)attr, sizeof(*attr),
FileNetworkOpenInformation);
}
NTSTATUS
xFsQueryAttrName(HANDLE root, LPWSTR buf, int n, FILE_NETWORK_OPEN_INFORMATION *attr)
{
NTSTATUS err;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
n = n * sizeof(WCHAR);
cwspath.Buffer = buf;
cwspath.Length = (USHORT) n;
cwspath.MaximumLength = (USHORT) n;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
err = NtQueryFullAttributesFile(&objattrs, attr);
return err;
}
NTSTATUS
xFsReadDir(HANDLE fd, PVOID buf, int *rlen, BOOLEAN flag)
{
NTSTATUS err;
IO_STATUS_BLOCK iostatus;
err = NtQueryDirectoryFile(fd, NULL, NULL, NULL, &iostatus,
(LPVOID) buf, *rlen >> 1,
FileDirectoryInformation, FALSE, NULL,
flag);
*rlen = (int) iostatus.Information;
return err;
}
LPWSTR
xFsBuildRelativePath(VolInfo_t *vol, int nid, LPWSTR path)
{
LPWSTR share, s=NULL;
// Since the shares were opened using ipaddress, skip over the node name part.
// share = wcschr(vol->DiskList[nid], L'\\');
// share = wcschr(vol->DiskList[nid], L'\\');
// FsLog(("xFsBuildRelativePath(%ws) root:%ws\n", path, vol->Root));
share = vol->Root;
if (share != NULL) {
s = wcsstr(path, share);
if (s != NULL) {
s += (wcslen(share)+1);
s = wcsstr(s, share);
if (s != NULL) {
s += (wcslen(share) + 1);
}
}
}
// FsLog(("xFsBuildRelativePath() returns %ws\n", s));
return s;
}
NTSTATUS
_FsGetHandleById(HANDLE root, fs_id_t *id, UINT32 access, HANDLE *fhdl)
{
NTSTATUS err, status;
int sz;
BOOLEAN flag = TRUE;
IO_STATUS_BLOCK ios;
PVOID buf;
DWORD bufsize;
bufsize = PAGESIZE;
buf = (PVOID) VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE);
if (buf == NULL) {
return STATUS_NO_MEMORY;
}
status = STATUS_OBJECT_PATH_NOT_FOUND;
while (TRUE) {
PFILE_DIRECTORY_INFORMATION p;
err = NtQueryDirectoryFile(root, NULL, NULL, NULL, &ios,
(LPVOID) buf, bufsize,
FileDirectoryInformation, FALSE, NULL,
flag);
sz = (int) ios.Information;
if (err != STATUS_SUCCESS) {
// FsLogError(("ReadDir failed %x flags %d\n", err, flag));
break;
}
flag = FALSE;
p = (PFILE_DIRECTORY_INFORMATION) buf;
while (TRUE) {
// open each entry and get its object id
HANDLE fd;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
cwspath.Buffer = p->FileName;
cwspath.Length = (USHORT) p->FileNameLength;
cwspath.MaximumLength = (USHORT) p->FileNameLength;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
// todo: what if the file is nonsharable @ this time?
err = NtOpenFile(&fd,
SYNCHRONIZE |
((p->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE) : (access)),
&objattrs,
&ios,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
if (err == STATUS_SUCCESS) {
fs_id_t fid;
err = xFsQueryObjectId(fd, (PVOID)fid);
if (err == STATUS_SUCCESS) {
#ifdef DEBUG
wprintf(L"Compare file %wZ, %I64x:%I64x with %I64x:%I64x\n",
&cwspath, fid[0], fid[1], (*id)[0], (*id)[1]);
#endif
if (fid[0] == (*id)[0] && fid[1] == (*id)[1]) {
#ifdef DEBUG
wprintf(L"Found file %wZ, %I64x:%I64x\n",
&cwspath, fid[0], fid[1]);
#endif
status = STATUS_SUCCESS;
#if 0
if (access != FILE_GENERIC_READ) {
HANDLE nfd;
xFsClose(fd);
err = NtOpenFile(&nfd,
SYNCHRONIZE | access,
&objattrs,
&ios,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_ALERT);
if (err == STATUS_SUCCESS) {
fd = nfd;
} else {
fd = 0;
}
}
#endif
*fhdl = fd;
} else if (p->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
status = _FsGetHandleById(fd, id, access, fhdl);
xFsClose(fd);
fd = 0;
}
} else {
FsLog(("_GetHandleById: query '%wZ' err 0x%x\n", &cwspath, err));
}
if (status == STATUS_SUCCESS) {
goto done;
}
if (fd)
xFsClose(fd);
} else {
if (err == STATUS_SHARING_VIOLATION) {
status = err;
FsLog(("_GetHandleById: open '%wZ' err 0x%x\n", &cwspath, err));
break;
}
}
if (p->NextEntryOffset == 0)
break;
p = (PFILE_DIRECTORY_INFORMATION) (((PBYTE)p) + p->NextEntryOffset);
}
}
done:
VirtualFree(buf, 0, MEM_RELEASE);
return status;
}
NTSTATUS
xFsGetHandleById(HANDLE root, fs_id_t *id, UINT32 access, HANDLE *fhdl)
{
// open each entry and get its object id
HANDLE fd;
NTSTATUS err;
err = NtDuplicateObject(NtCurrentProcess(),
root,
NtCurrentProcess(),
&fd,
(ACCESS_MASK)0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (err != STATUS_SUCCESS) {
FsLogError(("Unable to duplicate root handle %x\n", err));
return err;
}
err = _FsGetHandleById(fd, id, access, fhdl);
xFsClose(fd);
return err;
}
DWORD
xFsGetHandlePath(HANDLE fd, WCHAR *path, int *pathlen)
{
NTSTATUS status;
IO_STATUS_BLOCK iostatus;
int orig_len=*pathlen;
struct {
FILE_NAME_INFORMATION x;
WCHAR buf[MAXPATH];
}info;
*path = L'\0';
*pathlen = 0;
status = NtQueryInformationFile(fd, &iostatus,
(LPVOID) &info, sizeof(info),
FileNameInformation);
if (status == STATUS_SUCCESS) {
int k = info.x.FileNameLength / sizeof(WCHAR);
info.x.FileName[k] = L'\0';
StringCbCopyW(path, orig_len, info.x.FileName);
*pathlen = k;
}
return status;
}
NTSTATUS
xFsGetPathById(HANDLE vfd, fs_id_t *id, WCHAR *name, int *name_len)
{
NTSTATUS err;
HANDLE fd;
err = xFsGetHandleById(vfd, id, FILE_READ_EA, &fd);
if (err == STATUS_SUCCESS) {
err = xFsGetHandlePath(fd, name, name_len);
xFsClose(fd);
}
return err;
}
NTSTATUS
_xFsEnumTree(HANDLE hdl, int mode, PXFS_ENUM_CALLBACK callback, PVOID callback_arg)
{
NTSTATUS err = STATUS_SUCCESS;
IO_STATUS_BLOCK ios;
BOOLEAN flag;
PVOID buf;
DWORD bufsize;
bufsize = PAGESIZE;
buf = (PVOID) VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE);
if (buf == NULL) {
return STATUS_NO_MEMORY;
}
flag = TRUE;
while (err == STATUS_SUCCESS) {
PFILE_DIRECTORY_INFORMATION p;
p = (PFILE_DIRECTORY_INFORMATION) buf;
err = NtQueryDirectoryFile(hdl, NULL, NULL, NULL, &ios,
(LPVOID) buf, bufsize,
FileDirectoryInformation, FALSE, NULL, flag);
if (err != STATUS_SUCCESS) {
break;
}
flag = FALSE;
while (err == STATUS_SUCCESS) {
BOOLEAN skip = FALSE;
if (p->FileNameLength == 2 && p->FileName[0] == L'.' ||
(p->FileNameLength == 4 && p->FileName[0] == L'.' && p->FileName[1] == L'.'))
skip = TRUE;
// skip . and ..
if (skip == FALSE) {
// traverse before
if (mode & XFS_ENUM_FIRST) {
err = callback(callback_arg, hdl, p);
}
if ((mode & XFS_ENUM_DIR) && (p->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
HANDLE fd;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
cwspath.Buffer = p->FileName;
cwspath.Length = (USHORT) p->FileNameLength;
cwspath.MaximumLength = (USHORT) p->FileNameLength;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
hdl, NULL);
// todo: what if the dir is nonsharable @ this time?
err = NtOpenFile(&fd,
SYNCHRONIZE | FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
&objattrs,
&ios,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_ALERT);
if (err == STATUS_SUCCESS) {
err = _xFsEnumTree(fd, mode, callback, callback_arg);
NtClose(fd);
} else {
FsLog(("Open failed on traverse dir %S %x\n", p->FileName, err));
}
}
// traverse after
if (mode & XFS_ENUM_LAST) {
err = callback(callback_arg, hdl, p);
}
}
if (p->NextEntryOffset == 0)
break;
p = (PFILE_DIRECTORY_INFORMATION) (((PBYTE)p) + p->NextEntryOffset);
}
}
VirtualFree(buf, 0, MEM_RELEASE);
if (err == STATUS_NO_MORE_FILES)
err = STATUS_SUCCESS;
return err;
}
NTSTATUS
xFsRemove(PVOID arg, HANDLE root, PFILE_DIRECTORY_INFORMATION item)
{
NTSTATUS err;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
cwspath.Buffer = item->FileName;
cwspath.Length = (USHORT) item->FileNameLength;
cwspath.MaximumLength = (USHORT) item->FileNameLength;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
// if the file is marked read-only, we have to clear it first before we delete
if (item->FileAttributes & FILE_ATTRIBUTE_READONLY) {
// clear this bit in order to delete
HANDLE fd;
IO_STATUS_BLOCK iostatus;
err = NtOpenFile(&fd,
SYNCHRONIZE | (STANDARD_RIGHTS_WRITE | FILE_WRITE_ATTRIBUTES),
&objattrs,
&iostatus,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_ALERT);
if (err == STATUS_SUCCESS) {
FILE_BASIC_INFORMATION attr;
memset((PVOID) &attr, 0, sizeof(attr));
attr.FileAttributes = item->FileAttributes & ~FILE_ATTRIBUTE_READONLY;
err = NtSetInformationFile(fd, &iostatus,
(PVOID) &attr, sizeof(attr),
FileBasicInformation);
xFsClose(fd);
}
}
err = NtDeleteFile(&objattrs);
FsLog(("xFsRemove: '%wZ' err 0x%x\n", &cwspath, err));
// The code below is an infinite loop.
// while (err == STATUS_SHARING_VIOLATION) {
// Sleep(5*10000);
// }
return err;
}
NTSTATUS
xFsCopy(PVOID arg, HANDLE root, PFILE_DIRECTORY_INFORMATION item)
{
WCHAR *name = item->FileName;
int name_len = item->FileNameLength / sizeof(WCHAR);
NTSTATUS err;
err = xFsDupFile(root, (HANDLE) arg, name, name_len, TRUE);
return err;
}
// copy all files from mvfd to vfd.
NTSTATUS
xFsCopyTree(HANDLE mvfd, HANDLE vfd)
{
NTSTATUS err;
// first, remove all files in vfd
FsLog(("CopyTree: remove first\n"));
err = _xFsEnumTree(vfd, XFS_ENUM_LAST|XFS_ENUM_DIR, xFsRemove, NULL);
// copy files
if (err == STATUS_SUCCESS) {
FsLog(("CopyTree: copy second\n"));
err = _xFsEnumTree(mvfd, XFS_ENUM_FIRST, xFsCopy, (PVOID) vfd);
}
FsLog(("CopyTree: exit %x\n", err));
return err;
}
// delete all files
NTSTATUS
xFsDeleteTree(HANDLE vfd)
{
// remove all files in vfd
return _xFsEnumTree(vfd, XFS_ENUM_LAST|XFS_ENUM_DIR, xFsRemove, NULL);
}
NTSTATUS
xFsTouch(PVOID arg, HANDLE root, PFILE_DIRECTORY_INFORMATION item)
{
NTSTATUS err=STATUS_SUCCESS;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING cwspath;
HANDLE fd;
IO_STATUS_BLOCK iostatus;
DWORD cnt = 0;
cwspath.Buffer = item->FileName;
cwspath.Length = (USHORT) item->FileNameLength;
cwspath.MaximumLength = (USHORT) item->FileNameLength;
InitializeObjectAttributes(&objattrs, &cwspath, OBJ_CASE_INSENSITIVE,
root, NULL);
do {
err = NtOpenFile(&fd,
SYNCHRONIZE | FILE_GENERIC_READ | FILE_GENERIC_WRITE,
&objattrs,
&iostatus,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_ALERT);
if (err == STATUS_SUCCESS) {
NtClose(fd);
} else {
FsLogError(("xFsTouch: open '%wZ' failed 0x%x\n", &cwspath, err));
if (err == STATUS_SHARING_VIOLATION || err == STATUS_DELETE_PENDING) {
Sleep(5*1000);
cnt++;
}
}
} while ((err == STATUS_SHARING_VIOLATION ||
err == STATUS_DELETE_PENDING) && cnt < 1000);
return err;
}
// touch each file
NTSTATUS
xFsTouchTree(HANDLE mvfd)
{
return _xFsEnumTree(mvfd, XFS_ENUM_LAST | XFS_ENUM_DIR, xFsTouch, NULL);
}
NTSTATUS
xFsDupFile(HANDLE mvfd, HANDLE vfd, WCHAR *name, int name_len, BOOLEAN flag)
{
NTSTATUS err;
HANDLE mfd, fd;
IO_STATUS_BLOCK ios;
fs_id_t *fs_id;
fs_ea_t xattr;
FILE_NETWORK_OPEN_INFORMATION attr;
FILE_BASIC_INFORMATION new_attr;
char buf[PAGESIZE];
// Create file on vfd with same name and attrib and extended attributes (object id)
// If we created a directory, we are done.
// Otherwise, copy all data from source file to new file
// Open master file
err = xFsOpenRD(&mfd, mvfd, name, name_len);
if (err != STATUS_SUCCESS) {
// We couldn't open source file. If file is locked we have to use the handle we
// already have. todo:
FsLog(("FsDupFile: unable to open source '%.*ls' err %x\n", name_len/sizeof(WCHAR), name, err));
return err;
}
// Query name on mvfd and obtain all attributes.
err = xFsQueryAttr(mfd, &attr);
if (err != STATUS_SUCCESS) {
FsLog(("FsDupFile: unable to query source '%.*ls' err %x\n", name_len/sizeof(WCHAR), name, err));
xFsClose(mfd);
return err;
}
// get objectid and set the ea stuff
FsInitEa(&xattr);
FsInitEaFid(&xattr, fs_id);
err = xFsQueryObjectId(mfd, (PVOID) fs_id);
if (err == STATUS_SUCCESS) {
UINT32 disp = FILE_CREATE;
err = xFsCreate(&fd, vfd, name, name_len,
(attr.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ?
FILE_DIRECTORY_FILE : 0),
attr.FileAttributes,
FILE_SHARE_READ,
&disp,
FILE_GENERIC_EXECUTE | FILE_GENERIC_WRITE,
(PVOID) &xattr,
sizeof(xattr));
if (err == STATUS_SUCCESS) {
assert(disp == FILE_CREATED);
// if file we need to copy data and set access flags
if (!(attr.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
int buflen = sizeof(buf);
LARGE_INTEGER off;
off.LowPart = 0;
off.HighPart = 0;
while (1) {
err = NtReadFile(mfd, NULL, NULL, NULL, &ios, buf, buflen, &off, NULL);
if (err == STATUS_PENDING) {
EventWait(mfd);
err = ios.Status;
}
if (err != STATUS_SUCCESS) {
break;
}
err = NtWriteFile(fd, NULL, NULL, NULL, &ios, buf, (ULONG)ios.Information,
&off, NULL);
if (err == STATUS_PENDING) {
EventWait(fd);
err = ios.Status;
}
if (err != STATUS_SUCCESS) {
break;
}
off.LowPart += (ULONG) ios.Information;
}
// adjust return code
if (err == STATUS_END_OF_FILE) {
err = STATUS_SUCCESS;
}
FsLog(("FsDupFile: '%.*ls' err %x\n", name_len/sizeof(WCHAR), name, err));
} else if (flag == TRUE) {
// call enum again
err = _xFsEnumTree(mfd, XFS_ENUM_FIRST, xFsCopy, (PVOID) fd);
}
// set new file attributes
new_attr.CreationTime = attr.CreationTime;
new_attr.LastAccessTime = attr.LastAccessTime;
new_attr.LastWriteTime = attr.LastWriteTime;
new_attr.ChangeTime = attr.ChangeTime;
new_attr.FileAttributes = attr.FileAttributes;
err = xFsSetAttr(fd, &new_attr);
// close new file
xFsClose(fd);
}
if (err != STATUS_SUCCESS)
FsLog(("FsDupFile: unable to open/reset attr '%S' err %x\n", name, err));
} else {
FsLog(("FsDupFile: unable to query sid '%S' err %x, skip!\n", name, err));
err = STATUS_SUCCESS;
}
// close master file
xFsClose(mfd);
return err;
}