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