windows-nt/Source/XPSP1/NT/ds/netapi/svcimgs/ntrepl/util/util.c
2020-09-26 16:20:57 +08:00

8432 lines
227 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
util.c
Abstract:
This module contains support routines for the NT File Replication Service.
Author:
David A. Orbits (davidor) 25-Mar-1997
Environment:
User Mode Service
Revision History:
--*/
#include <ntreppch.h>
#pragma hdrstop
#include <frs.h>
#include <tablefcn.h>
#include <ntfrsapi.h>
#include <info.h>
#include <sddl.h>
#ifdef SECURITY_WIN32
#include <security.h>
#else
#define SECURITY_WIN32
#include <security.h>
#undef SECURITY_WIN32
#endif
#include "stdarg.h"
#include <accctrl.h>
#include <aclapi.h>
BOOL
JrnlIsChangeOrderInReplica(
IN PCHANGE_ORDER_ENTRY ChangeOrder,
IN PLONGLONG DirFileID
);
LPTSTR
FrsSupInitPath(
OUT LPTSTR OutPath,
IN LPTSTR InPath,
IN ULONG MaxOutPath
)
/*++
Routine Description:
Initialize a directory path string. Add a backslash as needed and
return a pointer to the start of the file part of the output string.
Return NULL if the Output path string is too small.
If InPath is NULL, OutPath is set to NULL and no slash.
Arguments:
OutPath - The output string with the initialized path.
InPath - The supplied input path.
MaxOutPath - The maximum number of charaters that fit in OutPath.
Return Value:
Pointer to the start of the filename part of the output string.
NULL if output string is too small.
--*/
//
// Capture the directory path and add a backslash if necc.
//
{
#undef DEBSUB
#define DEBSUB "FrsSupInitPath:"
ULONG Length;
Length = wcslen(InPath);
if (Length > MaxOutPath) {
return NULL;
}
wcscpy(OutPath, InPath);
if (Length > 0) {
if (OutPath[Length - 1] != COLON_CHAR &&
OutPath[Length - 1] != BACKSLASH_CHAR) {
wcscat(OutPath, L"\\");
Length += 1;
}
}
return &OutPath[Length];
}
LONG
FrsIsParent(
IN PWCHAR Directory,
IN PWCHAR Path
)
/*++
Routine Description:
Is Path a child of Directory or is the Directory a child of the path.
In other words, is the directory represented by Path beneath
the directory hierarchy represented by Directory (or vice-versa).
E.g., c:\a\b is a child of c:\a.
In the case of an exact match, Path is considered a child of
Directory. This routine can be easily spoofed; a better check
using FIDs and volume IDs should be implemented.
Arguments:
Directory
Path
Return Value:
-1 = Path is a child of Directory or Path is the same as Directory
0 = No relationship
1 = Directory is a child of Path
--*/
{
#undef DEBSUB
#define DEBSUB "FrsIsParent:"
PWCHAR D;
PWCHAR P;
LONG Result = 0;
PWCHAR IndexPtrDir = NULL;
PWCHAR IndexPtrPath = NULL;
DWORD Colon = 0;
DWORD CloseBrace = 0;
DWORD WStatus;
HANDLE Handle = INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Iosb;
PFILE_FS_VOLUME_INFORMATION VolumeInfoDir = NULL;
PFILE_FS_VOLUME_INFORMATION VolumeInfoPath = NULL;
DWORD VolumeInfoLength;
NTSTATUS NtStatus;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING FileName;
ULONG FileAttributes;
ULONG CreateDisposition;
ULONG ShareMode;
//
// Note: This is easily spoofed into giving false negatives.
// Need to improve it to uses FIDs and voluem IDs
//
//
// Defensive; NULL strings or empty strings can't be children/parents
//
if (!Directory || !Path || !*Directory || !*Path) {
return Result;
}
//
// If both the paths are on different volumes then they can not overlap.
//
//
// Open the target symlink. If this is a dos type path name then
// convert it to NtPathName or else use it as it is.
//
if (wcscspn(Directory, L":") == 1) {
WStatus = FrsOpenSourceFileW(&Handle,
Directory,
GENERIC_READ,
FILE_OPEN_FOR_BACKUP_INTENT);
CLEANUP1_WS(4, "++ Could not open %ws; ", Directory, WStatus, RETURN);
} else {
//
// The path already in Nt style. Use it as it is.
//
FileName.Buffer = Directory;
FileName.Length = (USHORT)(wcslen(Directory) * sizeof(WCHAR));
FileName.MaximumLength = (USHORT)(wcslen(Directory) * sizeof(WCHAR));
InitializeObjectAttributes(&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
CreateDisposition = FILE_OPEN; // Open existing file
ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
FileAttributes = FILE_ATTRIBUTE_NORMAL;
NtStatus = NtCreateFile(&Handle,
GENERIC_READ,
&Obja,
&Iosb,
NULL, // Initial allocation size
FileAttributes,
ShareMode,
CreateDisposition,
FILE_OPEN_FOR_BACKUP_INTENT,
NULL, 0);
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(4, "++ Could not open %ws;", Directory, WStatus, RETURN);
}
//
// Get the volume information.
//
VolumeInfoLength = sizeof(FILE_FS_VOLUME_INFORMATION) +
MAXIMUM_VOLUME_LABEL_LENGTH;
VolumeInfoDir = FrsAlloc(VolumeInfoLength);
NtStatus = NtQueryVolumeInformationFile(Handle,
&Iosb,
VolumeInfoDir,
VolumeInfoLength,
FileFsVolumeInformation);
CloseHandle(Handle);
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(4,"ERROR - Getting NtQueryVolumeInformationFile for %ws\n", Directory, WStatus, RETURN);
// Open the target symlink. If this is a dos type path name then
// convert it to NtPathName or else use it as it is.
//
if (wcscspn(Path, L":") == 1) {
WStatus = FrsOpenSourceFileW(&Handle,
Path,
GENERIC_READ,
FILE_OPEN_FOR_BACKUP_INTENT);
CLEANUP1_WS(4, "++ Could not open %ws; ", Path, WStatus, RETURN);
} else {
//
// The path already in Nt style. Use it as it is.
//
FileName.Buffer = Path;
FileName.Length = (USHORT)(wcslen(Path) * sizeof(WCHAR));
FileName.MaximumLength = (USHORT)(wcslen(Path) * sizeof(WCHAR));
InitializeObjectAttributes(&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
CreateDisposition = FILE_OPEN; // Open existing file
ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
FileAttributes = FILE_ATTRIBUTE_NORMAL;
NtStatus = NtCreateFile(&Handle,
GENERIC_READ,
&Obja,
&Iosb,
NULL, // Initial allocation size
FileAttributes,
ShareMode,
CreateDisposition,
FILE_OPEN_FOR_BACKUP_INTENT,
NULL, 0);
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(4, "++ Could not open %ws;", Path, WStatus, RETURN);
}
//
// Get the volume information.
//
VolumeInfoLength = sizeof(FILE_FS_VOLUME_INFORMATION) +
MAXIMUM_VOLUME_LABEL_LENGTH;
VolumeInfoPath = FrsAlloc(VolumeInfoLength);
NtStatus = NtQueryVolumeInformationFile(Handle,
&Iosb,
VolumeInfoPath,
VolumeInfoLength,
FileFsVolumeInformation);
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(4,"ERROR - Getting NtQueryVolumeInformationFile for %ws\n", Path, WStatus, RETURN);
if (VolumeInfoDir->VolumeSerialNumber != VolumeInfoPath->VolumeSerialNumber) {
goto RETURN;
}
//
// Find the colon. Every path has to either have a colon followed by a '\'
// or it should be of the form. "\??\Volume{60430005-ab47-11d3-8973-806d6172696f}\"
//
Colon = wcscspn(Directory, L":");
if (Colon == wcslen(Directory)) {
//
// Path does not have a colon. It can be of the form
// "\??\Volume{60430005-ab47-11d3-8973-806d6172696f}\"
//
CloseBrace = wcscspn(Directory, L"}");
if (Directory[CloseBrace] != L'}' ||
Directory[CloseBrace + 1] != L'\\') {
Result = 0;
goto RETURN;
}
//
// Copy the path up to 1 past the closing brace as it is. It could be \??\Volume...
// or \\.\Volume... or \\?\Volume.. or some other complex form.
// Start looking for reparse points past the closing brace.
//
IndexPtrDir = &Directory[CloseBrace + 1];
} else {
if (Directory[Colon] != L':' ||
Directory[Colon + 1] != L'\\') {
Result = 0;
goto RETURN;
}
//
// Copy the path up to 1 past the colon as it is. It could be d:\
// or \\.\d:\ or \??\d:\ or some other complex form.
// Start looking for reparse points past the colon.
//
IndexPtrDir = &Directory[Colon + 1];
}
//
// Find the colon. Every path has to either have a colon followed by a '\'
// or it should be of the form. "\??\Volume{60430005-ab47-11d3-8973-806d6172696f}\"
//
Colon = wcscspn(Path, L":");
if (Colon == wcslen(Path)) {
//
// Path does not have a colon. It can be of the form
// "\??\Volume{60430005-ab47-11d3-8973-806d6172696f}\"
//
CloseBrace = wcscspn(Path, L"}");
if (Path[CloseBrace] != L'}' ||
Path[CloseBrace + 1] != L'\\') {
Result = 0;
goto RETURN;
}
//
// Copy the path up to 1 past the closing brace as it is. It could be \??\Volume...
// or \\.\Volume... or \\?\Volume.. or some other complex form.
// Start looking for reparse points past the closing brace.
//
IndexPtrPath = &Path[CloseBrace + 1];
} else {
if (Path[Colon] != L':' ||
Path[Colon + 1] != L'\\') {
Result = 0;
goto RETURN;
}
//
// Copy the path up to 1 past the colon as it is. It could be d:\
// or \\.\d:\ or \??\d:\ or some other complex form.
// Start looking for reparse points past the colon.
//
IndexPtrPath = &Path[Colon + 1];
}
//
// Break at the first non-matching wchar (collapse dup \s)
//
for (D = IndexPtrDir, P = IndexPtrPath; *P && *D; ++P, ++D) {
//
// Skip dup \s
//
while (*P == L'\\' && *(P + 1) == L'\\') {
++P;
}
while (*D == L'\\' && *(D + 1) == L'\\') {
++D;
}
if (towlower(*P) != towlower(*D)) {
break;
}
}
//
// Exact match; consider Path a child of Directory
//
if (!*D && !*P) {
Result = -1;
goto RETURN;
}
//
// Collapse dup \s
//
while (*P == L'\\' && *(P + 1) == L'\\') {
++P;
}
while (*D == L'\\' && *(D + 1) == L'\\') {
++D;
}
//
// Path is a child of Directory
//
if ((!*D || (*D == L'\\' && !*(D + 1))) &&
(!*P || *P == L'\\' || (P != Path && *(P - 1) == L'\\'))) {
Result = -1;
goto RETURN;
}
//
// Directory is a child of Path
//
if ((!*P || (*P == L'\\' && !*(P + 1))) &&
(!*D || *D == L'\\' || (D != Directory && *(D - 1) == L'\\'))) {
Result = 1;
goto RETURN;
}
//
// no relationship
//
RETURN:
FRS_CLOSE(Handle);
FrsFree(VolumeInfoDir);
FrsFree(VolumeInfoPath);
return Result;
}
#if 0
ULONG FrsSupMakeFullFileName(
IN PREPLICA Replica,
IN PTCHAR RelativeName,
OUT PTCHAR FullName,
IN ULONG MaxLength
)
{
/*++
Routine Description:
Build a full file name for a given data source with the supplied
RelativeName.
Arguments:
Replica - The replica tree to provide the root path.
RelativeName - The relative file name from the root of the data source.
FullName - The returned full path name of the file.
MaxLength - The maximum number of characters that fit in FullName.
Return Value:
Status - ERROR_BAD_PATHNAME if the name is too long.
--*/
#undef DEBSUB
#define DEBSUB "FrsSupMakeFullFileName:"
ULONG Length, TotalLength;
PTCHAR pFilePart;
PCONFIG_TABLE_RECORD ConfigRecord;
ConfigRecord = (PCONFIG_TABLE_RECORD) (Replica->ConfigTable.pDataRecord);
//
// Init the file name string with the DataSource root path.
//
pFilePart = FrsSupInitPath( FullName, ConfigRecord->FSRootPath, MaxLength);
if (pFilePart == NULL) {
return ERROR_BAD_PATHNAME;
}
Length = wcslen(RelativeName);
TotalLength = Length + wcslen(FullName);
if (TotalLength > MaxLength) {
return ERROR_BAD_PATHNAME;
}
//
// Append the relative file name to the end of the base path.
//
wcscpy(pFilePart, RelativeName);
return ERROR_SUCCESS;
}
#endif 0
ULONG
FrsForceDeleteFile(
PTCHAR DestName
)
/*++
Routine Description:
Support routine to delete File System Files. Returns success if file
is not there or if it was there and was deleted.
Arguments:
DestName - The fully qualified file name.
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsForceDeleteFile:"
ULONG WStatus = ERROR_SUCCESS;
ULONG FileAttributes;
if (!DeleteFile(DestName)) {
WStatus = GetLastError();
if ((WStatus == ERROR_FILE_NOT_FOUND) ||
(WStatus == ERROR_PATH_NOT_FOUND)) {
return ERROR_SUCCESS;
}
FileAttributes = GetFileAttributes(DestName);
if ((FileAttributes != 0xFFFFFFFF) &&
(FileAttributes & NOREPL_ATTRIBUTES)) {
//
// Reset file attributes to allow delete.
//
SetFileAttributes(DestName,
FILE_ATTRIBUTE_NORMAL |
(FileAttributes & ~NOREPL_ATTRIBUTES));
}
if (!DeleteFile(DestName)) {
WStatus = GetLastError();
DPRINT1_WS(4, "++ WARN - cannot delete %ws;", DestName, WStatus);
}
}
return WStatus;
}
HANDLE
FrsCreateEvent(
IN BOOL ManualReset,
IN BOOL InitialState
)
/*++
Routine Description:
Support routine to create an event.
Arguments:
ManualReset - TRUE if ResetEvent is required
InitialState - TRUE if signaled
Return Value:
Address of the created event handle.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCreateEvent:"
HANDLE Handle;
Handle = CreateEvent(NULL, ManualReset, InitialState, NULL);
if (!HANDLE_IS_VALID(Handle)) {
RaiseException(ERROR_INVALID_HANDLE, 0, 0, NULL);
}
return Handle;
}
HANDLE
FrsCreateWaitableTimer(
IN BOOL ManualReset
)
/*++
Routine Description:
Support routine to create a waitable timer.
Arguments:
ManualReset - TRUE if not synchronization timer
Return Value:
Address of the created waitable timer handle.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCreateWaitableTimer:"
HANDLE Handle;
Handle = CreateWaitableTimer(NULL, ManualReset, NULL);
if (!HANDLE_IS_VALID(Handle)) {
RaiseException(ERROR_INVALID_HANDLE, 0, 0, NULL);
}
return Handle;
}
ULONG
FrsUuidCreate(
OUT GUID *Guid
)
/*++
Routine Description:
Frs wrapper on UuidCreate() to generate an exception if we fail
to get correctly formed Guid. In particular UuidCreate can have
problems getting the network address.
RPC_S_OK - The operation completed successfully.
RPC_S_UUID_NO_ADDRESS - We were unable to obtain the ethernet or
token ring address for this machine.
RPC_S_UUID_LOCAL_ONLY - On NT & Chicago if we can't get a
network address. This is a warning to the user, the
UUID is still valid, it just may not be unique on other machines.
RPC_S_OUT_OF_MEMORY - Returned as needed.
Arguments:
Guid - Pointer to returned guid.
Return Value:
FrsStatus
--*/
{
#undef DEBSUB
#define DEBSUB "FrsUuidCreate:"
DWORD MsgBufSize;
WCHAR MsgBuf[MAX_PATH + 1];
RPC_STATUS RpcStatusFromUuidCreate;
RpcStatusFromUuidCreate = UuidCreate(Guid);
if (RpcStatusFromUuidCreate == RPC_S_OK) {
return FrsErrorSuccess;
}
DPRINT_WS(0, "ERROR - Failed to get GUID.", RpcStatusFromUuidCreate);
if (RpcStatusFromUuidCreate == RPC_S_UUID_NO_ADDRESS) {
DPRINT(0, "++ UuidCreate() returned RPC_S_UUID_NO_ADDRESS.\n");
} else
if (RpcStatusFromUuidCreate == RPC_S_UUID_LOCAL_ONLY) {
DPRINT(0, "++ UuidCreate() returned RPC_S_UUID_LOCAL_ONLY.\n");
} else
if (RpcStatusFromUuidCreate == RPC_S_OUT_OF_MEMORY) {
DPRINT(0, "++ UuidCreate() returned RPC_S_OUT_OF_MEMORY.\n");
}
//
// Format the error code
//
MsgBufSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
RpcStatusFromUuidCreate,
0,
MsgBuf,
MAX_PATH + 1,
NULL);
//
// No message; use the status code
//
if (!MsgBufSize) {
swprintf(MsgBuf, L"%d (0x%08x)", RpcStatusFromUuidCreate, RpcStatusFromUuidCreate);
}
//
// This is very bad. Any member that can't generate proper GUIDs is
// busted.
//
// Shutdown with an event log message
//
EPRINT2(EVENT_FRS_CANNOT_CREATE_UUID, ComputerName, MsgBuf);
//
// EXIT BECAUSE THE CALLERS CANNOT HANDLE THIS ERROR.
//
DPRINT(0, ":S: NTFRS IS EXITING W/O CLEANUP! SERVICE CONTROLLER RESTART EXPECTED.\n");
DEBUG_FLUSH();
exit(RpcStatusFromUuidCreate);
return FrsErrorInvalidGuid;
}
LONG
FrsGuidCompare (
IN GUID *Guid1,
IN GUID *Guid2
)
/*++
Routine Description:
Do a simple, straight unsigned compare of two GUIDs.
UuidCompare doesn't do this. I don't know what kind of comparison it
does.
Arguments:
Guid1 - The first Guid
Guid2 - The second Guid.
Return Value:
Result: -1 if Guid1 < Guid2
0 if Guid1 = Guid2
+1 if Guid1 > Guid2
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGuidCompare:"
PULONG p1 = (PULONG) Guid1;
PULONG p2 = (PULONG) Guid2;
p1 += 4;
p2 += 4;
while (p1 != (PVOID) Guid1) {
p1 -= 1;
p2 -= 1;
if (*p1 > *p2) {
return 1;
}
if (*p1 < *p2) {
return -1;
}
}
return 0;
}
VOID
FrsNowAsFileTime(
IN PLONGLONG Now
)
/*++
Routine Description:
Return the current time as a filetime in longlong format.
Arguments:
Now - address of longlong to receive current time.
Return Value:
Fill in Now with current file time
--*/
{
#undef DEBSUB
#define DEBSUB "FrsNowAsFileTime:"
FILETIME FileTime;
GetSystemTimeAsFileTime(&FileTime);
COPY_TIME(Now, &FileTime);
}
char *Days[] =
{
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
char *Months[] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
VOID
FileTimeToString(
IN FILETIME *FileTime,
OUT PCHAR Buffer
)
/*++
Routine Description:
Convert a FileTime (UTC time) to an ANSI date/time string in the
local time zone.
Arguments:
Time - ptr to a FILETIME
Str - a string of at least TIME_STRING_LENGTH bytes to receive the time.
Return Value:
None
--*/
{
#undef DEBSUB
#define DEBSUB "FileTimeToString:"
FILETIME LocalFileTime;
SYSTEMTIME SystemTime;
Buffer[0] = '\0';
if (FileTime->dwHighDateTime != 0 || FileTime->dwLowDateTime != 0)
{
if (!FileTimeToLocalFileTime(FileTime, &LocalFileTime) ||
!FileTimeToSystemTime(&LocalFileTime, &SystemTime))
{
strcpy(Buffer, "Time???");
return;
}
sprintf(
Buffer,
"%s %s %2d, %4d %02d:%02d:%02d",
Days[SystemTime.wDayOfWeek],
Months[SystemTime.wMonth - 1],
SystemTime.wDay,
SystemTime.wYear,
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond);
}
return;
}
VOID
FileTimeToStringClockTime(
IN FILETIME *FileTime,
OUT PCHAR Buffer
)
/*++
Routine Description:
Convert a FileTime (UTC time) to an ANSI time string in the
local time zone.
Arguments:
Time - ptr to a FILETIME
Str - a string to hold hh:mm:ss\0. (9 bytes min.)
Return Value:
None
--*/
{
#undef DEBSUB
#define DEBSUB "FileTimeToStringClockTime:"
FILETIME LocalFileTime;
SYSTEMTIME SystemTime;
Buffer[0] = '\0';
if (FileTime->dwHighDateTime == 0 && FileTime->dwLowDateTime == 0) {
strcpy(Buffer, "??:??:??");
return;
}
if (!FileTimeToLocalFileTime(FileTime, &LocalFileTime) ||
!FileTimeToSystemTime(&LocalFileTime, &SystemTime)) {
strcpy(Buffer, "??:??:??");
return;
}
_snprintf(Buffer, 9, "%02d:%02d:%02d",
SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);
}
VOID
GuidToStr(
IN GUID *pGuid,
OUT PCHAR s
)
/*++
Routine Description:
Convert a GUID to a string.
Based on code from Mac McLain.
Arguments:
pGuid - ptr to the GUID.
s - The output character buffer.
Must be at least GUID_CHAR_LEN (36 bytes) long.
Function Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "GuidToStr:"
if (pGuid != NULL) {
sprintf(s, "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
pGuid->Data1,
pGuid->Data2,
pGuid->Data3,
pGuid->Data4[0],
pGuid->Data4[1],
pGuid->Data4[2],
pGuid->Data4[3],
pGuid->Data4[4],
pGuid->Data4[5],
pGuid->Data4[6],
pGuid->Data4[7]);
} else {
sprintf(s, "<ptr-null>");
}
}
VOID
GuidToStrW(
IN GUID *pGuid,
OUT PWCHAR ws
)
/*++
Routine Description:
Convert a GUID to a wide string.
Arguments:
pGuid - ptr to the GUID.
ws - The output character buffer.
Must be at least GUID_CHAR_LEN (36 wchars) long.
Function Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "GuidToStrW:"
if (pGuid) {
swprintf(ws, L"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
pGuid->Data1,
pGuid->Data2,
pGuid->Data3,
pGuid->Data4[0],
pGuid->Data4[1],
pGuid->Data4[2],
pGuid->Data4[3],
pGuid->Data4[4],
pGuid->Data4[5],
pGuid->Data4[6],
pGuid->Data4[7]);
} else {
swprintf(ws, L"<ptr-null>");
}
}
BOOL
StrWToGuid(
IN PWCHAR ws,
OUT GUID *pGuid
)
/*++
Routine Description:
Convert a wide string into a GUID. The wide string was created with
GuidToStrW().
Arguments:
pGuid - ptr to the output GUID.
ws - The character buffer.
Function Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "StrWToGuid:"
DWORD Fields;
UCHAR Guid[sizeof(GUID) + sizeof(DWORD)]; // 3 byte overflow
GUID *lGuid = (GUID *)Guid;
FRS_ASSERT(ws && pGuid);
Fields = swscanf(ws, L"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
&lGuid->Data1,
&lGuid->Data2,
&lGuid->Data3,
&lGuid->Data4[0],
&lGuid->Data4[1],
&lGuid->Data4[2],
&lGuid->Data4[3],
&lGuid->Data4[4],
&lGuid->Data4[5],
&lGuid->Data4[6],
&lGuid->Data4[7]);
COPY_GUID(pGuid, lGuid);
return (Fields == 11);
}
VOID
StrToGuid(
IN PCHAR s,
OUT GUID *pGuid
)
/*++
Routine Description:
Convert a string in GUID display format to an object ID that
can be used to lookup a file.
based on a routine by Mac McLain
Arguments:
pGuid - ptr to the output GUID.
s - The input character buffer in display guid format.
e.g.: b81b486b-c338-11d0-ba4f0000f80007df
Must be at least GUID_CHAR_LEN (35 bytes) long.
Function Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "StrToGuid:"
UCHAR Guid[sizeof(GUID) + sizeof(DWORD)]; // 3 byte overflow
GUID *lGuid = (GUID *)Guid;
FRS_ASSERT(s && pGuid);
sscanf(s, "%08lx-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
&lGuid->Data1,
&lGuid->Data2,
&lGuid->Data3,
&lGuid->Data4[0],
&lGuid->Data4[1],
&lGuid->Data4[2],
&lGuid->Data4[3],
&lGuid->Data4[4],
&lGuid->Data4[5],
&lGuid->Data4[6],
&lGuid->Data4[7]);
COPY_GUID(pGuid, lGuid);
}
NTSTATUS
SetupOnePrivilege (
ULONG Privilege,
PUCHAR PrivilegeName
)
{
#undef DEBSUB
#define DEBSUB "SetupOnePrivilege:"
BOOLEAN PreviousPrivilegeState;
NTSTATUS Status;
Status = RtlAdjustPrivilege(Privilege, TRUE, FALSE, &PreviousPrivilegeState);
if (!NT_SUCCESS(Status)) {
DPRINT1(0, ":S: Your login does not have `%s' privilege.\n", PrivilegeName);
if (Status != STATUS_PRIVILEGE_NOT_HELD) {
DPRINT_NT(0, ":S: RtlAdjustPrivilege failed :", Status);
}
DPRINT(0, ":S: Update your: User Manager -> Policies -> User Rights.\n");
}
DPRINT2(4, ":S: Added `%s' privilege (previous: %s)\n",
PrivilegeName, (PreviousPrivilegeState ? "Enabled" : "Disabled"));
return Status;
}
PWCHAR
FrsGetResourceStr(
LONG Id
)
/*++
Routine Description:
This routine Loads the specified resource string.
It allocates a buffer and returns the ptr.
Arguments:
Id - An FRS_IDS_xxx identifier.
Return Value:
Ptr to allocated string.
The caller must free the buffer with a call to FrsFree().
--*/
#undef DEBSUB
#define DEBSUB "FrsGetResourceStr:"
{
LONG N = 0;
WCHAR WStr[200];
HINSTANCE hInst = NULL;
PWCHAR MessageFile = NULL;
//
// ID Must be Valid.
//
if ((Id <= IDS_TABLE_START) || (Id > IDS_TABLE_END)) {
DPRINT1(0, "++ Resource string ID is out of range - %d\n", Id);
Id = IDS_MISSING_STRING;
}
WStr[0] = UNICODE_NULL;
CfgRegReadString(FKC_FRS_MESSAGE_FILE_PATH, NULL, 0, &MessageFile);
hInst = LoadLibrary(MessageFile);
if (hInst != NULL) {
N = LoadString(hInst, Id, WStr, ARRAY_SZ(WStr));
if (N == 0) {
DPRINT_WS(0, "ERROR - Failed to get resource string.", GetLastError());
}
FreeLibrary(hInst);
} else {
DPRINT_WS(0, "ERROR - Failed to LoadLibrary.", GetLastError());
}
FrsFree(MessageFile);
return FrsWcsDup(WStr);
}
DWORD
FrsOpenSourceFileW(
OUT PHANDLE Handle,
IN LPCWSTR lpFileName,
IN ACCESS_MASK DesiredAccess,
IN ULONG CreateOptions
)
/*++
Routine Description:
This function opens the specified file with backup intent for
reading all the files attributes, ...
Arguments:
Handle - A pointer to a handle to return an open handle.
lpFileName - Represents the name of the file or directory to be opened.
DesiredAccess
CreateOptions
Return Value:
Win32 Error status.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsOpenSourceFileW:"
NTSTATUS Status;
DWORD WStatus = ERROR_SUCCESS;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN b;
RTL_RELATIVE_NAME RelativeName;
PVOID FreeBuffer;
ULONG FileAttributes;
ULONG CreateDisposition;
ULONG ShareMode;
//
// Convert the Dos name to an NT name.
//
b = RtlDosPathNameToNtPathName_U(lpFileName, &FileName, NULL, &RelativeName);
if ( !b ) {
return ERROR_INVALID_NAME;
}
FreeBuffer = FileName.Buffer;
if ( RelativeName.RelativeName.Length ) {
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
} else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL);
CreateDisposition = FILE_OPEN; // Open existing file
ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
FileAttributes = FILE_ATTRIBUTE_NORMAL;
Status = NtCreateFile(Handle,
DesiredAccess,
&Obja,
&IoStatusBlock,
NULL, // Initial allocation size
FileAttributes,
ShareMode,
CreateDisposition,
CreateOptions,
NULL, 0);
if (!NT_SUCCESS(Status)) {
*Handle = INVALID_HANDLE_VALUE;
//
// Get a Win32 status.
//
WStatus = FrsSetLastNTError(Status);
DPRINT_NT(0, "NtCreateFile failed :", Status);
if ( Status == STATUS_OBJECT_NAME_COLLISION ) {
//
// Standard Win32 mapping for this is ERROR_ALREADY_EXISTS.
// Change it.
//
WStatus = ERROR_FILE_EXISTS;
SetLastError(ERROR_FILE_EXISTS);
}
DPRINT1_WS(0, "++ CreateFile failed on file %ws;", FileName.Buffer, WStatus);
}
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
return WStatus;
}
DWORD
FrsOpenSourceFile2W(
OUT PHANDLE Handle,
IN LPCWSTR lpFileName,
IN ACCESS_MASK DesiredAccess,
IN ULONG CreateOptions,
IN ULONG ShareMode
)
/*++
Routine Description:
This function opens the specified file with backup intent for
reading all the files attributes, ...
Like FrsOpenSourceFileW but also accepts the sharing mode parameter.
Arguments:
Handle - A pointer to a handle to return an open handle.
lpFileName - Represents the name of the file or directory to be opened.
DesiredAccess
CreateOptions
ShareMode - File sharing mode for NtCreateFile.
Return Value:
Win32 Error status.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsOpenSourceFile2W:"
NTSTATUS Status;
DWORD WStatus = ERROR_SUCCESS;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN b;
RTL_RELATIVE_NAME RelativeName;
PVOID FreeBuffer;
ULONG FileAttributes;
ULONG CreateDisposition;
//
// Convert the Dos name to an NT name.
//
b = RtlDosPathNameToNtPathName_U(lpFileName, &FileName, NULL, &RelativeName);
if ( !b ) {
return ERROR_INVALID_NAME;
}
FreeBuffer = FileName.Buffer;
if ( RelativeName.RelativeName.Length ) {
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
} else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL);
CreateDisposition = FILE_OPEN; // Open existing file
FileAttributes = FILE_ATTRIBUTE_NORMAL;
Status = NtCreateFile(Handle,
DesiredAccess,
&Obja,
&IoStatusBlock,
NULL, // Initial allocation size
FileAttributes,
ShareMode,
CreateDisposition,
CreateOptions,
NULL, 0);
if (!NT_SUCCESS(Status)) {
*Handle = INVALID_HANDLE_VALUE;
//
// Get a Win32 status.
//
WStatus = FrsSetLastNTError(Status);
DPRINT_NT(0, "NtCreateFile failed :", Status);
if ( Status == STATUS_OBJECT_NAME_COLLISION ) {
//
// Standard Win32 mapping for this is ERROR_ALREADY_EXISTS.
// Change it.
//
WStatus = ERROR_FILE_EXISTS;
SetLastError(ERROR_FILE_EXISTS);
}
DPRINT1_WS(0, "++ CreateFile failed on file %ws;", FileName.Buffer, WStatus);
}
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
return WStatus;
}
BOOL
FrsGetFileInfoByHandle(
IN PWCHAR Name,
IN HANDLE Handle,
OUT PFILE_NETWORK_OPEN_INFORMATION FileOpenInfo
)
/*++
Routine Description:
Return the network file info for the specified handle.
Arguments:
Name - File's name for printing error messages
Handle - Open file handle
FileOpenInfo - Returns the file FILE_NETWORK_OPEN_INFORMATION data.
Return Value:
TRUE - FileOpenInfo contains the file's info
FALSE - Contents of FileOpenInfo is undefined
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetFileInfoByHandle:"
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
//
// Return some file info
//
Status = NtQueryInformationFile(Handle,
&IoStatusBlock,
FileOpenInfo,
sizeof(FILE_NETWORK_OPEN_INFORMATION),
FileNetworkOpenInformation);
if (!NT_SUCCESS(Status)) {
DPRINT_NT(0, "NtQueryInformationFile failed :", Status);
return FALSE;
}
return TRUE;
}
DWORD
FrsGetFileInternalInfoByHandle(
IN HANDLE Handle,
OUT PFILE_INTERNAL_INFORMATION InternalFileInfo
)
/*++
Routine Description:
Return the internal file info for the specified handle.
Arguments:
Handle - Open file handle
InternalFileInfo - Basically, file's reference number (fid)
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetFileInternalInfoByHandle:"
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
//
// Return some file info
//
Status = NtQueryInformationFile(Handle,
&IoStatusBlock,
InternalFileInfo,
sizeof(FILE_INTERNAL_INFORMATION),
FileInternalInformation);
return FrsSetLastNTError(Status);
}
DWORD
FrsReadFileDetails(
IN HANDLE Handle,
IN LPCWSTR FileName,
OUT PFILE_OBJECTID_BUFFER ObjectIdBuffer,
OUT PLONGLONG FileIdBuffer,
OUT PFILE_NETWORK_OPEN_INFORMATION FileNetworkOpenInfo,
IN OUT BOOL *ExistingOid
)
/*++
Routine Description:
This routine reads the object ID. If there is no
object ID on the file we put one on it.
Arguments:
Handle -- The file handle of an opened file.
FileName -- The name of the file. For error messages only.
ObjectIdBuffer -- The output buffer to hold the object ID.
FileIdBuffer -- Returns the NTFS FileReference (FileId).
FileNetworkOpenInfo -- returns FILE_NETWORK_OPEN_INFORMATION
ExistingOid -- INPUT: TRUE means use existing File OID if found.
RETURN: TRUE means an existing File OID was used.
Return Value:
Returns the Win Status of the last error found, or success.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsReadFileDetails:"
FILE_INTERNAL_INFORMATION FileReference;
NTSTATUS Status;
IO_STATUS_BLOCK Iosb;
LONG Loop;
BOOL CallerSupplied = FALSE;
CHAR GuidStr[GUID_CHAR_LEN];
//
// Get the file ID.
//
Status = NtQueryInformationFile(Handle,
&Iosb,
FileIdBuffer,
sizeof(FILE_INTERNAL_INFORMATION),
FileInternalInformation);
if (!NT_SUCCESS(Status)) {
DPRINT_NT(0, "++ ERROR - QueryInfoFile FileID failed :", Status);
FrsSetLastNTError(Status);
}
//
// Get file times, size, attributes.
//
Status = NtQueryInformationFile(Handle,
&Iosb,
FileNetworkOpenInfo,
sizeof(FILE_NETWORK_OPEN_INFORMATION),
FileNetworkOpenInformation);
if (!NT_SUCCESS(Status)) {
DPRINT_NT(0, "++ ERROR - QueryInfoFile FileNetworkOpenInformation failed :", Status);
FrsSetLastNTError(Status);
}
if (!*ExistingOid) {
//
// Set up to slam a new OID on the file.
//
CallerSupplied = TRUE;
ZeroMemory(ObjectIdBuffer, sizeof(FILE_OBJECTID_BUFFER));
FrsUuidCreate((GUID *)ObjectIdBuffer->ObjectId);
}
return FrsGetOrSetFileObjectId(Handle, FileName, CallerSupplied, ObjectIdBuffer);
}
#if 0
// This may not be needed.
ULONG
FrsReadFileSecurity(
IN HANDLE Handle,
IN OUT PTABLE_CTX TableCtx,
IN PWCHAR FileName
)
/*++
Routine Description:
This routine gets the security descriptor from the file. The returned data
is stored into the security descriptor field in the data record allocated
with the table context. If the default buffer is not large enough
a larger buffer is allocated.
Arguments:
Handle -- Handle to open file from which to extract the security desc.
TableCtx -- The table context struct where the security descriptor is
to be written. It must be an IDTable.
FileName -- The full filename. For error messages only.
Return Value:
Returns the WIN32 STATUS error status.
Note: In the event that GetFileSecurity returns ERROR_NO_SECURITY_ON_OBJECT
we release the buffer, setting the length to zero, and return ERROR_SUCCESS.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsReadFileSecurity:"
ULONG WStatus;
NTSTATUS Status;
ULONG BufLen;
PSECURITY_DESCRIPTOR Buffer;
ULONG BufNeeded;
ULONG ActualLen;
JET_ERR jerr;
PJET_SETCOLUMN JSetColumn;
//
// Check the table type is an IDTable.
//
if (TableCtx->TableType != IDTablex) {
DPRINT1(0, "++ ERROR - Invalid Table Type: %d\n", TableCtx->TableType);
return ERROR_INVALID_PARAMETER;
}
//
// Get ptrs to the Jet SetColumn array and the buffer address & length
//
JSetColumn = TableCtx->pJetSetCol;
Buffer = (PSECURITY_DESCRIPTOR) JSetColumn[SecDescx].pvData;
BufLen = JSetColumn[SecDescx].cbData;
//
// The security descriptor is a variable length binary field that
// must have a type/size prefix.
//
((PFRS_NODE_HEADER) Buffer)->Size = (USHORT) BufLen;
((PFRS_NODE_HEADER) Buffer)->Type = 0;
BufNeeded = 0;
//
// Check that the security descriptor buffer looks reasonable.
//
if (Buffer == NULL) {
DPRINT2(0, "++ ERROR - Invalid SD buffer. Buffer Addr: %08x, Len: %d\n",
Buffer, BufLen);
return ERROR_INVALID_PARAMETER;
}
//
// Now go get all the security information.
//
while (TRUE) {
BufLen -= sizeof(FRS_NODE_HEADER); // for type / size prefix.
(PCHAR)Buffer += sizeof(FRS_NODE_HEADER);
Status = NtQuerySecurityObject(
Handle,
SACL_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
Buffer,
BufLen,
&BufNeeded);
if (NT_SUCCESS(Status)) {
ActualLen = GetSecurityDescriptorLength(Buffer) + sizeof(FRS_NODE_HEADER);
BufLen += sizeof(FRS_NODE_HEADER);
DPRINT3(5, "++ GetFileSecurity-1 Buflen: %d, Bufneeded: %d, ActualLen: %d\n",
BufLen, BufNeeded, ActualLen);
//
// If current buffer size is more than 16 bytes larger than needed AND
// also more than 5% greater than needed then shrink the buffer but
// keep the data.
//
if (((BufLen-ActualLen) > 16) &&
(BufLen > (ActualLen + ActualLen/20))) {
DPRINT3(5, "++ GetFileSecurity-2 Reducing buffer, Buflen: %d, Bufneeded: %d, ActualLen: %d\n",
BufLen, BufNeeded, ActualLen);
//
// Unused space in field buffer is greater than 6%.
// Reduce the buffer size but keep the data.
//
jerr = DbsReallocateFieldBuffer(TableCtx, SecDescx, ActualLen, TRUE);
if (!JET_SUCCESS(jerr)) {
return ERROR_NOT_ENOUGH_MEMORY;
}
Buffer = (PSECURITY_DESCRIPTOR) JSetColumn[SecDescx].pvData;
((PFRS_NODE_HEADER) Buffer)->Size = (USHORT) ActualLen;
((PFRS_NODE_HEADER) Buffer)->Type = 0;
}
return ERROR_SUCCESS;
}
//
// Set the win32 error code and message string.
//
WStatus = FrsSetLastNTError(Status);
//
// If not enough buffer reallocate larger buffer.
//
if (WStatus == ERROR_INSUFFICIENT_BUFFER) {
//
// Reallocate the buffer for the security descriptor.
//
jerr = DbsReallocateFieldBuffer(TableCtx, SecDescx, BufNeeded, FALSE);
if (!JET_SUCCESS(jerr)) {
DPRINT_JS(0, "++ ERROR - DbsReallocateFieldBuffer failed.", jerr);
return ERROR_NOT_ENOUGH_MEMORY;
}
Buffer = (PSECURITY_DESCRIPTOR) JSetColumn[SecDescx].pvData;
((PFRS_NODE_HEADER) Buffer)->Size = (USHORT) BufNeeded;
((PFRS_NODE_HEADER) Buffer)->Type = 0;
//
// Get new buffer params and try again to get security information.
//
BufLen = BufNeeded;
continue;
}
//
// Check for ERROR_NO_SECURITY_ON_OBJECT and release the buffer so we
// don't waste space in the database.
//
if (WStatus == ERROR_NO_SECURITY_ON_OBJECT) {
DPRINT2(0, "++ ERROR - GetFileSecurity-3 (NO_SEC) Buflen: %d, Bufneeded: %d\n", BufLen, BufNeeded);
//
// Free the buffer and set the length to zero.
//
DbsReallocateFieldBuffer(TableCtx, SecDescx, 0, FALSE);
return ERROR_SUCCESS;
}
//
// Some other error.
//
DPRINT_WS(0, "++ ERROR - GetFileSecurity-4;", WStatus);
return WStatus;
}
}
#endif
PWCHAR
FrsGetFullPathByHandle(
IN PWCHAR Name,
IN HANDLE Handle
)
/*++
Routine Description:
Return a copy of the handle's full pathname. Free with FrsFree().
Arguments:
Name
Handle
Return Value:
Return a copy of the handle's full pathname. Free with FrsFree().
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetFullPathByHandle"
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
DWORD BufferSize;
PCHAR Buffer;
PWCHAR RetFileName = NULL;
CHAR NameBuffer[sizeof(ULONG) + (sizeof(WCHAR)*(MAX_PATH+1))];
PFILE_NAME_INFORMATION FileName;
if (!HANDLE_IS_VALID(Handle)) {
return NULL;
}
BufferSize = sizeof(NameBuffer);
Buffer = NameBuffer;
again:
FileName = (PFILE_NAME_INFORMATION) Buffer;
FileName->FileNameLength = BufferSize - (sizeof(ULONG) + sizeof(WCHAR));
Status = NtQueryInformationFile(Handle,
&IoStatusBlock,
FileName,
BufferSize - sizeof(WCHAR),
FileNameInformation);
if (NT_SUCCESS(Status) ) {
FileName->FileName[FileName->FileNameLength/2] = UNICODE_NULL;
RetFileName = FrsWcsDup(FileName->FileName);
} else {
//
// Try a larger buffer
//
if (Status == STATUS_BUFFER_OVERFLOW) {
DPRINT2(4, "++ Buffer size %d was too small for %ws\n",
BufferSize, Name);
BufferSize = FileName->FileNameLength + sizeof(ULONG) + sizeof(WCHAR);
if (Buffer != NameBuffer) {
FrsFree(Buffer);
}
Buffer = FrsAlloc(BufferSize);
DPRINT2(4, "++ Retrying with buffer size %d for %ws\n",
BufferSize, Name);
goto again;
}
DPRINT1_NT(0, "++ NtQueryInformationFile - FileNameInformation failed.",
Name, Status);
}
//
// A large buffer was allocated if the file's full
// name could not fit into MAX_PATH chars.
//
if (Buffer != NameBuffer) {
FrsFree(Buffer);
}
return RetFileName;
}
PWCHAR
FrsGetTrueFileNameByHandle(
IN PWCHAR Name,
IN HANDLE Handle,
OUT PLONGLONG DirFileID
)
/*++
Routine Description:
Return a copy of the filename part associated with this handle.
Free with FrsFree().
Arguments:
Name
Handle
DirFileID - If non-null, return the parent File ID.
Return Value:
Return a copy of the filename part associated with this handle.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetTrueFileNameByHandle"
PWCHAR Path;
PWCHAR File;
ULONG Len;
Path = FrsGetFullPathByHandle(Name, Handle);
if (!Path) {
return NULL;
}
for (Len = wcslen(Path); Len && Path[Len] != L'\\'; --Len);
File = FrsWcsDup(&Path[Len + 1]);
FrsFree(Path);
if (DirFileID != NULL) {
FrsReadFileParentFid(Handle, DirFileID);
}
return File;
}
DWORD
FrsOpenFileRelativeByName(
IN HANDLE VolumeHandle,
IN PULONGLONG FileReferenceNumber,
IN PWCHAR FileName,
IN GUID *ParentGuid,
IN GUID *FileGuid,
OUT HANDLE *Handle
)
/*++
Routine Description:
Open the file specified by its true name using the FID for either
a rename or delete installation. If the FID is null then use the
Filename as given.
FrsOpenFileRelativeByName(Coe->NewReplica->pVme->VolumeHandle,
&Coe->FileReferenceNumber, // or NULL
Coc->FileName,
&Coc->OldParentGuid,
&Coc->FileGuid
&Handle);
Arguments:
VolumeHandle, - handle to root of the drive
FileReferenceNumber - FID for the file in question (NULL if supplied
filename is valid)
FileName, - Filename
*ParentGuid, - ptr to the object ID for the file's parent dir.
*FileGuid, - ptr to the object ID for the file (for checking,
NULL if no check needed).
*Handle - Returned handle for open file.
Return Value:
Handle and win32 status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsOpenFileRelativeByName"
PWCHAR TrueFileName;
DWORD WStatus;
*Handle = INVALID_HANDLE_VALUE;
if (FileReferenceNumber != NULL) {
//
// Open the source file and get the current "True" File name.
//
WStatus = FrsOpenSourceFileById(Handle,
NULL,
NULL,
VolumeHandle,
FileReferenceNumber,
FILE_ID_LENGTH,
// READ_ACCESS,
READ_ATTRIB_ACCESS,
ID_OPTIONS,
SHARE_ALL,
FILE_OPEN);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(4, "++ Couldn't open file %ws;", FileName, WStatus);
return WStatus;
}
//
// File's TrueFileName
//
TrueFileName = FrsGetTrueFileNameByHandle(FileName, *Handle, NULL);
FRS_CLOSE(*Handle);
if (TrueFileName == NULL) {
DPRINT1(4, "++ Couldn't get base name for %ws\n", FileName);
WIN_SET_FAIL(WStatus);
return WStatus;
}
} else {
TrueFileName = FileName;
}
//
// Open the file relative to the parent using the true filename.
//
WStatus = FrsCreateFileRelativeById(Handle,
VolumeHandle,
ParentGuid,
OBJECT_ID_LENGTH,
FILE_ATTRIBUTE_NORMAL,
TrueFileName,
(USHORT)(wcslen(TrueFileName) * sizeof(WCHAR)),
NULL,
FILE_OPEN,
// DELETE | READ_ACCESS | FILE_WRITE_ATTRIBUTES);
DELETE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY);
if (FileReferenceNumber != NULL) {
FrsFree(TrueFileName);
}
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(4, "++ Couldn't open relative file %ws;", FileName, WStatus);
return WStatus;
}
//
// Get the file's oid and check it against the value supplied.
//
if (FileGuid != NULL) {
WStatus = FrsCheckObjectId(FileName, *Handle, FileGuid);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(0, "++ Object id mismatch for file %ws;", FileName, WStatus);
FRS_CLOSE(*Handle);
}
}
return WStatus;
}
DWORD
FrsDeleteFileRelativeByName(
IN HANDLE VolumeHandle,
IN GUID *ParentGuid,
IN PWCHAR FileName,
IN PQHASH_TABLE FrsWriteFilter
)
/*++
Routine Description:
Delete the file or dir subtree specified by its name relative to
the parent dir specified by its object ID (guid).
Arguments:
VolumeHandle, - handle to root of the drive
*ParentGuid, - ptr to the object ID for the file's parent dir.
FileName, - Filename
FrsWriteFilter - Write filter to use for dampening (NULL if undampened).
e.g. Coe->NewReplica->pVme->FrsWriteFilter
Return Value:
Win32 status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteFileRelativeByName"
DWORD WStatus;
HANDLE Handle = INVALID_HANDLE_VALUE;
//
// Open the file
//
WStatus = FrsOpenFileRelativeByName(VolumeHandle,
NULL,
FileName,
ParentGuid,
NULL,
&Handle);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(4, "++ Couldn't open file %ws for delete;", FileName, WStatus);
//
// File has already been deleted; done
//
if (WIN_NOT_FOUND(WStatus)) {
DPRINT1(4, "++ %ws is already deleted\n", FileName);
WStatus = ERROR_SUCCESS;
}
goto out;
}
//
// Handles can be marked so that any usn records resulting from
// operations on the handle will have the same "mark". In this
// case, the mark is a bit in the SourceInfo field of the usn
// record. The mark tells NtFrs to ignore the usn record during
// recovery because this was a NtFrs generated change.
//
if (FrsWriteFilter) {
WStatus = FrsMarkHandle(VolumeHandle, Handle);
DPRINT1_WS(0, "++ WARN - FrsMarkHandle(%ws);", FileName, WStatus);
}
//
// Reset the attributes that prevent deletion
//
WStatus = FrsResetAttributesForReplication(FileName, Handle);
if (!WIN_SUCCESS(WStatus)) {
goto out;
}
//
// Mark the file for delete
//
WStatus = FrsDeleteByHandle(FileName, Handle);
if (!WIN_SUCCESS(WStatus)) {
//
// If this was a non-empty dir then delete the subtree.
//
if (WStatus == ERROR_DIR_NOT_EMPTY) {
WStatus = FrsEnumerateDirectory(Handle,
FileName,
0,
ENUMERATE_DIRECTORY_FLAGS_NONE,
NULL,
FrsEnumerateDirectoryDeleteWorker);
}
WStatus = FrsDeleteByHandle(FileName, Handle);
}
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(0, "++ Could not delete %ws;", FileName, WStatus);
goto out;
}
out:
DPRINT2(5, "++ %s deleting %ws\n", (WIN_SUCCESS(WStatus)) ? "Success" : "Failure",
FileName);
//
// If the file was marked for delete, this close will delete it
//
if (HANDLE_IS_VALID(Handle)) {
if (FrsWriteFilter != NULL) {
FrsCloseWithUsnDampening(FileName, &Handle, FrsWriteFilter, NULL);
} else {
FRS_CLOSE(Handle);
}
}
return WStatus;
}
DWORD
FrsDeletePath(
IN PWCHAR Path,
IN DWORD DirectoryFlags
)
/*++
Routine Description:
Delete the file or dir subtree specified by its path
WARN: Does not dampen the operations. To be safe, the replica
set should not exist or the directory should be filtered.
Otherwise, the deletes might replicate.
Arguments:
Path - Path of file system object
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
Return Value:
Win32 status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeletePath"
DWORD WStatus;
HANDLE Handle = INVALID_HANDLE_VALUE;
FILE_NETWORK_OPEN_INFORMATION FileInfo;
//
// Open the file
//
WStatus = FrsOpenSourceFileW(&Handle,
Path,
// DELETE | READ_ACCESS | FILE_WRITE_ATTRIBUTES,
DELETE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY,
OPEN_OPTIONS);
if (WIN_NOT_FOUND(WStatus)) {
CLEANUP1_WS(1, "++ WARN - FrsOpenSourceFile(%ws); (IGNORED);",
Path, WStatus, RETURN_SUCCESS);
}
CLEANUP1_WS(0, "++ ERROR - FrsOpenSourceFile(%ws);", Path, WStatus, CLEANUP);
//
// Get the file's attributes
//
if (!FrsGetFileInfoByHandle(Path, Handle, &FileInfo)) {
DPRINT1(1, "++ WARN - Can't get attributes for %ws\n", Path);
WIN_SET_FAIL(WStatus);
goto CLEANUP;
}
//
// Don't delete the file if DIRECTORIES_ONLY is set
//
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_DIRECTORIES_ONLY &&
!(FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
WStatus = ERROR_DIRECTORY;
goto CLEANUP;
}
//
// Reset the attributes that prevent deletion
//
if (FileInfo.FileAttributes & NOREPL_ATTRIBUTES) {
DPRINT1(5, "++ Reseting attributes for %ws for delete\n", Path);
WStatus = FrsSetFileAttributes(Path, Handle,
FileInfo.FileAttributes &
~NOREPL_ATTRIBUTES);
CLEANUP1_WS(0, "++ ERROR: - Can't reset attributes for %ws for delete", Path, WStatus, CLEANUP);
DPRINT1(5, "++ Attributes for %ws now allow deletion\n", Path);
}
//
// Mark the file for delete
//
WStatus = FrsDeleteByHandle(Path, Handle);
if (!WIN_SUCCESS(WStatus)) {
//
// If this was a non-empty dir then delete the subtree.
//
if (WStatus == ERROR_DIR_NOT_EMPTY) {
WStatus = FrsEnumerateDirectory(Handle,
Path,
0,
DirectoryFlags,
NULL,
FrsEnumerateDirectoryDeleteWorker);
}
WStatus = FrsDeleteByHandle(Path, Handle);
}
DPRINT1_WS(0, "++ ERROR - Could not delete %ws;", Path, WStatus);
CLEANUP:
DPRINT2(5, "++ %s deleting %ws\n",
(WIN_SUCCESS(WStatus)) ? "Success" : "Failure", Path);
FRS_CLOSE(Handle);
return WStatus;
RETURN_SUCCESS:
WStatus = ERROR_SUCCESS;
goto CLEANUP;
}
DWORD
FrsDeleteDirectoryContents(
IN PWCHAR Path,
IN DWORD DirectoryFlags
)
/*++
Routine Description:
Delete the contents of the directory Path
Arguments:
Path - Path of file system object
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
Return Value:
Win32 status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteDirectoryContents"
DWORD WStatus;
HANDLE Handle = INVALID_HANDLE_VALUE;
//
// Open the file
//
WStatus = FrsOpenSourceFileW(&Handle, Path,
// READ_ACCESS,
READ_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
OPEN_OPTIONS);
if (WIN_NOT_FOUND(WStatus)) {
CLEANUP1_WS(1, "++ WARN - FrsOpenSourceFile(%ws); (IGNORED);",
Path, WStatus, RETURN_SUCCESS);
}
CLEANUP1_WS(0, "++ ERROR - FrsOpenSourceFile(%ws);", Path, WStatus, CLEANUP);
WStatus = FrsEnumerateDirectory(Handle,
Path,
0,
DirectoryFlags,
NULL,
FrsEnumerateDirectoryDeleteWorker);
CLEANUP1_WS(0, "++ ERROR - Could not delete contents of %ws;",
Path, WStatus, CLEANUP);
CLEANUP:
DPRINT2(5, "++ %s deleting contents of %ws\n",
(WIN_SUCCESS(WStatus)) ? "Success" : "Failure", Path);
FRS_CLOSE(Handle);
return WStatus;
RETURN_SUCCESS:
WStatus = ERROR_SUCCESS;
goto CLEANUP;
}
DWORD
FrsOpenBaseNameForInstall(
IN PCHANGE_ORDER_ENTRY Coe,
OUT HANDLE *Handle
)
/*++
Routine Description:
Open the file specified by Coe by its relative name for either
a rename or delete installation.
Note that it is possible for the file to have been moved to a new parent
dir by a previous remote CO or a local CO, making the OldParentGuid in the
Change Order invalid. First we try to find the file under the OldParentGuid
in the CO and then we try by the parent Guid in the IDTable. We check for
a match by comparing with the file GUID in the change order.
It is also possible that the file has been renamed to a point outside the
replica tree so even if we find it by FID we still can't do anything to it.
When we fail to find the file in either of the above directories we force this
CO thru retry, expecting another CO behind us to get processed and update
the parent guid in the IDTable or maybe mark the file as deleted.
Arguments:
Coe
Handle
Return Value:
Handle and win status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsOpenBaseNameForInstall"
LONGLONG ParentFid;
PWCHAR FileName;
DWORD WStatus;
PCHANGE_ORDER_COMMAND Coc = &Coe->Cmd;
PIDTABLE_RECORD IDTableRec;
BOOLEAN UseActualLocation = FALSE;
ParentFid = QUADZERO;
*Handle = INVALID_HANDLE_VALUE;
//
// Open the source file
//
WStatus = FrsOpenSourceFileById(Handle,
NULL,
NULL,
Coe->NewReplica->pVme->VolumeHandle,
&Coe->FileReferenceNumber,
FILE_ID_LENGTH,
// READ_ACCESS,
READ_ATTRIB_ACCESS,
ID_OPTIONS,
SHARE_ALL,
FILE_OPEN);
if (!WIN_SUCCESS(WStatus)) {
CHANGE_ORDER_TRACEW(3, Coe, "File open by FID failed", WStatus);
return WStatus;
}
//
// Get the File's true on-disk filename and the true parent FID.
//
FileName = FrsGetTrueFileNameByHandle(Coc->FileName, *Handle, &ParentFid);
FRS_CLOSE(*Handle);
if (FileName == NULL) {
CHANGE_ORDER_TRACE(3, Coe, "Failed to get file base name");
return ERROR_FILE_NOT_FOUND;
}
//
// Open the file relative to the parent using the true filename.
//
WStatus = FrsCreateFileRelativeById(Handle,
Coe->NewReplica->pVme->VolumeHandle,
&Coc->OldParentGuid,
OBJECT_ID_LENGTH,
FILE_ATTRIBUTE_NORMAL,
FileName,
(USHORT)(wcslen(FileName) * sizeof(WCHAR)),
NULL,
FILE_OPEN,
// DELETE | READ_ACCESS | FILE_WRITE_ATTRIBUTES);
DELETE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY);
if (WIN_SUCCESS(WStatus)) {
//
// Get the file's oid and check it against the change order. Need to
// do this to cover the case of a rename of the file to a different
// parent dir followed by a create of a file with the same name.
// If this occurred the above open would succeed.
//
WStatus = FrsCheckObjectId(Coc->FileName, *Handle, &Coc->FileGuid);
if (WIN_SUCCESS(WStatus)) {
goto RETURN;
}
CHANGE_ORDER_TRACE(3, Coe, "File OID mismatch CO, after Coc->OldParentGuid open");
FRS_CLOSE(*Handle);
} else {
CHANGE_ORDER_TRACEW(3, Coe, "File open failed under Coc->OldParentGuid", WStatus);
}
//
// We did not find the file using the True Name from the file and the
// Old parent Guid from the change order. The file may have been moved
// by a previous remote CO or a Local CO. Try the Parent Guid in the
// IDTable record.
//
FRS_ASSERT(Coe->RtCtx != NULL);
FRS_ASSERT(IS_ID_TABLE(&Coe->RtCtx->IDTable));
IDTableRec = Coe->RtCtx->IDTable.pDataRecord;
FRS_ASSERT(IDTableRec != NULL);
WStatus = FrsCreateFileRelativeById(Handle,
Coe->NewReplica->pVme->VolumeHandle,
&IDTableRec->ParentGuid,
OBJECT_ID_LENGTH,
FILE_ATTRIBUTE_NORMAL,
FileName,
(USHORT)(wcslen(FileName) * sizeof(WCHAR)),
NULL,
FILE_OPEN,
// DELETE | READ_ACCESS | FILE_WRITE_ATTRIBUTES);
DELETE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY);
if (WIN_SUCCESS(WStatus)) {
//
// Get the file's oid and check it against the change order.
//
WStatus = FrsCheckObjectId(Coc->FileName, *Handle, &Coc->FileGuid);
if (WIN_SUCCESS(WStatus)) {
goto RETURN;
}
CHANGE_ORDER_TRACE(3, Coe, "File OID mismatch CO, after IDTableRec->ParentGuid");
FRS_CLOSE(*Handle);
} else {
CHANGE_ORDER_TRACEW(3, Coe, "File open failed under IDTableRec->ParentGuid", WStatus);
}
//
// If this is a delete change order then we may have a problem if the file
// has been moved to a different parent dir by a local file operation.
// The local change order that did this can be rejected if the remote
// CO delete is processed first so the local co fails reconcile. But the
// ondisk rename has been completed and when the remote CO delete tries to
// delete the file it may not be in either the parent dir from the remote
// CO or the parent dir from the IDTable. To cover this case we find the
// TRUE parent dir and check to see if the file is still in the replica tree.
// If it is then we delete it using the TRUE parent dir. IF it isn't then
// return success since the file is already outside the tree.
// Note that a sharing conflict on the target file can block the delete
// for an extended period of time so the timing window in which this can
// occur can be pretty wide.
//
//
// We also need to deal with the case where a new file is still sitting in
// the preinstall directory (and the CO is in install_rename_retry.)
// In that case we will have failed to find the file under the old parent from
// the CO or under the parent listed in the IDTable.
//
if (DOES_CO_DELETE_FILE_NAME(Coc)) {
if (JrnlIsChangeOrderInReplica(Coe, &ParentFid)) {
UseActualLocation = TRUE;
} else {
//
// File not in the replica tree any more so tell the caller.
//
WStatus = ERROR_FILE_NOT_FOUND;
goto RETURN;
}
}
if(ParentFid == Coe->NewReplica->PreInstallFid) {
UseActualLocation = TRUE;
}
if (UseActualLocation) {
WStatus = FrsCreateFileRelativeById(Handle,
Coe->NewReplica->pVme->VolumeHandle,
&ParentFid,
FILE_ID_LENGTH,
FILE_ATTRIBUTE_NORMAL,
FileName,
(USHORT)(wcslen(FileName) * sizeof(WCHAR)),
NULL,
FILE_OPEN,
// DELETE | READ_ACCESS | FILE_WRITE_ATTRIBUTES);
DELETE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY);
if (WIN_SUCCESS(WStatus)) {
//
// Get the file's oid and check it against the change order.
//
WStatus = FrsCheckObjectId(Coc->FileName, *Handle, &Coc->FileGuid);
if (WIN_SUCCESS(WStatus)) {
goto RETURN;
}
CHANGE_ORDER_TRACE(3, Coe, "File OID mismatch with CO after TRUE ParentFid open");
FRS_CLOSE(*Handle);
} else {
CHANGE_ORDER_TRACEW(3, Coe, "File open failed under True Parent FID", WStatus);
}
}
//
// The file is there but not where we expected it to be so send this CO
// through retry to let a subsequent Local CO get processed and update the
// IDTable.
//
WStatus = ERROR_RETRY;
RETURN:
CHANGE_ORDER_TRACEW(3, Coe, "Base File open", WStatus);
FrsFree(FileName);
return WStatus;
}
DWORD
FrsDeleteById(
IN PWCHAR VolumeName,
IN PWCHAR Name,
IN PVOLUME_MONITOR_ENTRY pVme,
IN PVOID Id,
IN DWORD IdLen
)
/*++
Routine Description:
Delete the file represented by Id
Arguments:
VolumeName - corresponding to pVme
Name - For error messages
pVme - volume entry
Id - Represents the name of the file or directory to be opened.
IdLen - length of Id (Fid or Oid)
Return Value:
Handle and win status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteById"
DWORD WStatus;
HANDLE Handle = INVALID_HANDLE_VALUE;
PWCHAR Path = NULL;
PWCHAR FullPath = NULL;
DPRINT1(5, "++ Deleting %ws by id\n", Name);
//
// Open the source file
//
WStatus = FrsOpenSourceFileById(&Handle,
NULL,
NULL,
pVme->VolumeHandle,
Id,
IdLen,
// READ_ACCESS,
READ_ATTRIB_ACCESS,
ID_OPTIONS,
SHARE_ALL,
FILE_OPEN);
CLEANUP1_WS(4, "++ ERROR - FrsOpenSourceFileById(%ws);", Name, WStatus, CLEANUP);
//
// File's relative pathname
//
Path = FrsGetFullPathByHandle(Name, Handle);
if (Path) {
FullPath = FrsWcsCat(VolumeName, Path);
}
FRS_CLOSE(Handle);
if (FullPath == NULL) {
DPRINT1(4, "++ ERROR - FrsGetFullPathByHandle(%ws)\n", Name);
WIN_SET_FAIL(WStatus);
goto CLEANUP;
}
//
// Open the file relative to the parent using the true filename.
//
WStatus = FrsOpenSourceFileW(&Handle,
FullPath,
// DELETE | READ_ACCESS | FILE_WRITE_ATTRIBUTES,
DELETE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES,
OPEN_OPTIONS);
CLEANUP2_WS(4, "++ ERROR - FrsOpenSourceFile(%ws -> %ws);",
Name, FullPath, WStatus, CLEANUP);
//
// Handles can be marked so that any usn records resulting from
// operations on the handle will have the same "mark". In this
// case, the mark is a bit in the SourceInfo field of the usn
// record. The mark tells NtFrs to ignore the usn record during
// recovery because this was a NtFrs generated change.
//
WStatus = FrsMarkHandle(pVme->VolumeHandle, Handle);
CLEANUP1_WS(0, "++ WARN - FrsMarkHandle(%ws);", Name, WStatus, RETURN_SUCCESS);
//
// Get the file's oid and check it against the id
//
if (IdLen == OBJECT_ID_LENGTH) {
WStatus = FrsCheckObjectId(Name, Handle, Id);
CLEANUP1_WS(4, "++ ERROR - FrsCheckObjectId(%ws);", Name, WStatus, CLEANUP);
}
WStatus = FrsResetAttributesForReplication(FullPath, Handle);
DPRINT1_WS(4, "++ ERROR - FrsResetAttributesForReplication(%ws):", FullPath, WStatus);
WStatus = FrsDeleteByHandle(Name, Handle);
FrsCloseWithUsnDampening(Name, &Handle, pVme->FrsWriteFilter, NULL);
CLEANUP1_WS(4, "++ ERROR - FrsDeleteByHandle(%ws);", Name, WStatus, CLEANUP);
CLEANUP:
FRS_CLOSE(Handle);
FrsFree(Path);
FrsFree(FullPath);
return WStatus;
RETURN_SUCCESS:
WStatus = ERROR_SUCCESS;
goto CLEANUP;
}
BOOL
FrsCloseWithUsnDampening(
IN PWCHAR Name,
IN OUT PHANDLE Handle,
IN PQHASH_TABLE FrsWriteFilter,
OUT USN *RetUsn
)
/*++
Routine Description:
Close the handle after insuring that any modifications made to the
file will not generate change orders.
Arguments:
Name - File name for error messages.
Handle - Handle to the replica set file file being closed. Nop if
INVALID_HANDLE_VALUE.
Replica - ptr to Replica struct where this file was written.
This gets us to the volume write filter table to record the USN.
RetUsn - ptr to return location for the close USN. NULL if not requested.
Return Value:
TRUE - handle is closed and any changes where dampened
FALSE - handle is closed but replication was *not* dampened
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCloseWithUsnDampening"
DWORD BytesReturned = 0;
USN Usn = 0;
ULONG GStatus;
BOOL RetStatus;
RetStatus = TRUE;
if (!HANDLE_IS_VALID(*Handle)) {
return TRUE;
}
//
// Get the lock on the Usn Write Filter table.
// We have to get this before the FSCTL_WRITE_USN_CLOSE_RECORD call
// which will generate the journal close record. THis closes the
// race between our subsequent update of the WriteFilter below
// and the journal thread that processes the USN close record.
//
QHashAcquireLock(FrsWriteFilter);
//
// Close the file and force out the journal close record now. This
// call returns the USN of the generated close record so we can filter
// it out of the journal record stream.
//
if (!DeviceIoControl(*Handle,
FSCTL_WRITE_USN_CLOSE_RECORD,
NULL, 0,
&Usn, sizeof(USN),
&BytesReturned, NULL)) {
//
// Access denied is returned if there is another open
//
if (GetLastError() != ERROR_ACCESS_DENIED) {
DPRINT1_WS(0, "++ Can't dampen replication on %ws;", Name, GetLastError());
} else {
DPRINT1(0, "++ Can't dampen %ws; access denied\n", Name);
}
RetStatus = FALSE;
}
RetStatus = RetStatus && (BytesReturned == sizeof(USN));
if (RetStatus) {
//
// Put the USN in the FrsWriteFilter table for the replica so we
// can ignore it and the drop the lock on the table.
//
GStatus = QHashInsert(FrsWriteFilter, &Usn, &Usn, 0, TRUE);
QHashReleaseLock(FrsWriteFilter);
if (GStatus != GHT_STATUS_SUCCESS ) {
DPRINT1(0, "++ QHashInsert error: %d\n", GStatus);
RetStatus = FALSE;
}
} else {
QHashReleaseLock(FrsWriteFilter);
}
//
// Return the close USN.
//
if (RetUsn != NULL) {
*RetUsn = Usn;
}
//
// Now do the normal close to release the handle. NTFS completed its
// close work above.
//
FRS_CLOSE(*Handle);
DPRINT2(5, "++ Dampening %s on %ws\n", (RetStatus) ? "Succeeded" : "Failed", Name);
return RetStatus;
}
VOID
ProcessOpenByIdStatus(
IN HANDLE Handle,
IN ULONG NtStatus,
IN PVOID ObjectId,
IN ULONG Length
)
/*++
Routine Description:
Print the results of an open-by-id.
Arguments:
NtStatus
ObjectId
Length
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "ProcessOpenByIdStatus:"
CHAR GuidStr[GUID_CHAR_LEN];
PWCHAR Path;
if (!NT_SUCCESS(NtStatus)) {
//
// Note: The following call seems to generate intermittant AVs in the
// symbol lookup code. Only include it for testing.
//
//STACK_TRACE_AND_PRINT(2);
if (Length == FILE_ID_LENGTH) {
DPRINT2_NT(1, "++ %08X %08X Fid Open failed;",
*((PULONG)ObjectId+1), *(PULONG)ObjectId, NtStatus);
} else {
GuidToStr((GUID *) ObjectId, GuidStr);
DPRINT1_NT(1, "++ %s ObjectId Open failed;", GuidStr, NtStatus);
}
return;
}
//
// Open succeeded.
//
if (Length == FILE_ID_LENGTH) {
DPRINT2(4,"++ %08X %08X Fid Opened succesfully\n",
*((PULONG)ObjectId+1), *((PULONG)ObjectId));
} else {
GuidToStr((GUID *) ObjectId, GuidStr);
DPRINT1(4, "++ %s ObjectId Opened succesfully\n", GuidStr);
}
if (DoDebug(4, DEBSUB)) {
Path = FrsGetFullPathByHandle(L"Unknown", Handle);
if (Path) {
DPRINT1(4, "++ Filename is: %ws\n", Path);
}
FrsFree(Path);
}
}
DWORD
FrsForceOpenId(
OUT PHANDLE Handle,
IN OUT OVERLAPPED *OpLock, OPTIONAL
IN PVOLUME_MONITOR_ENTRY pVme,
IN PVOID Id,
IN DWORD IdLen,
IN ACCESS_MASK DesiredAccess,
IN ULONG CreateOptions,
IN ULONG ShareMode,
IN ULONG CreateDisposition
)
/*++
Routine Description:
Open the file for the desired access. If the open fails, reset
the readonly/system/hidden attributes and retry. In any case,
make sure the attributes are reset to their original value
before returning.
Arguments:
Handle - Returns the file handle.
OpLock - Overlapped struct for an oplock (optional).
pVme - volume entry
Id - Represents the name of the file or directory to be opened.
IdLen - length of Id (Fid or Oid)
DesiredAccess - see replutil.h for defined access modes (xxx_ACCESS)
CreateOptions - see replutil.h for defined options (xxx_OPTIONS
ShareMode - standard share modes defined in sdk
CreateDisposition - E.g., FILE_OPEN or FILE_OVERWRITE
Return Value:
Win error status.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsForceOpenId:"
HANDLE AttrHandle;
ULONG WStatus, WStatus1;
ULONG AttrWStatus;
FILE_NETWORK_OPEN_INFORMATION FileNetworkOpenInfo;
DPRINT2(5, "++ Attempting to force open Id %08x %08x (%d bytes)\n",
PRINTQUAD((*(PULONGLONG)Id)), IdLen);
//
// Open the file
//
WStatus = FrsOpenSourceFileById(Handle,
NULL,
OpLock,
pVme->VolumeHandle,
Id,
IdLen,
DesiredAccess,
CreateOptions,
ShareMode,
CreateDisposition);
//
// File has been opened successfully
//
if (WIN_SUCCESS(WStatus)) {
DPRINT2(5, "++ Successfully opened Id %08x %08x (%d)\n",
PRINTQUAD((*(PULONGLONG)Id)), IdLen);
return WStatus;
}
//
// File has been deleted; done
//
if (WIN_NOT_FOUND(WStatus)) {
DPRINT2(4, "++ Id %08x %08x (%d) not found\n",
PRINTQUAD((*(PULONGLONG)Id)), IdLen);
return WStatus;
}
//
// Not an attribute problem
//
if (!WIN_ACCESS_DENIED(WStatus)) {
DPRINT2_WS(4, "++ Open Id %08x %08x (%d) failed;",
PRINTQUAD((*(PULONGLONG)Id)), IdLen, WStatus);
return WStatus;
}
//
// Attempt to reset attributes (e.g., reset readonly)
//
AttrWStatus = FrsOpenSourceFileById(&AttrHandle,
&FileNetworkOpenInfo,
NULL,
pVme->VolumeHandle,
Id,
IdLen,
// READ_ACCESS | FILE_WRITE_ATTRIBUTES,
// STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | ACCESS_SYSTEM_SECURITY | SYNCHRONIZE,
READ_ATTRIB_ACCESS | WRITE_ATTRIB_ACCESS,
CreateOptions,
SHARE_ALL,
FILE_OPEN);
//
// Couldn't open the file for write-attribute access
//
if (!WIN_SUCCESS(AttrWStatus)) {
DPRINT2_WS(4, "++ Open Id %08x %08x (%d) for minimal access failed;",
PRINTQUAD((*(PULONGLONG)Id)), IdLen, WStatus);
return WStatus;
}
//
// Handles can be marked so that any usn records resulting from
// operations on the handle will have the same "mark". In this
// case, the mark is a bit in the SourceInfo field of the usn
// record. The mark tells NtFrs to ignore the usn record during
// recovery because this was a NtFrs generated change.
//
WStatus1 = FrsMarkHandle(pVme->VolumeHandle, AttrHandle);
DPRINT1_WS(0, "++ WARN - FrsMarkHandle(%08x %08x);",
PRINTQUAD((*(PULONGLONG)Id)), WStatus1);
//
// The file's attributes are not preventing the open; done
//
if (!(FileNetworkOpenInfo.FileAttributes & NOREPL_ATTRIBUTES)) {
DPRINT2_WS(4, "++ Id %08x %08x (%d)attributes not preventing open;",
PRINTQUAD((*(PULONGLONG)Id)), IdLen, WStatus);
FRS_CLOSE(AttrHandle);
return WStatus;
}
//
// Reset the attributes
//
WStatus1 = FrsSetFileAttributes(L"<unknown>",
AttrHandle,
FileNetworkOpenInfo.FileAttributes &
~NOREPL_ATTRIBUTES);
if (!WIN_SUCCESS(WStatus1)) {
DPRINT2_WS(4, "++ Can't reset attributes for Id %08x %08x (%d);",
PRINTQUAD((*(PULONGLONG)Id)), IdLen, WStatus1);
FRS_CLOSE(AttrHandle);
return WStatus1;
}
//
// Try to open the file again
//
WStatus = FrsOpenSourceFileById(Handle,
NULL,
NULL,
pVme->VolumeHandle,
Id,
IdLen,
DesiredAccess,
CreateOptions,
SHARE_ALL,
CreateDisposition);
//
// Reset the original attributes
//
WStatus1 = FrsSetFileAttributes(L"<unknown>",
AttrHandle,
FileNetworkOpenInfo.FileAttributes);
if (!WIN_SUCCESS(WStatus1)) {
DPRINT2_WS(0, "++ ERROR - Can't set attributes for Id %08x %08x (%d);",
PRINTQUAD((*(PULONGLONG)Id)), IdLen, WStatus1);
}
//
// Close the handle that we used to set and reset attributes
//
FRS_CLOSE(AttrHandle);
DPRINT3(4, "++ Force open %08x %08x (%d) %s WITH SHARE ALL!\n",
PRINTQUAD((*(PULONGLONG)Id)), IdLen,
WIN_SUCCESS(WStatus) ? "Succeeded" : "Failed");
return (WStatus);
}
DWORD
FrsOpenSourceFileById(
OUT PHANDLE Handle,
OUT PFILE_NETWORK_OPEN_INFORMATION FileOpenInfo,
OUT OVERLAPPED *OpLock,
IN HANDLE VolumeHandle,
IN PVOID ObjectId,
IN ULONG Length,
IN ACCESS_MASK DesiredAccess,
IN ULONG CreateOptions,
IN ULONG ShareMode,
IN ULONG CreateDisposition
)
/*++
Routine Description:
This function opens the specified file by File ID or Object ID.
If the length is 8 perform a relative open using the file ID and the
volume handle passed in the VolumeHandle arg. If the length is 16
perform an object ID relative open using the volume handle.
Arguments:
Handle - Returns the file handle.
FileOpenInfo - If non-NULL, returns the FILE_NETWORK_OPEN_INFORMATION data.
OpLock - If non-NULL, the caller desires an oplock
VolumeHandle - The handle for a FileID based relative open.
ObjectId - Represents the name of the file or directory to be opened.
Length - 8 for file IDs and 16 for object IDs.
DesiredAccess - see replutil.h for defined access modes (xxx_ACCESS)
CreateOptions - see replutil.h for defined options (xxx_OPTIONS
ShareMode - standard share modes defined in sdk
CreateDisposition - E.g., FILE_OPEN or FILE_OVERWRITE
Return Value:
Win error status.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsOpenSourceFileById:"
ULONG Ignored;
NTSTATUS NtStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING str;
FRS_ASSERT(HANDLE_IS_VALID(VolumeHandle));
FRS_ASSERT(Length == OBJECT_ID_LENGTH || Length == FILE_ID_LENGTH);
*Handle = INVALID_HANDLE_VALUE;
//
// Object attributes (e.g., the file's fid or oid
//
str.Length = (USHORT)Length;
str.MaximumLength = (USHORT)Length;
str.Buffer = ObjectId;
InitializeObjectAttributes(&ObjectAttributes,
&str,
OBJ_CASE_INSENSITIVE,
VolumeHandle,
NULL);
//
// Optional oplock
//
if (OpLock != NULL) {
ZeroMemory(OpLock, sizeof(OVERLAPPED));
OpLock->hEvent = FrsCreateEvent(TRUE, FALSE);
CreateOptions &= ~FILE_SYNCHRONOUS_IO_NONALERT;
}
NtStatus = NtCreateFile(Handle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
ShareMode,
CreateDisposition,
CreateOptions,
NULL,
0);
//
// Apply oplock if requested
//
if (NT_SUCCESS(NtStatus) && OpLock) {
if (!DeviceIoControl(*Handle,
FSCTL_REQUEST_FILTER_OPLOCK,
NULL,
0,
NULL,
0,
&Ignored,
OpLock)) {
if (GetLastError() != ERROR_IO_PENDING) {
DPRINT_WS(3, "++ WARN: Can't get oplock;", GetLastError());
//
// Cleanup the handles
//
FRS_CLOSE(OpLock->hEvent);
}
}
}
//
// Report status
//
ProcessOpenByIdStatus(*Handle, NtStatus, ObjectId, Length);
//
// Couldn't open; return status
//
if (!NT_SUCCESS(NtStatus) ) {
*Handle = INVALID_HANDLE_VALUE;
DPRINT_NT(0, "++ ERROR - NtCreateFile failed :", NtStatus);
return FrsSetLastNTError(NtStatus);
}
//
// Return some file info and the file handle.
//
if (FileOpenInfo) {
NtStatus = NtQueryInformationFile(*Handle,
&IoStatusBlock,
FileOpenInfo,
sizeof(FILE_NETWORK_OPEN_INFORMATION),
FileNetworkOpenInformation);
if (!NT_SUCCESS(NtStatus) ) {
//
// Cleanup the handles
//
DPRINT_NT(0, "++ NtQueryInformationFile - FileNetworkOpenInformation failed:", NtStatus);
FRS_CLOSE(*Handle);
if (OpLock != NULL) {
FRS_CLOSE(OpLock->hEvent);
}
return FrsSetLastNTError(NtStatus);
}
}
return FrsSetLastNTError(NtStatus);
}
DWORD
FrsCreateFileRelativeById(
OUT PHANDLE Handle,
IN HANDLE VolumeHandle,
IN PVOID ParentObjectId,
IN ULONG OidLength,
IN ULONG FileCreateAttributes,
IN PWCHAR BaseFileName,
IN USHORT FileNameLen,
IN PLARGE_INTEGER AllocationSize,
IN ULONG CreateDisposition,
IN ACCESS_MASK DesiredAccess
)
/*++
Routine Description:
This function creates a new file in the directory specified by the parent
file object ID. It does a replative open of the parent using the volume
handle provided. Then it does a relative open of the target file using
the parent handle and the file name.
If the length is 8 perform a relative open using the file ID and the
volume handle passed in the VolumeHandle arg. If the length is 16
perform an object ID relative open using the volume handle.
The file attributes parameter is used to decide if the create is a
file or a directory.
Arguments:
Handle - Returns the file handle.
VolumeHandle - The handle for a ID based relative open.
ParentObjectId - The object or file id of the parent directory. If NULL
open the file relative to the Volume Handle.
OidLength - 8 for file IDs and 16 for object IDs. (len of parent oid)
FileCreateAttributes - Initial File Create Attributes
BaseFileName - ptr to NULL terminated file name
FileNameLen - File name length (not incl the null) in bytes.
AllocationSize - The allocation size for the file.
CreateDisposition - E.g., FILE_CREATE or FILE_OPEN
DesiredAccess - Access rights
Return Value:
WIN32 error status. Use GetLastError() to get the win32 error code.
If the file already exsists the Win32 error return is ERROR_ALREADY_EXISTS.
The NT error status is STATUS_OBJECT_NAME_COLLISION.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCreateFileRelativeById:"
UNICODE_STRING UStr;
DWORD WStatus;
NTSTATUS NtStatus;
NTSTATUS NtStatus2;
HANDLE File, DirHandle;
ULONG ShareMode;
ULONG CreateOptions;
ULONG EaSize;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION EaBuffer;
PFILE_NAME_INFORMATION FileName;
CHAR GuidStr[GUID_CHAR_LEN];
CHAR NameBuffer[sizeof(ULONG) + (sizeof(WCHAR)*(MAX_PATH+1))];
*Handle = INVALID_HANDLE_VALUE;
//
// Open the parent directory using the object ID provided.
//
if (ParentObjectId != NULL) {
WStatus = FrsOpenSourceFileById(&DirHandle,
NULL,
NULL,
VolumeHandle,
ParentObjectId,
OidLength,
// READ_ACCESS,
READ_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
ID_OPTIONS,
SHARE_ALL,
FILE_OPEN);
if (!WIN_SUCCESS(WStatus)) {
DPRINT_WS(1, "++ ERROR - Open on parent dir failed;", WStatus);
return WStatus;
}
} else {
DirHandle = VolumeHandle;
OidLength = 0;
}
//
// Create the target file.
//
FrsSetUnicodeStringFromRawString(&UStr, FileNameLen, BaseFileName, FileNameLen);
InitializeObjectAttributes( &ObjectAttributes,
&UStr,
OBJ_CASE_INSENSITIVE,
DirHandle,
NULL );
//
// Mask off the junk that may have come in from the journal
//
FileCreateAttributes &= FILE_ATTRIBUTE_VALID_FLAGS;
//
// Set create options depending on file or dir.
//
CreateOptions = FILE_OPEN_FOR_BACKUP_INTENT // FILE_FLAG_BACKUP_SEMANTICS
| FILE_OPEN_REPARSE_POINT
| FILE_OPEN_NO_RECALL // Don't migrate data for HSM
| FILE_SEQUENTIAL_ONLY
| FILE_SYNCHRONOUS_IO_NONALERT;
if (CreateDisposition == FILE_CREATE || CreateDisposition == FILE_OPEN_IF) {
if (FileCreateAttributes & FILE_ATTRIBUTE_DIRECTORY) {
CreateOptions |= FILE_DIRECTORY_FILE;
CreateOptions &= ~(FILE_SEQUENTIAL_ONLY | FILE_OPEN_NO_RECALL);
} else {
CreateOptions |= FILE_NON_DIRECTORY_FILE;
}
}
EaBuffer = NULL;
EaSize = 0;
// ShareMode = 0; // no sharing
//
// Fix for Bug 186880
//
ShareMode = FILE_SHARE_READ; // share for read.
//
// Do the relative open.
//
DPRINT1(5, "++ DesiredAccess: %08x\n", DesiredAccess);
if (AllocationSize != NULL) {
DPRINT2(5, "++ AllocationSize: %08x %08x\n", AllocationSize->HighPart, AllocationSize->LowPart);
}
DPRINT1(5, "++ FileCreateAttributes: %08x\n", FileCreateAttributes);
DPRINT1(5, "++ ShareMode: %08x\n", ShareMode);
DPRINT1(5, "++ CreateDisposition: %08x\n", CreateDisposition);
DPRINT1(5, "++ CreateOptions: %08x\n", CreateOptions);
if (OidLength == 16) {
GuidToStr((GUID *) ParentObjectId, GuidStr);
DPRINT1(5, "++ Parent ObjectId: %s\n", GuidStr);
}
NtStatus = NtCreateFile(&File,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
AllocationSize, // Initial allocation size
FileCreateAttributes,
ShareMode,
CreateDisposition,
CreateOptions,
EaBuffer,
EaSize);
if (ParentObjectId != NULL) {
FRS_CLOSE(DirHandle);
}
if (!NT_SUCCESS(NtStatus)) {
DPRINT1_NT(5, "++ ERROR - CreateFile failed on %ws.", BaseFileName, NtStatus);
DPRINT1(5, "++ DesiredAccess: %08x\n", DesiredAccess);
if (AllocationSize != NULL) {
DPRINT2(5, "++ AllocationSize: %08x %08x\n", AllocationSize->HighPart, AllocationSize->LowPart);
}
DPRINT1(5, "++ FileCreateAttributes: %08x\n", FileCreateAttributes);
DPRINT1(5, "++ ShareMode: %08x\n", ShareMode);
DPRINT1(5, "++ CreateDisposition: %08x\n", CreateDisposition);
DPRINT1(5, "++ CreateOptions: %08x\n", CreateOptions);
if (OidLength == 16) {
GuidToStr((GUID *) ParentObjectId, GuidStr);
DPRINT1(5, "++ Parent ObjectId: %s\n", GuidStr);
}
if (NtStatus == STATUS_INVALID_PARAMETER) {
DPRINT(5, "++ Invalid parameter on open by ID likely means file not found.\n");
return ERROR_FILE_NOT_FOUND;
}
return FrsSetLastNTError(NtStatus);
}
if (DoDebug(5, DEBSUB)) {
FileName = (PFILE_NAME_INFORMATION) &NameBuffer[0];
FileName->FileNameLength = sizeof(NameBuffer) - sizeof(ULONG);
NtStatus2 = NtQueryInformationFile(File,
&IoStatusBlock,
FileName,
sizeof(NameBuffer),
FileNameInformation );
if (!NT_SUCCESS(NtStatus2)) {
DPRINT_NT(1, "++ NtQueryInformationFile - FileNameInformation failed:",
NtStatus2);
} else {
FileName->FileName[FileName->FileNameLength/2] = UNICODE_NULL;
DPRINT1(5, "++ Name of created file is: %ws\n", FileName->FileName); //
}
}
//
// Return the file handle.
//
*Handle = File;
return FrsSetLastNTError(NtStatus);
}
DWORD
FrsDeleteFile(
IN PWCHAR Name
)
/*++
Routine Description:
Delete the file
Arguments:
Name
Return Value:
WStatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteFile:"
DWORD WStatus = ERROR_SUCCESS;
//
// Delete file
//
DPRINT1(4, "++ Deleting %ws\n", Name);
if (!DeleteFile(Name)) {
WStatus = GetLastError();
if (WStatus != ERROR_FILE_NOT_FOUND &&
WStatus != ERROR_PATH_NOT_FOUND) {
DPRINT1_WS(0, "++ Can't delete file %ws;", Name, WStatus);
} else {
WStatus = ERROR_SUCCESS;
}
}
return WStatus;
}
DWORD
FrsCreateDirectory(
IN PWCHAR Name
)
/*++
Routine Description:
Create a directory
Arguments:
Name
Return Value:
Win32 Error Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCreateDirectory:"
ULONG WStatus;
//
// Create the directory
//
if (!CreateDirectory(Name, NULL)) {
WStatus = GetLastError();
if (!WIN_ALREADY_EXISTS(WStatus)) {
DPRINT1_WS(0, "Can't create directory %ws;", Name, WStatus);
return WStatus;
}
}
return ERROR_SUCCESS;
}
DWORD
FrsVerifyVolume(
IN PWCHAR Path,
IN PWCHAR SetName,
IN ULONG Flags
)
/*++
Routine Description:
Does the volume exist and is it NTFS? If not generate an event log entry
and return non success.
Arguments:
Path -- A path string with a volume component.
SetName -- the Replica set name for event log messages.
Flags -- The file system flags that must be set. The currently valid
set are:
FILE_CASE_SENSITIVE_SEARCH
FILE_CASE_PRESERVED_NAMES
FILE_UNICODE_ON_DISK
FILE_PERSISTENT_ACLS
FILE_FILE_COMPRESSION
FILE_VOLUME_QUOTAS
FILE_SUPPORTS_SPARSE_FILES
FILE_SUPPORTS_REPARSE_POINTS
FILE_SUPPORTS_REMOTE_STORAGE
FILE_VOLUME_IS_COMPRESSED
FILE_SUPPORTS_OBJECT_IDS
FILE_SUPPORTS_ENCRYPTION
FILE_NAMED_STREAMS
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsVerifyVolume:"
DWORD WStatus = ERROR_SUCCESS;
PWCHAR VolumeName = NULL;
ULONG FsAttributeInfoLength;
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo = NULL;
HANDLE PathHandle = INVALID_HANDLE_VALUE;
if ((Path == NULL) || (wcslen(Path) == 0)) {
WStatus = ERROR_INVALID_PARAMETER;
goto RETURN;
}
//
// Always open the path by masking off the FILE_OPEN_REPARSE_POINT flag
// because we want to open the destination dir not the junction if the root
// happens to be a mount point.
//
WStatus = FrsOpenSourceFileW(&PathHandle,
Path,
GENERIC_READ,
FILE_OPEN_FOR_BACKUP_INTENT);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(0, "ERROR - Unable to open root path %ws. Retry at next poll.",
Path, WStatus);
goto RETURN;
}
//
// Get the volume information.
//
FsAttributeInfoLength = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
MAXIMUM_VOLUME_LABEL_LENGTH;
FsAttributeInfo = FrsAlloc(FsAttributeInfoLength);
Status = NtQueryVolumeInformationFile(PathHandle,
&Iosb,
FsAttributeInfo,
FsAttributeInfoLength,
FileFsAttributeInformation);
if (!NT_SUCCESS(Status)) {
DPRINT2(0,"ERROR - Getting NtQueryVolumeInformationFile for %ws. NtStatus = %08x\n",
Path, Status);
goto RETURN;
}
if ((FsAttributeInfo->FileSystemAttributes & Flags) != Flags) {
DPRINT3(0, "++ Error - Required filesystem not present for %ws. Needed %08x, Found %08x\n",
Path, Flags, FsAttributeInfo->FileSystemAttributes);
WStatus = ERROR_INVALID_PARAMETER;
goto RETURN;
}
WStatus = ERROR_SUCCESS;
RETURN:
if (!WIN_SUCCESS(WStatus)) {
if (WStatus == ERROR_INVALID_PARAMETER) {
//
// Generate an event log message.
//
VolumeName = FrsWcsVolume(Path);
EPRINT4(EVENT_FRS_VOLUME_NOT_SUPPORTED,
SetName,
ComputerName,
((Path == NULL) ? L"<null>" : Path),
((VolumeName == NULL) ? L"<null>" : VolumeName));
} else {
//
// The Path is probably not accessible.
//
EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Path);
}
}
FrsFree(FsAttributeInfo);
FRS_CLOSE(PathHandle);
FrsFree(VolumeName);
return WStatus;
}
DWORD
FrsCheckForNoReparsePoint(
IN PWCHAR Name
)
/*++
Routine Description:
Does the path reside on the same volume at the prefix drive name?
It won't exist on the same volume if any element of the path
is a reparse point pointing to a directory on another volume.
Arguments:
Name
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCheckForNoReparsePoint:"
DWORD WStatus;
NTSTATUS Status;
PWCHAR Volume = NULL;
PWCHAR Temp = NULL;
HANDLE FileHandlePath;
HANDLE FileHandleDrive;
IO_STATUS_BLOCK Iosb;
PFILE_FS_VOLUME_INFORMATION VolumeInfoPath = NULL;
PFILE_FS_VOLUME_INFORMATION VolumeInfoDrive = NULL;
ULONG VolumeInfoLength;
if (!Name) {
return ERROR_INVALID_PARAMETER;
}
//
// Get the handle to the path passed in.
//
WStatus = FrsOpenSourceFileW(&FileHandlePath,
Name,
// READ_ACCESS,
READ_ATTRIB_ACCESS,
OPEN_OPTIONS & ~FILE_OPEN_REPARSE_POINT);
CLEANUP1_WS(4, "++ ERROR - FrsOpenSourceFile(%ws);", Name, WStatus, CLEANUP);
//
// Get the volume information for this handle.
//
VolumeInfoPath = FrsAlloc(sizeof(FILE_FS_VOLUME_INFORMATION) + MAXIMUM_VOLUME_LABEL_LENGTH);
VolumeInfoLength = sizeof(*VolumeInfoPath) + MAXIMUM_VOLUME_LABEL_LENGTH;
Status = NtQueryVolumeInformationFile(FileHandlePath,
&Iosb,
VolumeInfoPath,
VolumeInfoLength,
FileFsVolumeInformation);
NtClose(FileHandlePath);
if (!NT_SUCCESS(Status)) {
WStatus = FrsSetLastNTError(Status);
CLEANUP1_NT(4, "++ ERROR - NtQueryVolumeInformationFile(%ws);",
Name, Status, CLEANUP);
}
//
// Get the volume part of the absolute path.
//
Temp = FrsWcsVolume(Name);
if (!Temp || (wcslen(Temp) == 0)) {
WStatus = ERROR_FILE_NOT_FOUND;
goto CLEANUP;
}
Volume = FrsWcsCat(Temp, L"\\");
//
// Get the handle to the prefix drive of the path passed in.
//
WStatus = FrsOpenSourceFileW(&FileHandleDrive, Volume,
// READ_ACCESS,
READ_ATTRIB_ACCESS,
OPEN_OPTIONS);
CLEANUP1_WS(4, "++ ERROR - opening volume %ws ;", Volume, WStatus, CLEANUP);
//
// Get the volume information for this handle.
//
VolumeInfoDrive = FrsAlloc(sizeof(FILE_FS_VOLUME_INFORMATION) + MAXIMUM_VOLUME_LABEL_LENGTH);
VolumeInfoLength = sizeof(*VolumeInfoDrive) + MAXIMUM_VOLUME_LABEL_LENGTH;
Status = NtQueryVolumeInformationFile(FileHandleDrive,
&Iosb,
VolumeInfoDrive,
VolumeInfoLength,
FileFsVolumeInformation);
NtClose(FileHandleDrive);
if (!NT_SUCCESS(Status)) {
WStatus = FrsSetLastNTError(Status);
CLEANUP1_NT(4, "++ ERROR - NtQueryVolumeInformationFile(%ws);",
Volume, Status, CLEANUP);
}
//
// Now compare the VolumeSerialNumber acquired from the above two queries.
// If it is the same then there are no reparse points in the path that
// redirect the path to a different volume.
//
if (VolumeInfoPath->VolumeSerialNumber != VolumeInfoDrive->VolumeSerialNumber) {
WStatus = ERROR_GEN_FAILURE;
DPRINT2(0, "++ Error - VolumeSerialNumber mismatch %x != %x\n",
VolumeInfoPath->VolumeSerialNumber,
VolumeInfoDrive->VolumeSerialNumber);
DPRINT2(0, "++ Error - Root path (%ws) is not on %ws. Invalid replica root path.\n",
Name,Volume);
goto CLEANUP;
}
CLEANUP:
//
// Cleanup
//
FrsFree(VolumeInfoPath);
FrsFree(Volume);
FrsFree(Temp);
FrsFree(VolumeInfoDrive);
return WStatus;
}
DWORD
FrsDoesDirectoryExist(
IN PWCHAR Name,
OUT PDWORD pAttributes
)
/*++
Routine Description:
Does the directory Name exist?
Arguments:
Name
pAttributes - return the attributes on the file/dir if it exits.
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDoesDirectoryExist:"
DWORD WStatus;
//
// Can't get attributes
//
*pAttributes = GetFileAttributes(Name);
if (*pAttributes == 0xFFFFFFFF) {
WStatus = GetLastError();
DPRINT1_WS(4, "++ GetFileAttributes(%ws); ", Name, WStatus);
return WStatus;
}
//
// Not a directory
//
if (!(*pAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
DPRINT1(4, "++ %ws is not a directory\n", Name);
return ERROR_DIRECTORY;
}
return ERROR_SUCCESS;
}
DWORD
FrsDoesFileExist(
IN PWCHAR Name
)
/*++
Routine Description:
Does the file Name exist?
Arguments:
Name
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDoesFileExist:"
DWORD WStatus;
DWORD Attributes;
//
// Can't get attributes
//
Attributes = GetFileAttributes(Name);
if (Attributes == 0xFFFFFFFF) {
WStatus = GetLastError();
DPRINT1_WS(4, "++ GetFileAttributes(%ws); ", Name, WStatus);
return WStatus;
}
//
// Not a directory
//
if (Attributes & FILE_ATTRIBUTE_DIRECTORY) {
DPRINT1(4, "++ %ws is not a file\n", Name);
return ERROR_DIRECTORY;
}
return ERROR_SUCCESS;
}
DWORD
FrsSetFilePointer(
IN PWCHAR Name,
IN HANDLE Handle,
IN ULONG High,
IN ULONG Low
)
/*++
Routine Description:
Position file pointer
Arguments:
Handle
Name
High
Low
Return Value:
Win32 Error Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetFilePointer:"
DWORD WStatus = ERROR_SUCCESS;
Low = SetFilePointer(Handle, Low, &High, FILE_BEGIN);
if (Low == INVALID_SET_FILE_POINTER) {
WStatus = GetLastError();
if (WStatus != NO_ERROR) {
DPRINT1_WS(0, "++ Can't set file pointer for %ws;", Name, WStatus);
}
}
return WStatus;
}
DWORD
FrsSetFileTime(
IN PWCHAR Name,
IN HANDLE Handle,
IN FILETIME *CreateTime,
IN FILETIME *AccessTime,
IN FILETIME *WriteTime
)
/*++
Routine Description:
Position file pointer
Arguments:
Name
Handle
Attributes
CreateTime
AccessTime
WriteTime
Return Value:
WStatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetFileTime:"
DWORD WStatus = ERROR_SUCCESS;
if (!SetFileTime(Handle, CreateTime, AccessTime, WriteTime)) {
WStatus = GetLastError();
DPRINT1_WS(0, "++ Can't set file times for %ws;", Name, WStatus);
}
return WStatus;
}
DWORD
FrsSetEndOfFile(
IN PWCHAR Name,
IN HANDLE Handle
)
/*++
Routine Description:
Set end of file at current file position
Arguments:
Handle
Name
Return Value:
WStatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetEndOfFile:"
DWORD WStatus = ERROR_SUCCESS;
if (!SetEndOfFile(Handle)) {
WStatus = GetLastError();
DPRINT1_WS(0, "++ ERROR - Setting EOF for %ws;", Name, WStatus);
}
return WStatus;
}
DWORD
FrsFlushFile(
IN PWCHAR Name,
IN HANDLE Handle
)
/*++
Routine Description:
Flush the file's data to disk
Arguments:
Handle
Name
Return Value:
WStatus
--*/
{
#undef DEBSUB
#define DEBSUB "FrsFlushFile:"
DWORD WStatus = ERROR_SUCCESS;
if (HANDLE_IS_VALID(Handle)) {
if (!FlushFileBuffers(Handle)) {
WStatus = GetLastError();
DPRINT1_WS(0, "++ Can't flush file for %ws;", Name, WStatus);
}
}
return WStatus;
}
DWORD
FrsSetCompression(
IN PWCHAR Name,
IN HANDLE Handle,
IN USHORT TypeOfCompression
)
/*++
Routine Description:
Enable compression on Handle.
Arguments:
Name
Handle
TypeOfCompression
Return Value:
WStatus
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetCompression:"
DWORD BytesReturned;
DWORD WStatus = ERROR_SUCCESS;
if (!DeviceIoControl(Handle,
FSCTL_SET_COMPRESSION,
&TypeOfCompression, sizeof(TypeOfCompression),
NULL, 0, &BytesReturned, NULL)) {
WStatus = GetLastError();
DPRINT1_WS(0, "++ Can't set compression on %ws;", Name, WStatus);
}
return WStatus;
}
DWORD
FrsGetCompression(
IN PWCHAR Name,
IN HANDLE Handle,
IN PUSHORT TypeOfCompression
)
/*++
Routine Description:
Enable compression on Handle.
Arguments:
Handle
Name
TypeOfCompression
Return Value:
WStatus
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetCompression:"
DWORD BytesReturned;
DWORD WStatus = ERROR_SUCCESS;
if (!DeviceIoControl(Handle,
FSCTL_GET_COMPRESSION,
NULL, 0,
TypeOfCompression, sizeof(TypeOfCompression),
&BytesReturned, NULL)) {
WStatus = GetLastError();
DPRINT1_WS(0, "++ Can't get compression for %ws;", Name, WStatus);
}
return WStatus;
}
DWORD
FrsRenameByHandle(
IN PWCHAR Name,
IN ULONG NameLen,
IN HANDLE Handle,
IN HANDLE TargetHandle,
IN BOOL ReplaceIfExists
)
/*++
Routine Description:
Rename the file
Arguments:
Name - New name
NameLen - length of Name
Handle - file handle
TargetHandle - Target directory
ReplaceIfExists
Return Value:
Win Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRenameByHandle:"
PFILE_RENAME_INFORMATION RenameInfo;
IO_STATUS_BLOCK IoStatus;
ULONG NtStatus;
//
// Rename the file; deleting any destination file if requested
//
RenameInfo = FrsAlloc(sizeof(FILE_RENAME_INFORMATION) + NameLen);
RenameInfo->ReplaceIfExists = (ReplaceIfExists != 0);
RenameInfo->RootDirectory = TargetHandle;
RenameInfo->FileNameLength = NameLen;
CopyMemory(RenameInfo->FileName, Name, NameLen);
NtStatus = NtSetInformationFile(Handle,
&IoStatus,
RenameInfo,
sizeof(FILE_RENAME_INFORMATION)
+ NameLen,
FileRenameInformation);
FrsFree(RenameInfo);
DPRINT1_NT(5, "++ INFO - Renaming %ws failed; ", Name, NtStatus);
return FrsSetLastNTError(NtStatus);
}
DWORD
FrsCheckObjectId(
IN PWCHAR Name,
IN HANDLE Handle,
IN GUID *Guid
)
/*++
Routine Description:
Check that the GUID on the file is the same.
Arguments:
Name - for error messages
Handle - Supplies a handle to the file
Guid - guid to check
Return Value:
Win Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCheckObjectId:"
DWORD WStatus;
FILE_OBJECTID_BUFFER ObjectIdBuffer;
//
// Get the file's object id and check it
//
WStatus = FrsGetObjectId(Handle, &ObjectIdBuffer);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(4, "++ No object id for file %ws;", Name, WStatus);
} else {
//
// Same file, no morph needed. (must have been renamed sometime before)
//
if (memcmp(ObjectIdBuffer.ObjectId, Guid, sizeof(GUID))) {
DPRINT1(4, "++ Object ids don't match for file %ws\n", Name);
WStatus = ERROR_FILE_NOT_FOUND;
}
}
return WStatus;
}
PWCHAR
FrsCreateGuidName(
IN GUID *Guid,
IN PWCHAR Prefix
)
/*++
Routine Description:
Convert the guid into a file name
Arguments:
Guid
Prefix
Return Value:
Character string
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCreateGuidName:"
WCHAR WGuid[GUID_CHAR_LEN + 1];
//
// Translate the guid into a string
//
GuidToStrW(Guid, WGuid);
//
// Create the file name <prefix>Guid
//
return FrsWcsCat(Prefix, WGuid);
}
DWORD
FrsGetObjectId(
IN HANDLE Handle,
OUT PFILE_OBJECTID_BUFFER ObjectIdBuffer
)
/*++
Routine Description:
This routine reads the object ID.
Arguments:
Handle -- The file handle of an opened file.
ObjectIdBuffer -- The output buffer to hold the object ID.
Return Value:
Returns the Win Status of the last error found, or success.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetObjectId:"
NTSTATUS NtStatus;
IO_STATUS_BLOCK Iosb;
CHAR GuidStr[GUID_CHAR_LEN];
//
// zero the buffer in case the data that comes back is short.
//
ZeroMemory(ObjectIdBuffer, sizeof(FILE_OBJECTID_BUFFER));
//
// Get the Object ID
//
NtStatus = NtFsControlFile(Handle, // file handle
NULL, // event
NULL, // apc routine
NULL, // apc context
&Iosb, // iosb
FSCTL_GET_OBJECT_ID, // FsControlCode
&Handle, // input buffer
sizeof(HANDLE), // input buffer length
ObjectIdBuffer, // OutputBuffer for data from the FS
sizeof(FILE_OBJECTID_BUFFER)); // OutputBuffer Length
if (NT_SUCCESS(NtStatus)) {
GuidToStr((GUID *)ObjectIdBuffer->ObjectId, GuidStr);
DPRINT1(4, "++ Existing oid for this file is %s\n", GuidStr );
}
return FrsSetLastNTError(NtStatus);
}
DWORD
FrsGetOrSetFileObjectId(
IN HANDLE Handle,
IN LPCWSTR FileName,
IN BOOL CallerSupplied,
OUT PFILE_OBJECTID_BUFFER ObjectIdBuffer
)
/*++
Routine Description:
This routine reads the object ID. If there is no
object ID on the file we put one on it. If the CallerSupplied flag is
TRUE then we delete the current object ID on the file (if any) and
stamp the object ID provided on the file.
Note: This function does not preserve the 48 byte extended info in the
object ID. Currently this is not a problem but could be a link tracking
issue in the future.
Arguments:
Handle -- The file handle of an opened file.
FileName -- The name of the file. For error messages only.
CallerSupplied -- TRUE if caller supplies new OID to override ANY
OID currently on the file.
ObjectIdBuffer -- The output buffer to hold the object ID.
Return Value:
Returns the Win Status of the last error found, or success.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetOrSetFileObjectId:"
DWORD WStatus = ERROR_SUCCESS;
NTSTATUS NtStatus;
ULONG ObjectIdBufferSize;
IO_STATUS_BLOCK Iosb;
CHAR GuidStr[GUID_CHAR_LEN];
LONG Loop;
ObjectIdBufferSize = sizeof(FILE_OBJECTID_BUFFER);
if (!CallerSupplied) {
WStatus = FrsGetObjectId(Handle, ObjectIdBuffer);
if (WIN_SUCCESS(WStatus)) {
return WStatus;
}
//
// Clear the extra bits past the object id
//
ZeroMemory(ObjectIdBuffer, sizeof(FILE_OBJECTID_BUFFER));
}
if (WIN_OID_NOT_PRESENT(WStatus) || CallerSupplied) {
//
// No object ID on the file. Create one. Just in case, try 15 times
// to get a unique one. Don't let the kernel create the object ID
// using FSCTL_CREATE_OR_GET_OBJECT_ID since it currently (April 97)
// doesn't add the net card address.
//
Loop = 0;
do {
if (!CallerSupplied) {
FrsUuidCreate((GUID *)ObjectIdBuffer->ObjectId);
}
if (Loop > 0) {
DPRINT2(1, "++ Failed to assign Object ID %s (dup_name, retrying) to the file: %ws\n",
GuidStr, FileName);
}
GuidToStr((GUID *)ObjectIdBuffer->ObjectId, GuidStr);
//
// If this object ID is caller supplied then there might already
// be one on the file so delete it first.
//
NtStatus = NtFsControlFile(
Handle, // file handle
NULL, // event
NULL, // apc routine
NULL, // apc context
&Iosb, // iosb
FSCTL_DELETE_OBJECT_ID, // FsControlCode
NULL, // input buffer
0, // input buffer length
NULL, // OutputBuffer for data from the FS
0); // OutputBuffer Length
NtStatus = NtFsControlFile(
Handle, // file handle
NULL, // event
NULL, // apc routine
NULL, // apc context
&Iosb, // iosb
FSCTL_SET_OBJECT_ID, // FsControlCode
ObjectIdBuffer, // input buffer
ObjectIdBufferSize, // input buffer length
NULL, // OutputBuffer for data from the FS
0); // OutputBuffer Length
} while ((NtStatus == STATUS_DUPLICATE_NAME) &&
(++Loop < 16) &&
(!CallerSupplied));
if (!NT_SUCCESS(NtStatus)) {
DPRINT1_NT(1, "++ ERROR - Set oid failed on file %ws;", FileName, NtStatus);
} else {
GuidToStr((GUID *)ObjectIdBuffer->ObjectId, GuidStr);
DPRINT2(4, "++ Assigned Object ID %s (success) to the file: %ws\n",
GuidStr, FileName);
}
return FrsSetLastNTError(NtStatus);
}
return WStatus;
}
DWORD
FrsDeleteFileObjectId(
IN HANDLE Handle,
IN LPCWSTR FileName
)
/*++
Routine Description:
Delete object id (if it exists)
Arguments:
Handle -- The file handle of an opened file.
FileName -- The name of the file. For error messages only.
Return Value:
Returns the Win Status of the last error found, or success.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteFileObjectId:"
NTSTATUS NtStatus;
DWORD WStatus;
IO_STATUS_BLOCK Iosb;
//
// Remove the object id from the file
//
NtStatus = NtFsControlFile(Handle, // file handle
NULL, // event
NULL, // apc routine
NULL, // apc context
&Iosb, // iosb
FSCTL_DELETE_OBJECT_ID, // FsControlCode
NULL, // input buffer
0, // input buffer length
NULL, // OutputBuffer for data from the FS
0); // OutputBuffer Length
WStatus = FrsSetLastNTError(NtStatus);
if (WIN_NOT_IMPLEMENTED(WStatus)) {
DPRINT1_WS(0, "++ Could not delete object id for %ws (not implemented);", FileName, WStatus);
} else
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(0, "++ Could not delete object id for %ws;", FileName, WStatus);
} else {
DPRINT1(4, "++ Deleted object id from %ws.\n", FileName);
}
return WStatus;
}
DWORD
FrsReadFileUsnData(
IN HANDLE Handle,
OUT USN *UsnBuffer
)
/*++
Routine Description:
This routine reads the USN of the last modify operation to a file.
Arguments:
Handle -- The file handle of an opened file.
UsnBuffer -- The output buffer to hold the object ID.
Return Value:
Returns the NTSTATUS of the last error found, or success.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsReadFileUsnData:"
ULONG NtStatus;
IO_STATUS_BLOCK Iosb;
ULONGLONG Buffer[(sizeof(USN_RECORD) + MAX_PATH*2 + 7)/8];
//
// Go get the USN record for the file.
//
NtStatus = NtFsControlFile(
Handle, // file handle
NULL, // event
NULL, // apc routine
NULL, // apc context
&Iosb, // iosb
FSCTL_READ_FILE_USN_DATA, // FsControlCode
&Handle, // input buffer
sizeof(HANDLE), // input buffer length
Buffer, // OutputBuffer for USNRecord
sizeof(Buffer)); // OutputBuffer Length
if (!NT_SUCCESS(NtStatus)) {
if (NtStatus == STATUS_INVALID_DEVICE_STATE) {
DPRINT(0, "++ FSCTL_READ_FILE_USN_DATA failed. No journal on volume\n");
}
DPRINT_NT(0, "++ FSCTL_READ_FILE_USN_DATA failed. ", NtStatus);
return FrsSetLastNTError(NtStatus);
}
//
// Return the last USN on the file.
//
*UsnBuffer = ((PUSN_RECORD) (Buffer))->Usn;
DUMP_USN_RECORD(4, (PUSN_RECORD)(Buffer));
return ERROR_SUCCESS;
}
DWORD
FrsReadFileParentFid(
IN HANDLE Handle,
OUT ULONGLONG *ParentFid
)
/*++
Routine Description:
This routine reads the parent FID for the file.
*** WARNING ***
Note with multiple links to a file there could be multiple parents.
NTFS gives us one of them.
Arguments:
Handle -- The file handle of an opened file.
ParentFid -- The output buffer to hold the parent file ID.
Return Value:
Returns the NTSTATUS of the last error found, or success.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsReadFileParentFid:"
ULONG NtStatus;
IO_STATUS_BLOCK Iosb;
ULONGLONG Buffer[(sizeof(USN_RECORD) + MAX_PATH*2 + 7)/8];
//
// Go get the USN record for the file.
//
NtStatus = NtFsControlFile(
Handle, // file handle
NULL, // event
NULL, // apc routine
NULL, // apc context
&Iosb, // iosb
FSCTL_READ_FILE_USN_DATA, // FsControlCode
&Handle, // input buffer
sizeof(HANDLE), // input buffer length
Buffer, // OutputBuffer for USNRecord
sizeof(Buffer)); // OutputBuffer Length
if (!NT_SUCCESS(NtStatus)) {
if (NtStatus == STATUS_INVALID_DEVICE_STATE) {
DPRINT(0, "++ FSCTL_READ_FILE_USN_DATA failed. No journal on volume\n");
}
DPRINT_NT(0, "++ FSCTL_READ_FILE_USN_DATA failed.", NtStatus);
*ParentFid = ZERO_FID;
return FrsSetLastNTError(NtStatus);
}
//
// Return a parent FID for the file. (could be more than one with links)
//
*ParentFid = ((PUSN_RECORD) (Buffer))->ParentFileReferenceNumber;
DUMP_USN_RECORD(4, (PUSN_RECORD)(Buffer));
return ERROR_SUCCESS;
}
DWORD
FrsGetReparseTag(
IN HANDLE Handle,
OUT ULONG *ReparseTag
)
/*++
Routine Description:
Return the value of the reparse tag.
Arguments:
Handle - Handle for a reparse point
ReparseTag - returned reparse tag if ERROR_SUCCESS
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetReparseTag:"
NTSTATUS NtStatus;
DWORD ReparseDataLength;
PCHAR ReparseBuffer;
IO_STATUS_BLOCK IoStatusBlock;
PREPARSE_DATA_BUFFER ReparseBufferHeader;
//
// Allocate a buffer and get the information.
//
ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
ReparseBuffer = FrsAlloc(ReparseDataLength);
//
// Query the reparse point.
//
NtStatus = NtFsControlFile(Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_GET_REPARSE_POINT,
NULL,
0,
(PVOID)ReparseBuffer,
ReparseDataLength
);
if (!NT_SUCCESS(NtStatus)) {
DPRINT_NT(4, "++ Could not get reparse point;", NtStatus);
FrsFree(ReparseBuffer);
return FrsSetLastNTError(NtStatus);
}
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
*ReparseTag = ReparseBufferHeader->ReparseTag;
FrsFree(ReparseBuffer);
return ERROR_SUCCESS;
}
DWORD
FrsCheckReparse(
IN PWCHAR Name,
IN PVOID Id,
IN DWORD IdLen,
IN HANDLE VolumeHandle
)
/*++
Routine Description:
Check that the reparse point is allowed
Arguments:
Name - File name for error messages
Id - Fid or Oid
VolumeHandle - open handle to the volume root.
Thread Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCheckReparse:"
DWORD WStatus;
HANDLE Handle;
ULONG ReparseTag;
//
// For proper cleanup in the event of failure
//
Handle = INVALID_HANDLE_VALUE;
//
// Open the file for read access
WStatus = FrsOpenSourceFileById(&Handle,
NULL,
NULL,
VolumeHandle,
Id,
IdLen,
// READ_ACCESS,
READ_ATTRIB_ACCESS,
ID_OPTIONS,
SHARE_ALL,
FILE_OPEN);
//
// File has been deleted; done
//
if (!WIN_SUCCESS(WStatus)) {
DPRINT2_WS(4, "++ %ws (Id %08x %08x) could not open for reparse;",
Name, PRINTQUAD(*((PULONGLONG)Id)), WStatus);
return WStatus;
}
//
// What type of reparse is it?
//
WStatus = FrsGetReparseTag(Handle, &ReparseTag);
FRS_CLOSE(Handle);
if (!WIN_SUCCESS(WStatus)) {
DPRINT2_WS(4, "++ %ws (Id %08x %08x) could not get reparse tag;",
Name, PRINTQUAD(*((PULONGLONG)Id)), WStatus);
return WStatus;
}
//
// We only accept operations on files with SIS and HSM reparse points.
// For example a rename of a SIS file into a replica tree needs to prop
// a create CO.
//
if ((ReparseTag != IO_REPARSE_TAG_HSM) &&
(ReparseTag != IO_REPARSE_TAG_SIS)) {
DPRINT3(4, "++ %ws (Id %08x %08x) is reparse tag %08x is unsupported.\n",
Name, PRINTQUAD(*((PULONGLONG)Id)), ReparseTag);
return ERROR_OPERATION_ABORTED;
}
return ERROR_SUCCESS;
}
DWORD
FrsDeleteReparsePoint(
IN HANDLE Handle
)
/*++
Routine Description:
Delete the reparse point on the opened file.
Arguments:
Handle - Handle for a reparse point
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteReparsePoint:"
ULONG WStatus = ERROR_SUCCESS;
DWORD ReparseDataLength;
ULONG ReparseTag;
PCHAR ReparseData;
PREPARSE_DATA_BUFFER ReparseBufferHeader;
ULONG ActualSize;
//
// Allocate a buffer and get the information.
//
ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
ReparseData = FrsAlloc(ReparseDataLength);
//
// Need the reparse tag in order to do the delete.
//
if (!DeviceIoControl(Handle,
FSCTL_GET_REPARSE_POINT,
(LPVOID) NULL,
(DWORD) 0,
(LPVOID) ReparseData,
ReparseDataLength,
&ActualSize,
(LPOVERLAPPED) NULL )) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ FrsDeleteReparsePoint - FSCTL_GET_REPARSE_POINT failed,",
WStatus, RETURN);
}
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseData;
ReparseTag = ReparseBufferHeader->ReparseTag;
DPRINT1(3, "++ FrsDeleteReparsePoint - Tag: 08x\n", ReparseTag);
//
// Delete the reparse point.
//
ZeroMemory(ReparseBufferHeader, sizeof(REPARSE_DATA_BUFFER_HEADER_SIZE));
ReparseBufferHeader->ReparseTag = ReparseTag;
ReparseBufferHeader->ReparseDataLength = 0;
if (!DeviceIoControl(Handle,
FSCTL_DELETE_REPARSE_POINT,
(LPVOID) ReparseData,
REPARSE_DATA_BUFFER_HEADER_SIZE,
(LPVOID) NULL,
(DWORD) 0,
&ActualSize,
(LPOVERLAPPED) NULL )) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ FrsDeleteReparsePoint - FSCTL_DELETE_REPARSE_POINT failed,",
WStatus, RETURN);
}
RETURN:
FrsFree(ReparseData);
return WStatus;
}
DWORD
FrsChaseSymbolicLink(
IN PWCHAR SymLink,
OUT PWCHAR *OutPrintName,
OUT PWCHAR *OutSubstituteName
)
/*++
Routine Description:
This function opens the specified file with backup intent for
reading all the files attributes, ...
Arguments:
Handle - A pointer to a handle to return an open handle.
lpFileName - Represents the name of the file or directory to be opened.
DesiredAccess
CreateOptions
Return Value:
Winstatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsChaseSymbolicLink:"
NTSTATUS NtStatus;
DWORD WStatus;
HANDLE Handle;
DWORD ReparseDataLength;
PCHAR ReparseBuffer;
DWORD SubLen;
DWORD PrintLen;
PWCHAR SubName;
PWCHAR PrintName;
IO_STATUS_BLOCK IoStatusBlock;
PREPARSE_DATA_BUFFER ReparseBufferHeader;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING FileName;
ULONG FileAttributes;
ULONG CreateDisposition;
ULONG ShareMode;
ULONG Colon;
if ((OutPrintName == NULL) || (OutSubstituteName == NULL)) {
return ERROR_INVALID_PARAMETER;
}
//
// Assume no symlink
//
*OutPrintName = FrsWcsDup(SymLink);
*OutSubstituteName = FrsWcsDup(SymLink);
//
// Allocate a buffer and get the information.
//
ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
ReparseBuffer = FrsAlloc(ReparseDataLength);
NEXT_LINK:
//
// Open the target symlink. If this is a dos type path name then
// convert it to NtPathName or else use it as it is.
//
Colon = wcscspn(*OutSubstituteName, L":");
if (Colon == 1 ||
(wcsncmp(*OutSubstituteName, L"\\\\?\\", wcslen(L"\\\\?\\")) == 0 )) {
WStatus = FrsOpenSourceFileW(&Handle,
*OutSubstituteName,
GENERIC_READ,
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_OPEN_REPARSE_POINT);
CLEANUP1_WS(4, "++ Could not open %ws; ", *OutSubstituteName, WStatus, CLEANUP);
} else {
//
// The path already in Nt style. Use it as it is.
//
FileName.Buffer = *OutSubstituteName;
FileName.Length = (USHORT)(wcslen(*OutSubstituteName) * sizeof(WCHAR));
FileName.MaximumLength = (USHORT)(wcslen(*OutSubstituteName) * sizeof(WCHAR));
InitializeObjectAttributes(&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
CreateDisposition = FILE_OPEN; // Open existing file
ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
FileAttributes = FILE_ATTRIBUTE_NORMAL;
NtStatus = NtCreateFile(&Handle,
GENERIC_READ,
&Obja,
&IoStatusBlock,
NULL, // Initial allocation size
FileAttributes,
ShareMode,
CreateDisposition,
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_OPEN_REPARSE_POINT,
NULL, 0);
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(4, "++ Could not open %ws;", *OutSubstituteName, WStatus, CLEANUP);
}
//
// Query the reparse point.
//
// Now go and get the data.
//
NtStatus = NtFsControlFile(Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_GET_REPARSE_POINT,
NULL,
0,
(PVOID)ReparseBuffer,
ReparseDataLength
);
FRS_CLOSE(Handle);
if (NtStatus == STATUS_NOT_A_REPARSE_POINT) {
FrsFree(ReparseBuffer);
return ERROR_SUCCESS;
}
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(4, "++ Could not fsctl %ws;", *OutSubstituteName, WStatus, CLEANUP);
//
// Display the buffer.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
if ((ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) ||
(ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
SubName = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[0];
SubLen = ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength;
PrintName = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[(SubLen + sizeof(UNICODE_NULL))/sizeof(WCHAR)];
PrintLen = ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength;
SubName[SubLen / sizeof(WCHAR)] = L'\0';
PrintName[PrintLen / sizeof(WCHAR)] = L'\0';
DPRINT2(4, "++ %ws -> (print) %ws\n", *OutPrintName, PrintName);
DPRINT2(4, "++ %ws -> (substitute) %ws\n", *OutSubstituteName, SubName);
FrsFree(*OutPrintName);
FrsFree(*OutSubstituteName);
//
// We need to return both print name and substitute name.
//
*OutPrintName = FrsWcsDup(PrintName);
*OutSubstituteName = FrsWcsDup(SubName);
goto NEXT_LINK;
}
return ERROR_SUCCESS;
CLEANUP:
FRS_CLOSE(Handle);
FrsFree(ReparseBuffer);
*OutPrintName = FrsFree(*OutPrintName);
*OutSubstituteName = FrsFree(*OutSubstituteName);
return WStatus;
}
DWORD
FrsTraverseReparsePoints(
IN PWCHAR SuppliedPath,
OUT PWCHAR *RealPath
)
/*++
Routine Description:
This function steps through each element of the path
and maps all reparse points to actual paths. In the end the
path returned has no reparse points of the form
IO_REPARSE_TAG_MOUNT_POINT and IO_REPARSE_TAG_SYMBOLIC_LINK.
Arguments:
Supplied - Input path. May or may not have any reparse points.
RealPath - Path without any reparse points or NULL if there is an error
reading reparse data.
Return Value:
Winstatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsTraverseReparsePoints:"
PWCHAR TempStr = NULL;
PWCHAR IndexPtr = NULL;
PWCHAR BackSlashPtr = NULL;
PWCHAR TempPath = NULL;
PWCHAR PrintablePath = NULL;
DWORD Colon = 0;
DWORD CloseBrace = 0;
DWORD LoopBreaker = 0;
DWORD WStatus = ERROR_SUCCESS;
ULONG FileAttributes = 0;
BOOL ReparsePointFound = FALSE;
if (!SuppliedPath) {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
TempStr = FrsAlloc((wcslen(SuppliedPath) + 1) * sizeof(WCHAR));
wcscpy(TempStr,SuppliedPath);
//
// Repeat the procedure until you have a clean path without any
// reparse points.
// e.g.
// f:\c -> d:\destination
// e:\a\b -> f:\c\d (which is actually d:\destination\d)
// Given path is e:\a\b\c
// FIrst time through the loop we will have f:\c\d\c
// Second time we will translate the reparse point at f:\c
// and get the correct answer d:\destination\d\c
//
do {
*RealPath = NULL;
ReparsePointFound = FALSE;
//
// Find the colon. Every path has to either have a colon followed by a '\'
// or it should be of the form. "\??\Volume{60430005-ab47-11d3-8973-806d6172696f}\"
//
Colon = wcscspn(TempStr, L":");
if (Colon == wcslen(TempStr)) {
//
// Path does not have a colon. It can be of the form
// "\??\Volume{60430005-ab47-11d3-8973-806d6172696f}\"
//
CloseBrace = wcscspn(TempStr, L"}");
if (TempStr[CloseBrace] != L'}' ||
TempStr[CloseBrace + 1] != L'\\') {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
//
// Copy the path up to 1 past the closing brace as it is. It could be \??\Volume...
// or \\.\Volume... or \\?\Volume.. or some other complex form.
// Start looking for reparse points past the closing brace.
//
*RealPath = FrsAlloc((CloseBrace + 3)* sizeof(WCHAR));
wcsncpy(*RealPath,TempStr,CloseBrace + 2);
(*RealPath)[CloseBrace + 2] = L'\0';
IndexPtr = &TempStr[CloseBrace + 1];
} else {
if (TempStr[Colon] != L':' ||
TempStr[Colon + 1] != L'\\') {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
//
// Copy the path up to 1 past the colon as it is. It could be d:\
// or \\.\d:\ or \??\d:\ or some other complex form.
// Start looking for reparse points past the colon.
//
*RealPath = FrsAlloc((Colon + 3)* sizeof(WCHAR));
wcsncpy(*RealPath,TempStr,Colon + 2);
(*RealPath)[Colon + 2] = L'\0';
IndexPtr = &TempStr[Colon + 1];
}
BackSlashPtr = wcstok(IndexPtr,L"\\");
if (BackSlashPtr == NULL) {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
do {
if ((*RealPath)[wcslen(*RealPath) - 1] == L'\\') {
TempPath = FrsAlloc((wcslen(*RealPath) + wcslen(BackSlashPtr) + 1)* sizeof(WCHAR));
wcscpy(TempPath,*RealPath);
} else {
TempPath = FrsAlloc((wcslen(*RealPath) + wcslen(BackSlashPtr) + wcslen(L"\\") + 1)* sizeof(WCHAR));
wcscpy(TempPath,*RealPath);
wcscat(TempPath,L"\\");
}
wcscat(TempPath,BackSlashPtr);
FrsFree(*RealPath);
*RealPath = TempPath;
TempPath = NULL;
//
//
//
//
// FrsChaseSymbolicLink returns both then PrintName and the SubstituteName.
// We use the SubstituteName as it is always guaranteed to be there.
// PrintName is ignored.
//
WStatus = FrsChaseSymbolicLink(*RealPath, &PrintablePath, &TempPath);
PrintablePath = FrsFree(PrintablePath);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1(0,"ERROR reading reparse point data WStatus = %d\n",WStatus);
FrsFree(TempPath);
goto CLEANUP;
//
// We are only looking for reparse points that are
// either IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMBOLIC_LINK.
// Check if the path returned by FrsChaseSymbolicLink is same as the
// path passed to it. If it is then we haven't hit a reparse point.
//
} else if (wcscmp(*RealPath,TempPath)) {
ReparsePointFound = TRUE;
FrsFree(*RealPath);
*RealPath = TempPath;
TempPath = NULL;
} else {
TempPath = FrsFree(TempPath);
}
} while ( (BackSlashPtr = wcstok(NULL,L"\\")) != NULL);
if (SuppliedPath[wcslen(SuppliedPath) - 1] == L'\\') {
TempPath = FrsAlloc((wcslen(*RealPath) + wcslen(L"\\") + 1)* sizeof(WCHAR));
wcscpy(TempPath,*RealPath);
wcscat(TempPath,L"\\");
FrsFree(*RealPath);
*RealPath = TempPath;
TempPath = NULL;
}
FrsFree(TempStr);
TempStr = *RealPath;
//
// Break out of the loop if there is a junction point loop.
// If we have traversed the path 100 times and still can't
// get to the destination then we probably have a loop
// in the path.
//
++LoopBreaker;
} while ( ReparsePointFound && LoopBreaker < 100);
//
// Path has loops in it. Return error.
//
if (LoopBreaker >= 100) {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
CLEANUP:
DPRINT2(5,"Supplied Path = %ws, Traversed Path = %ws\n",SuppliedPath,(*RealPath)?*RealPath:L"<null>");
//
// If we are returning error then return NULL as the real path.
//
if (!WIN_SUCCESS(WStatus)) {
FrsFree(TempStr);
*RealPath = FrsFree(*RealPath);
}
return WStatus;
}
BOOL
FrsSearchArgv(
IN LONG ArgC,
IN PWCHAR *ArgV,
IN PWCHAR ArgKey,
OUT PWCHAR *ArgValue
)
/*++
Routine Description:
This routine searches an ArgV vector for the keyword in ArgKey.
If found it looks for an equals sign and returns a copy of the right
hand side in ArgValue. The caller must free the returned string.
Arguments:
ArgC - The number of entries in the ArgV vector.
ArgV - The vector of PWCHARS to search.
ArgKey - The key to search for. MUST BE LOWERCASE TO MATCH.
ArgValue - return location for the buffer ptr. Caller must free.
if NULL no right hand side is returned.
Return Value:
TRUE if ArgKey is found.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSearchArgv:"
LONG i, n, Len;
PWCHAR TestStr;
PWCHAR Wcs;
if (ArgValue != NULL) {
*ArgValue = NULL;
}
//
// Are we running as a service? We need to know prior
// to calling the first DPRINT.
//
for (n = 0; n < ArgC; ++n) {
TestStr = ArgV[n];
Len = wcslen(TestStr);
if (Len <= 0) {
continue;
}
//
// Skip -,/
//
if (TestStr[0] == L'-' || TestStr[0] == L'/') {
TestStr++;
Len--;
}
//
// Skip over leading spaces and tabs.
//
while ((TestStr[0] == UNICODE_SPACE) || (TestStr[0] == UNICODE_TAB) ) {
TestStr++;
Len--;
}
if (Len <= 0) {
continue;
}
_wcslwr(TestStr);
if (wcsstr(TestStr, ArgKey) != TestStr) {
continue;
}
//
// Found a match. Look for a value.
//
if (ArgValue != NULL) {
DPRINT2(5, "match on ArgV[%d] = %ws\n", n, TestStr);
Wcs = wcschr(TestStr, L'=');
if (Wcs) {
//
// Trim trailing leading spaces and tabs.
//
while ((TestStr[Len-1] == UNICODE_SPACE) ||
(TestStr[Len-1] == UNICODE_TAB )) {
Len--;
}
FRS_ASSERT(&TestStr[Len-1] >= Wcs);
TestStr[Len] = UNICODE_NULL;
*ArgValue = FrsWcsDup(Wcs+1);
DPRINT1(5, "++ return value = %ws\n", *ArgValue);
}
}
return TRUE;
}
DPRINT1(5, "++ No match for ArgKey = %ws\n", ArgKey);
return FALSE;
}
BOOL
FrsSearchArgvDWord(
IN LONG ArgC,
IN PWCHAR *ArgV,
IN PWCHAR ArgKey,
OUT PDWORD ArgValue
)
/*++
Routine Description:
This routine searches an ArgV vector for the keyword in ArgKey.
If found it looks for an equals sign and returns the right
hand side in ArgValue as a base 10 number.
Arguments:
ArgC - The number of entries in the ArgV vector.
ArgV - The vector of PWCHARS to search.
ArgKey - The key to search for. MUST BE LOWERCASE TO MATCH.
ArgValue - return location for the DWORD right hand side.
if ArgKey not found no right hand side is returned.
Return Value:
TRUE if ArgKey is found.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSearchArgvDWord:"
ULONG Len;
PWCHAR WStr;
if (FrsSearchArgv(ArgC, ArgV, ArgKey, &WStr)) {
//
// Found ArgKey
//
if (WStr != NULL) {
//
// Found rhs.
//
Len = wcslen(WStr);
if ((Len > 0) && (wcsspn(WStr, L"0123456789") == Len)){
*ArgValue = wcstoul(WStr, NULL, 10);
FrsFree(WStr);
return TRUE;
} else {
DPRINT2(0, "++ ERROR - Invalid decimal string '%ws' for %ws\n",
WStr, ArgKey);
FrsFree(WStr);
}
}
}
return FALSE;
}
BOOL
FrsDissectCommaList (
IN UNICODE_STRING RawArg,
OUT PUNICODE_STRING FirstArg,
OUT PUNICODE_STRING RemainingArg
)
/*++
Routine Description:
This routine parses a comma (or semicolon) separated string.
It picks off the first element in the given RawArg and provides both it and
the remaining part. Leading blanks and tabs are ignored. FirstArg is
returned zero length when there is either a leading comma or embedded
double comma. However the buffer address in FirstArg still points to where
the arg started so the caller can tell how much of the string has been
processed. The function returns FALSE when the input string is empty. It
returns TRUE when the FirstArg is valid, even if it is null.
Here are some examples:
RawArg FirstArg RemainingArg Result
---- --------- ------------- ------
empty empty empty FALSE
, empty empty TRUE
,, empty , TRUE
A A empty TRUE
A, A empty TRUE
,A empty A TRUE
"A ,B,C,D" A " B,C,D" TRUE
*A? *A? empty TRUE
Note that both output strings use the same string buffer memory of the
input string, and are not necessarily null terminated.
Based on FsRtlDissectName.
Arguments:
RawArg - The full string to parse.
FirstArg - The first name in the RawArg.
Don't allocate a buffer for this string.
RemainingArg - The rest of the RawArg after the first comma (if any).
Don't allocate a buffer for this string.
Return Value:
FALSE if the RawArg is empty else TRUE (meaning FirstArg is valid).
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDissectCommaList:"
ULONG i = 0;
ULONG RawArgLength;
ULONG FirstArgStart;
//
// Make both output strings empty for now
//
FirstArg->Length = 0;
FirstArg->MaximumLength = 0;
FirstArg->Buffer = NULL;
RemainingArg->Length = 0;
RemainingArg->MaximumLength = 0;
RemainingArg->Buffer = NULL;
RawArgLength = RawArg.Length / sizeof(WCHAR);
//DPRINT2(5, "RawArg string: %ws {%d)\n",
// (RawArg.Buffer != NULL) ? RawArg.Buffer : L"<NULL>", RawArg.Length);
//
// Skip over leading spaces and tabs.
//
while (i < RawArgLength) {
if (( RawArg.Buffer[i] != UNICODE_SPACE ) &&
( RawArg.Buffer[i] != UNICODE_TAB )){
break;
}
i += 1;
}
//
// Check for an empty input string
//
if (i == RawArgLength) {
return FALSE;
}
//
// Now run down the input string until we hit a comma or a semicolon or
// the end of the string, remembering where we started.
//
FirstArgStart = i;
while (i < RawArgLength) {
if ((RawArg.Buffer[i] == L',') || (RawArg.Buffer[i] == L';')) {
break;
}
i += 1;
}
//
// At this point all characters up to (but not including) i are
// in the first part. So setup the first arg. A leading comma returns
// a zero length string.
//
FirstArg->Length = (USHORT)((i - FirstArgStart) * sizeof(WCHAR));
FirstArg->MaximumLength = FirstArg->Length;
FirstArg->Buffer = &RawArg.Buffer[FirstArgStart];
//
// If no more string is left then return zero length. Else eat the comma and
// return the remaining part (could be null if string ends with comma).
//
if (i < RawArgLength) {
RemainingArg->Length = (USHORT)((RawArgLength - (i+1)) * sizeof(WCHAR));
RemainingArg->MaximumLength = RemainingArg->Length;
RemainingArg->Buffer = &RawArg.Buffer[i+1];
}
//DPRINT2(5, "FirstArg string: %ws {%d)\n",
// (FirstArg->Buffer != NULL) ? FirstArg->Buffer : L"<NULL>", FirstArg->Length);
//DPRINT2(5, "RemainingArg string: %ws {%d)\n",
// (RemainingArg->Buffer != NULL) ? RemainingArg->Buffer : L"<NULL>", RemainingArg->Length);
return TRUE;
}
BOOL
FrsCheckNameFilter(
IN PUNICODE_STRING Name,
IN PLIST_ENTRY FilterListHead
)
/*++
Routine Description:
Check the file name against each entry in the specified filter list.
Arguments:
Name - The file name to check (no slashes, spaces, etc.)
FilterListHead - The head of the filter list.
Return Value:
TRUE if Name is found in the FilterList.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCheckNameFilter:"
NTSTATUS Status;
ULONG Length;
BOOL Found = FALSE;
UNICODE_STRING UpcaseName;
WCHAR LocalBuffer[64];
if (IsListEmpty(FilterListHead)) {
return FALSE;
}
//
// Upper case the name string.
//
Length = Name->Length;
UpcaseName.Length = (USHORT) Length;
UpcaseName.MaximumLength = (USHORT) Length;
UpcaseName.Buffer = (Length > sizeof(LocalBuffer)) ? FrsAlloc(Length) : LocalBuffer;
Status = RtlUpcaseUnicodeString(&UpcaseName, Name, FALSE);
if (!NT_SUCCESS(Status)) {
DPRINT_NT(0, "++ RtlUpcaseUnicodeString failed;", Status);
FRS_ASSERT(!"RtlUpcaseUnicodeString failed");
goto RETURN;
}
//
// Walk the filter list, checking the name against each entry.
//
ForEachSimpleListEntry( FilterListHead, WILDCARD_FILTER_ENTRY, ListEntry,
//
// iterator pE is of type *WILDCARD_FILTER_ENTRY.
//
if (BooleanFlagOn(pE->Flags, WILDCARD_FILTER_ENTRY_IS_WILD)) {
Found = FrsIsNameInExpression(&pE->UFileName, &UpcaseName, FALSE, NULL);
} else {
Found = RtlEqualUnicodeString(&pE->UFileName, &UpcaseName, FALSE);
}
if (Found) {
break;
}
);
RETURN:
//
// Free the upcase buffer if we could not use the local one.
//
if (UpcaseName.Buffer != LocalBuffer) {
FrsFree(UpcaseName.Buffer);
}
UpcaseName.Buffer = NULL;
return Found;
}
VOID
FrsEmptyNameFilter(
IN PLIST_ENTRY FilterListHead
)
/*++
Routine Description:
Empty the filter list.
Arguments:
FilterListHead - The list head to empty.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsEmptyNameFilter:"
ForEachSimpleListEntry( FilterListHead, WILDCARD_FILTER_ENTRY, ListEntry,
//
// iterator pE is of type *WILDCARD_FILTER_ENTRY.
//
RemoveEntryList(&pE->ListEntry);
FrsFreeType(pE);
);
}
VOID
FrsLoadNameFilter(
IN PUNICODE_STRING FilterString,
IN PLIST_ENTRY FilterListHead
)
/*++
Routine Description:
Parse the input filter string and create a new filter list.
If the filter list passed in is not empty then it is emptied first.
Arguments:
FilterString - The comma separated filter list.
FilterListHead - The list head on which to create the filter entries.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsLoadNameFilter:"
NTSTATUS Status;
ULONG Length;
PWILDCARD_FILTER_ENTRY FilterEntry;
UNICODE_STRING UpcaseFilter, FirstArg;
WCHAR LocalBuffer[128];
//
// Empty the filter list if neessary.
//
FrsEmptyNameFilter(FilterListHead);
//
// Uppercase the new filter string.
//
DPRINT2(5, "++ filter string: %ws (%d)\n",
(FilterString->Buffer != NULL) ? FilterString->Buffer : L"<NULL>",
FilterString->Length);
Length = FilterString->Length;
UpcaseFilter.Length = (USHORT) Length;
UpcaseFilter.MaximumLength = (USHORT) Length;
UpcaseFilter.Buffer = (Length > sizeof(LocalBuffer)) ? FrsAlloc(Length) : LocalBuffer;
Status = RtlUpcaseUnicodeString(&UpcaseFilter, FilterString, FALSE);
if (!NT_SUCCESS(Status)) {
DPRINT_NT(0, "++ RtlUpcaseUnicodeString failed;", Status);
FRS_ASSERT(!"RtlUpcaseUnicodeString failed");
goto RETURN;
}
//
// Parse the comma list (skipping null entries) and create filter
// entries for each one.
//
while (FrsDissectCommaList (UpcaseFilter, &FirstArg, &UpcaseFilter)) {
Length = (ULONG) FirstArg.Length;
if (Length == 0) {
continue;
}
//DPRINT2(5, "++ FirstArg string: %ws {%d)\n",
// (FirstArg.Buffer != NULL) ? FirstArg.Buffer : L"<NULL>",
// FirstArg.Length);
//
// Allocate and init a wildcard filter entry.
//
FilterEntry = FrsAllocTypeSize(WILDCARD_FILTER_ENTRY_TYPE, Length);
FilterEntry->UFileName.Length = FirstArg.Length;
FilterEntry->UFileName.MaximumLength = FirstArg.MaximumLength;
CopyMemory(FilterEntry->UFileName.Buffer, FirstArg.Buffer, Length);
FilterEntry->UFileName.Buffer[Length/2] = UNICODE_NULL;
//
// Check for any wild card characters in the name.
//
if (FrsDoesNameContainWildCards(&FilterEntry->UFileName)) {
SetFlag(FilterEntry->Flags, WILDCARD_FILTER_ENTRY_IS_WILD);
//DPRINT1(5, "++ Wildcards found in %ws\n", FilterEntry->UFileName.Buffer);
}
//
// Add the entry to the end of the filter list.
//
InsertTailList(FilterListHead, &FilterEntry->ListEntry);
}
RETURN:
//
// Free the upcase buffer if we could not use the local one.
//
if (UpcaseFilter.Buffer != LocalBuffer) {
FrsFree(UpcaseFilter.Buffer);
}
UpcaseFilter.Buffer = NULL;
return;
}
ULONG
FrsParseIntegerCommaList(
IN PWCHAR ArgString,
IN ULONG MaxResults,
OUT PLONG Results,
OUT PULONG NumberResults,
OUT PULONG Offset
)
/*++
Routine Description:
Parse a list of integers separated by commas.
The integers are returned in successive locations of the Results array.
Null entries (e.g. ",,") return zero for the value.
Arguments:
ArgString - The comma separated NULL terminated string with integer values.
MaxResults - The maximum number of results that can be returned.
Results - An array of the integer results.
NumberResults - The number of results returned.
Offset - The offset to the next byte to process in ArgString if there
were not enough entries to return all the results.
Return Value:
FrsErrorStatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsParseIntegerCommaList:"
NTSTATUS Status;
ULONG Length, i = 0;
ULONG FStatus = FrsErrorSuccess;
BOOL More;
PWILDCARD_FILTER_ENTRY FilterEntry;
UNICODE_STRING TempUStr, FirstArg;
RtlInitUnicodeString(&TempUStr, ArgString);
//
// Parse the comma list and convert each entry to a LONG.
//
while (More = FrsDissectCommaList (TempUStr, &FirstArg, &TempUStr) &&
(i < MaxResults)) {
Length = (ULONG) FirstArg.Length;
Results[i] = 0;
if (Length == 0) {
i += 1;
continue;
}
Status = RtlUnicodeStringToInteger (&FirstArg, 10, &Results[i]);
if (!NT_SUCCESS(Status)) {
DPRINT2_NT(1, "++ RtlUnicodeStringToInteger failed on arg %d of %ws :",
i, ArgString, Status);
FStatus = FrsErrorBadParam;
}
i += 1;
}
*NumberResults = i;
if (More) {
//
// There are more arguments to parse but we are out of the loop so
// return MoreWork status along with the offset into ArgString where
// we left off.
//
if (FStatus == FrsErrorSuccess) {
FStatus = FrsErrorMoreWork;
}
*Offset = (ULONG)(FirstArg.Buffer - ArgString);
}
return FStatus;
}
DWORD
FrsSetFileAttributes(
PWCHAR Name,
HANDLE Handle,
ULONG FileAttributes
)
/*++
Routine Description:
This routine sets the file's attributes
Arguments:
Name - for error messages
Handle - Supplies a handle to the file that is to be marked for delete.
Attributes - Attributes for the file
Return Value:
WStatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetFileAttributes:"
IO_STATUS_BLOCK IoStatus;
FILE_BASIC_INFORMATION BasicInformation;
NTSTATUS Status;
DWORD WStatus = ERROR_SUCCESS;
//
// Set the attributes
//
ZeroMemory(&BasicInformation, sizeof(BasicInformation));
BasicInformation.FileAttributes = FileAttributes | FILE_ATTRIBUTE_NORMAL;
Status = NtSetInformationFile(Handle,
&IoStatus,
&BasicInformation,
sizeof(BasicInformation),
FileBasicInformation);
if (!NT_SUCCESS(Status)) {
WStatus = FrsSetLastNTError(Status);
DPRINT1_NT(0, " ERROR - NtSetInformationFile(BasicInformation) failed on %ws :",
Name, Status);
}
return WStatus;
}
DWORD
FrsResetAttributesForReplication(
PWCHAR Name,
HANDLE Handle
)
/*++
Routine Description:
This routine turns off the attributes that prevent deletion and write
Arguments:
Name - for error messages
Handle - Supplies a handle to the file that is to be marked for delete.
Return Value:
WStatus.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsResetAttributesForReplication:"
FILE_NETWORK_OPEN_INFORMATION FileInfo;
DWORD WStatus = ERROR_SUCCESS;
//
// Get the file's attributes
//
if (!FrsGetFileInfoByHandle(Name, Handle, &FileInfo)) {
DPRINT1(4, "++ Can't get attributes for %ws\n", Name);
WIN_SET_FAIL(WStatus);
return WStatus;
}
//
// Turn off the access attributes that prevent deletion and write
//
if (FileInfo.FileAttributes & NOREPL_ATTRIBUTES) {
DPRINT1(4, "++ Reseting attributes for %ws\n", Name);
WStatus = FrsSetFileAttributes(Name, Handle,
FileInfo.FileAttributes &
~NOREPL_ATTRIBUTES);
if (!WIN_SUCCESS(WStatus)) {
DPRINT1(4, "++ Can't reset attributes for %ws\n", Name);
return WStatus;
}
DPRINT1(4, "++ Attributes for %ws now allow replication\n", Name);
} else {
DPRINT1(4, "++ Attributes for %ws allow replication\n", Name);
}
return WStatus;
}
DWORD
FrsEnumerateDirectoryDeleteWorker(
IN HANDLE DirectoryHandle,
IN PWCHAR DirectoryName,
IN DWORD DirectoryLevel,
IN PFILE_DIRECTORY_INFORMATION DirectoryRecord,
IN DWORD DirectoryFlags,
IN PWCHAR FileName,
IN PVOID Ignored
)
/*++
Routine Description:
Empty a directory of non-replicating files and dirs if this is
an ERROR_DIR_NOT_EMPTY and this is a retry change order for a
directory delete.
Arguments:
DirectoryHandle - Handle for this directory.
DirectoryName - Relative name of directory
DirectoryLevel - Directory level (0 == root)
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
DirectoryRecord - Record from DirectoryHandle
FileName - From DirectoryRecord (w/terminating NULL)
Ignored - Context is ignored
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsEnumerateDirectoryDeleteWorker:"
DWORD WStatus;
NTSTATUS NtStatus;
HANDLE Handle = INVALID_HANDLE_VALUE;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
//
// Depth first
//
if (DirectoryRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
WStatus = FrsEnumerateDirectoryRecurse(DirectoryHandle,
DirectoryName,
DirectoryLevel,
DirectoryRecord,
DirectoryFlags,
FileName,
INVALID_HANDLE_VALUE,
Ignored,
FrsEnumerateDirectoryDeleteWorker);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
}
//
// Relative open
//
ZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectName.Length = (USHORT)DirectoryRecord->FileNameLength;
ObjectName.MaximumLength = (USHORT)DirectoryRecord->FileNameLength;
ObjectName.Buffer = DirectoryRecord->FileName;
ObjectAttributes.ObjectName = &ObjectName;
ObjectAttributes.RootDirectory = DirectoryHandle;
NtStatus = NtCreateFile(&Handle,
// GENERIC_READ | SYNCHRONIZE | DELETE | FILE_WRITE_ATTRIBUTES,
DELETE | SYNCHRONIZE | READ_ATTRIB_ACCESS | FILE_WRITE_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
NULL, // AllocationSize
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_OPEN_REPARSE_POINT |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, // EA buffer
0 // EA buffer size
);
//
// Error opening file or directory
//
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_WS(0, "++ ERROR - NtCreateFile(%ws) failed :", FileName, WStatus, CLEANUP);
//
// Turn off readonly, system, and hidden
//
FrsResetAttributesForReplication(FileName, Handle);
//
// Delete the file
//
WStatus = FrsDeleteByHandle(FileName, Handle);
DPRINT2(4, "++ Deleted file %ws\\%ws\n", DirectoryName, FileName);
CLEANUP:
FRS_CLOSE(Handle);
return WStatus;
}
DWORD
FrsEnumerateDirectoryRecurse(
IN HANDLE DirectoryHandle,
IN PWCHAR DirectoryName,
IN DWORD DirectoryLevel,
IN PFILE_DIRECTORY_INFORMATION DirectoryRecord,
IN DWORD DirectoryFlags,
IN PWCHAR FileName,
IN HANDLE FileHandle,
IN PVOID Context,
IN PENUMERATE_DIRECTORY_ROUTINE Function
)
/*++
Routine Description:
Open the directory identified by FileName in the directory
identified by DirectoryHandle and call FrsEnumerateDirectory().
Arguments:
DirectoryHandle - Handle for this directory.
DirectoryName - Relative name of directory
DirectoryLevel - Directory level
DirectoryRecord - From FrsEnumerateRecord()
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
FileName - Open this directory and recurse
FileHandle - Use for FileName if not INVALID_HANDLE_VALUE
Context - Passes global info from the caller to Function
Function - Called for every record
Return Value:
WIN32 STATUS
--*/
{
#undef DEBSUB
#define DEBSUB "FrsEnumerateDirectoryRecurse:"
DWORD WStatus;
NTSTATUS NtStatus;
HANDLE LocalHandle = INVALID_HANDLE_VALUE;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
//
// Relative open
//
if (!HANDLE_IS_VALID(FileHandle)) {
ZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectName.Length = (USHORT)DirectoryRecord->FileNameLength;
ObjectName.MaximumLength = (USHORT)DirectoryRecord->FileNameLength;
ObjectName.Buffer = DirectoryRecord->FileName;
ObjectAttributes.ObjectName = &ObjectName;
ObjectAttributes.RootDirectory = DirectoryHandle;
NtStatus = NtCreateFile(&LocalHandle,
// READ_ACCESS,
READ_ATTRIB_ACCESS | FILE_LIST_DIRECTORY,
&ObjectAttributes,
&IoStatusBlock,
NULL, // AllocationSize
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_OPEN_REPARSE_POINT |
FILE_SEQUENTIAL_ONLY |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, // EA buffer
0 // EA buffer size
);
//
// Error opening directory
//
if (!NT_SUCCESS(NtStatus)) {
DPRINT1_NT(0, "++ ERROR - NtCreateFile(%ws) :", FileName, NtStatus);
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
//
// Skip this directory tree
//
WStatus = ERROR_SUCCESS;
} else {
//
// Abort the entire enumeration
//
WStatus = FrsSetLastNTError(NtStatus);
}
goto CLEANUP;
}
FileHandle = LocalHandle;
}
//
// RECURSE
//
WStatus = FrsEnumerateDirectory(FileHandle,
FileName,
DirectoryLevel + 1,
DirectoryFlags,
Context,
Function);
CLEANUP:
FRS_CLOSE(LocalHandle);
return WStatus;
}
DWORD
FrsEnumerateDirectory(
IN HANDLE DirectoryHandle,
IN PWCHAR DirectoryName,
IN DWORD DirectoryLevel,
IN DWORD DirectoryFlags,
IN PVOID Context,
IN PENUMERATE_DIRECTORY_ROUTINE Function
)
/*++
Routine Description:
Enumerate the directory identified by DirectoryHandle, passing each
directory record to Function. If the record is for a directory,
call Function before recursing if ProcessBeforeCallingFunction
is TRUE.
Function controls the enumeration of the CURRENT directory
by setting ContinueEnumeration to TRUE (continue) or
FALSE (terminate).
Function controls the enumeration of the entire directory
tree by returning a WIN32 STATUS that is not ERROR_SUCCESS.
FrsEnumerateDirectory() will terminate the entire directory
enumeration by returning a WIN32 STATUS other than ERROR_SUCCESS
when encountering an error.
Context passes global info from the caller to Function.
Arguments:
DirectoryHandle - Handle for this directory.
DirectoryName - Relative name of directory
DirectoryLevel - Directory level
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
Context - Passes global info from the caller to Function
Function - Called for every record
Return Value:
WIN32 STATUS
--*/
{
#undef DEBSUB
#define DEBSUB "FrsEnumerateDirectory:"
DWORD WStatus;
NTSTATUS NtStatus;
BOOL Recurse;
PFILE_DIRECTORY_INFORMATION DirectoryRecord;
PFILE_DIRECTORY_INFORMATION DirectoryBuffer = NULL;
BOOLEAN RestartScan = TRUE;
PWCHAR FileName = NULL;
DWORD FileNameLength = 0;
DWORD NumBuffers = 0;
DWORD NumRecords = 0;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
extern LONG EnumerateDirectorySizeInBytes;
DPRINT3(4, "++ Enumerating %ws at level %d using buffer size %d\n",
DirectoryName, DirectoryLevel, EnumerateDirectorySizeInBytes);
//
// The buffer size is configurable with registry value
// ENUMERATE_DIRECTORY_SIZE
//
DirectoryBuffer = FrsAlloc(EnumerateDirectorySizeInBytes);
NEXT_BUFFER:
//
// READ A BUFFER FULL OF DIRECTORY INFORMATION
//
NtStatus = NtQueryDirectoryFile(DirectoryHandle, // Directory Handle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock,
DirectoryBuffer,
EnumerateDirectorySizeInBytes,
FileDirectoryInformation,
FALSE, // return single entry
NULL, // FileName
RestartScan // restart scan
);
//
// Enumeration Complete
//
if (NtStatus == STATUS_NO_MORE_FILES) {
WStatus = ERROR_SUCCESS;
goto CLEANUP;
}
//
// Error enumerating directory; return to caller
//
if (!NT_SUCCESS(NtStatus)) {
DPRINT1_NT(0, "++ ERROR - NtQueryDirectoryFile(%ws) : ", DirectoryName, NtStatus);
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
//
// Don't abort the entire enumeration; just this directory
//
WStatus = ERROR_SUCCESS;
} else {
//
// Abort the entire enumeration
//
WStatus = FrsSetLastNTError(NtStatus);
}
goto CLEANUP;
}
++NumBuffers;
//
// PROCESS DIRECTORY RECORDS
//
DirectoryRecord = DirectoryBuffer;
NEXT_RECORD:
++NumRecords;
//
// Filter . and ..
//
if (DirectoryRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//
// Skip .
//
if (DirectoryRecord->FileNameLength == 2 &&
DirectoryRecord->FileName[0] == L'.') {
goto ADVANCE_TO_NEXT_RECORD;
}
//
// Skip ..
//
if (DirectoryRecord->FileNameLength == 4 &&
DirectoryRecord->FileName[0] == L'.' &&
DirectoryRecord->FileName[1] == L'.') {
goto ADVANCE_TO_NEXT_RECORD;
}
} else if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_DIRECTORIES_ONLY) {
goto ADVANCE_TO_NEXT_RECORD;
}
//
// Add a terminating NULL to the FileName (painful)
//
if (FileNameLength < DirectoryRecord->FileNameLength + sizeof(WCHAR)) {
FrsFree(FileName);
FileNameLength = DirectoryRecord->FileNameLength + sizeof(WCHAR);
FileName = FrsAlloc(FileNameLength);
}
CopyMemory(FileName, DirectoryRecord->FileName, DirectoryRecord->FileNameLength);
FileName[DirectoryRecord->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
//
// Process the record
//
WStatus = (*Function)(DirectoryHandle,
DirectoryName,
DirectoryLevel,
DirectoryRecord,
DirectoryFlags,
FileName,
Context);
if (!WIN_SUCCESS(WStatus)) {
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
//
// Don't abort the entire enumeration; just this entry
//
WStatus = ERROR_SUCCESS;
} else {
//
// Abort the entire enumeration
//
goto CLEANUP;
}
}
ADVANCE_TO_NEXT_RECORD:
//
// Next record
//
if (DirectoryRecord->NextEntryOffset) {
DirectoryRecord = (PVOID)(((PCHAR)DirectoryRecord) +
DirectoryRecord->NextEntryOffset);
goto NEXT_RECORD;
}
//
// Done with this buffer; go get another one
// But don't restart the scan for every loop!
//
RestartScan = FALSE;
goto NEXT_BUFFER;
CLEANUP:
FrsFree(FileName);
FrsFree(DirectoryBuffer);
DPRINT5(4, "++ Enumerating %ws at level %d has finished "
"(%d buffers, %d records) with WStatus %s\n",
DirectoryName, DirectoryLevel, NumBuffers, NumRecords, ErrLabelW32(WStatus));
return WStatus;
}
DWORD
FrsFillDisk(
IN PWCHAR DirectoryName,
IN BOOL Cleanup
)
/*++
Routine Description:
Use all the disk space by creating a file in DirectoryName and
allocating space down to the last byte.
Delete the fill file if Cleanup is TRUE;
Arguments:
DirectoryName - Full pathname to the directory
Cleanup - Delete file if TRUE
Return Value:
WIN32 STATUS (ERROR_DISK_FULL is mapped to ERROR_SUCCESS)
--*/
{
#undef DEBSUB
#define DEBSUB "FrsFillDisk:"
DWORD WStatus;
NTSTATUS NtStatus;
DWORD Tid;
ULONGLONG Eof;
ULONGLONG NewEof;
ULONGLONG IncEof;
LARGE_INTEGER LargeInteger;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
HANDLE DirectoryHandle = INVALID_HANDLE_VALUE;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
WCHAR TidW[9];
//
// Open parent directory
//
WStatus = FrsOpenSourceFileW(&DirectoryHandle, DirectoryName, READ_ACCESS, OPEN_OPTIONS);
CLEANUP1_WS(0, "++ DBG ERROR - Cannot open fill directory %ws;",
DirectoryName, WStatus, CLEANUP);
//
// Relative open
//
Tid = GetCurrentThreadId();
swprintf(TidW, L"%08x", Tid);
ZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectName.Length = (USHORT)(wcslen(TidW) * sizeof(WCHAR));
ObjectName.MaximumLength = (USHORT)(wcslen(TidW) * sizeof(WCHAR));
ObjectName.Buffer = TidW;
ObjectAttributes.ObjectName = &ObjectName;
ObjectAttributes.RootDirectory = DirectoryHandle;
NtStatus = NtCreateFile(
&FileHandle,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | DELETE | FILE_WRITE_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
NULL, // AllocationSize
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_OPEN_REPARSE_POINT |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, // EA buffer
0 // EA buffer size
);
//
// Error opening file or directory
//
if (!NT_SUCCESS(NtStatus)) {
WStatus = FrsSetLastNTError(NtStatus);
CLEANUP1_NT(0, "++ DBG ERROR - NtCreateFile(%ws) : ", TidW, NtStatus, CLEANUP);
}
//
// Remove fill file
//
if (Cleanup) {
//
// Turn off readonly, system, and hidden
//
FrsResetAttributesForReplication(TidW, FileHandle);
//
// Delete the file
//
WStatus = FrsDeleteByHandle(TidW, FileHandle);
DPRINT2(4, "++ DBG - Deleted file %ws\\%ws\n", DirectoryName, TidW);
LeaveCriticalSection(&DebugInfo.DbsOutOfSpaceLock);
goto CLEANUP;
}
//
// WARN: Hold the lock until the file is deleted
//
EnterCriticalSection(&DebugInfo.DbsOutOfSpaceLock);
//
// Create fill file
//
NewEof = 0;
Eof = 0;
for (IncEof = (LONGLONG)-1; IncEof; IncEof >>= 1) {
NewEof = Eof;
do {
NewEof += IncEof;
LargeInteger.QuadPart = NewEof;
WStatus = FrsSetFilePointer(TidW, FileHandle, LargeInteger.HighPart,
LargeInteger.LowPart);
if (!WIN_SUCCESS(WStatus)) {
continue;
}
if (!SetEndOfFile(FileHandle)) {
WStatus = GetLastError();
continue;
}
DPRINT2(4, "++ DBG %ws: Allocated Eof is %08x %08x\n",
TidW, PRINTQUAD(NewEof));
Eof = NewEof;
WStatus = ERROR_SUCCESS;
} while (WIN_SUCCESS(WStatus) && !FrsIsShuttingDown);
}
DPRINT3(4, "++ DBG - Allocated %d MB in %ws\\%ws\n",
(DWORD)(Eof / (1024 * 1024)), DirectoryName, TidW);
CLEANUP:
FRS_CLOSE(DirectoryHandle);
FRS_CLOSE(FileHandle);
return WStatus;
}
#define THIRTY_SECONDS (30 * 1000)
ULONG
FrsRunProcess(
IN PWCHAR CommandLine,
IN HANDLE StandardIn,
IN HANDLE StandardOut,
IN HANDLE StandardError
)
/*++
Routine Description:
Run the specified command in a separate process.
Wait for the process to complete.
Arguments:
CommandLine - Unicode, null terminated command line string.
StandardIn - Handle to use for standard in.
StandardOut - Handle to use for Standard Out. NULL means use Debug log.
StandardError - Handle to use for Standard Error. NULL means use Debug log.
Return Value:
WIN32 STATUS
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRunProcess:"
#define MAX_CMD_LINE 1024
ULONG WStatus;
LONG WaitCount=20;
BOOL NeedDbgLock = FALSE;
BOOL CloseStandardIn = FALSE;
BOOL BStatus;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
DWORD Len;
DWORD TLen;
WCHAR ExpandedCmd[MAX_CMD_LINE+1];
TLen = ARRAY_SZ(ExpandedCmd);
//
// Setup the process I/O Handles.
//
if (!HANDLE_IS_VALID(StandardIn)) {
//
// Provide a handle to the NUL device for input.
//
StandardIn = CreateFileW(
L"NUL", // lpszName
GENERIC_READ | GENERIC_WRITE, // fdwAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, // fdwShareMode
NULL, // lpsa
OPEN_ALWAYS, // fdwCreate
FILE_ATTRIBUTE_NORMAL, // fdwAttrAndFlags
NULL // hTemplateFile
);
if (!HANDLE_IS_VALID(StandardIn)) {
WStatus = GetLastError();
DPRINT_WS(0, "++ CreateFileW(NUL) failed;", WStatus);
goto RETURN;
}
CloseStandardIn = TRUE;
}
if (!HANDLE_IS_VALID(StandardOut)) {
StandardOut = DebugInfo.LogFILE;
NeedDbgLock = TRUE;
}
if (!HANDLE_IS_VALID(StandardError)) {
StandardError = DebugInfo.LogFILE;
NeedDbgLock = TRUE;
}
memset(&StartupInfo, 0, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
StartupInfo.hStdInput = StandardIn;
StartupInfo.hStdOutput = StandardOut;
StartupInfo.hStdError = StandardError;
//
// Look for environment vars in command line and expand them.
//
Len = ExpandEnvironmentStrings(CommandLine, ExpandedCmd, TLen);
if (Len == 0) {
WStatus = GetLastError();
DPRINT1_WS(1, "++ ws command not expanded.", CommandLine, WStatus);
goto RETURN;
}
DPRINT1(0,"++ Running: %ws\n", ExpandedCmd);
//
// Get debug lock so our output stays in one piece.
//
if (NeedDbgLock) {DebLock();}
BStatus = CreateProcessW(
NULL, // lpApplicationName,
ExpandedCmd, // lpCommandLine,
NULL, // lpProcessAttributes,
NULL, // lpThreadAttributes,
TRUE, // bInheritHandles,
DETACHED_PROCESS | CREATE_NO_WINDOW, // dwCreationFlags,
NULL, // lpEnvironment,
NULL, // lpCurrentDirectory,
&StartupInfo, // lpStartupInfo,
&ProcessInfo); // lpProcessInformation
//
// Close the process and thread handles
//
if ( !BStatus ) {
if (NeedDbgLock) {DebUnLock();}
WStatus = GetLastError();
DPRINT1_WS(0, "++ CreateProcessW Failed to run: %ws,", CommandLine, WStatus);
goto RETURN;
}
WStatus = WAIT_FAILED;
while (--WaitCount > 0) {
WStatus = WaitForSingleObject( ProcessInfo.hProcess, THIRTY_SECONDS);
if (WStatus == WAIT_OBJECT_0) {
break;
}
DPRINT_NOLOCK1(0, "++ Waiting for process complete -- Time remaining: %d seconds\n",
WaitCount * (THIRTY_SECONDS / 1000));
}
if (NeedDbgLock) {DebUnLock();}
GetExitCodeProcess( ProcessInfo.hProcess, &WStatus );
if ( BStatus ) {
DPRINT1(0, "++ CreateProcess( %ws) succeeds\n", CommandLine);
DPRINT4(0, "++ ProcessInformation = hProcess %08x hThread %08x"
" ProcessId %08x ThreadId %08x\n",
ProcessInfo.hProcess, ProcessInfo.hThread, ProcessInfo.dwProcessId,
ProcessInfo.dwThreadId);
}
if (WStatus == STILL_ACTIVE) {
//
// Didn't finish. Bag it.
//
DPRINT(0, "++ Process failed to complete. Terminating\n");
WStatus = ERROR_PROCESS_ABORTED;
if (!TerminateProcess(ProcessInfo.hProcess, WStatus)) {
WStatus = GetLastError();
DPRINT_WS(0, "++ Process termination request failed :", WStatus);
}
} else {
DPRINT1(0, "++ Process completed with status: %d\n", WStatus);
}
FRS_CLOSE( ProcessInfo.hThread );
FRS_CLOSE( ProcessInfo.hProcess );
RETURN:
//
// close stdin handle.
//
if (CloseStandardIn) {
FRS_CLOSE(StandardIn);
}
return WStatus;
}
DWORD
FrsSetDacl(
PWCHAR RegName
)
/*++
Routine Description:
Add backup operators to the dacl for the specified registry key.
Arguments:
RegName - registry key (note HKEY_LOCAL_MACHINE becomes MACHINE)
Return Value:
WIN32 STATUS
API-UPDATE ... API-UPDATE ... API-UPDATE ... API-UPDATE ... API-UPDATE ...
From: Anne Hopkins
Sent: Tuesday, May 23, 2000 2:21 PM
To: Windows NT Development Announcements
Cc: Win32 API Changes Notification
Subject: RE: NT4 ACL API users should move to Win2K APIs
Sorry, the spec (and sample excerpt) referenced below is out-of-date
For Win2k security apis, use:
- public/sdk/inc/aclapi.h
- Platform SDK documentation (in MSDN) for reference and dev model
The reason to move to Win2k security APIs is to get the win2k Inheritance model,
with automatic propagation for File System and RGY ACLs. (DS does its own ACL
propagation). These APIs are also easier to use than the NT4 apis.
From: Anne Hopkins
Sent: Tuesday, May 23, 2000 10:49 AM
To: Win32 API Changes Notification
Subject: NT4 ACL API users should move to Win2K APIs
If you use old NT 4 or prior ACL APIs, you should plan on updating them
to win2k APIs as described in the New Win32 Access Control API spec:
\\cpntserver\areas\Security\Authorization\Specs\access5.doc
If you can't do this for Whistler, be sure to plan for it in Blackcomb.
NT 4 API EXAMPLE:
GetNamedSecurityInfo([in]object, [out]ACL...) // get the ACL from the file
BuildExplicitAccessWithName([out]ExplicitAccess, [in]TrusteeName, [in]Mask, <20>) // Build the new Explicit Access
SetEntriesInAcl([in]ExplicitAccess, [in]OldAcl, [out]NewAcl) // Add the new entry to the ACL
SetNameSecurityInfo([in]object, [in]NewACL...) // write the ACL back onto the file
NT 5.0 EXAMPLE:
GetNamedSecurityInfoEx([in]object, [in] provider, [out] pAccessList) // Get the access list from the file
BuildExplicitAccessWithName([out]ExplicitAccess, [in]TrusteeName, [in]Mask, <20>) // Build the access request
SetEntriesInAccessList([in]ExplicitAccess, [in] OldAccessList, [out]NewAccessList) // Add it to the list
SetNameSecurityInfoEx([in]object, [in[ NewAccessList<73>) // Write the access list back to the file
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetDacl"
DWORD WStatus;
PACL OldDACL;
PACL NewDACL = NULL;
PSECURITY_DESCRIPTOR SD = NULL;
PSID SystemSid = NULL;
PSID AdminsSid = NULL;
PSID EverySid = NULL;
PSID BackupSid = NULL;
EXPLICIT_ACCESS ExplicitAccess[4];
SID_IDENTIFIER_AUTHORITY SidNtAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SidWorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
//
// No registry key to process
//
if (!RegName) {
return ERROR_INVALID_PARAMETER;
}
//
// Get existing DACL
//
WStatus = GetNamedSecurityInfo(RegName,
SE_REGISTRY_KEY,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&OldDACL,
NULL,
&SD);
CLEANUP1_WS(0, "++ ERROR - GetNamedSecurityInfo(%ws);", RegName, WStatus, CLEANUP);
//
// Allocate the admins sid
//
if (!AllocateAndInitializeSid(&SidNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdminsSid)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - AllocateAndInitializeSid(ADMINS);", WStatus, CLEANUP);
}
//
// Allocate the system sid
//
if (!AllocateAndInitializeSid(&SidNtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&SystemSid)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - AllocateAndInitializeSid(SYSTEM);", WStatus, CLEANUP);
}
//
// Allocate the backup operators sid
//
if (!AllocateAndInitializeSid(&SidNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_BACKUP_OPS,
0, 0, 0, 0, 0, 0,
&BackupSid)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - AllocateAndInitializeSid(BACKUP OPS);", WStatus, CLEANUP);
}
//
// Allocate the everyone sid
//
if (!AllocateAndInitializeSid(&SidWorldAuthority,
1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&EverySid)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - AllocateAndInitializeSid(EVERYONE);", WStatus, CLEANUP);
}
//
// Initialize an EXPLICIT_ACCESS structure to allow access
//
ZeroMemory(ExplicitAccess, sizeof(ExplicitAccess));
//
// Admins
//
ExplicitAccess[0].grfAccessPermissions = GENERIC_ALL;
ExplicitAccess[0].grfAccessMode = SET_ACCESS;
ExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ExplicitAccess[0].Trustee.pMultipleTrustee = NULL;
ExplicitAccess[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccess[0].Trustee.ptstrName = AdminsSid;
//
// System
//
ExplicitAccess[1].grfAccessPermissions = GENERIC_ALL;
ExplicitAccess[1].grfAccessMode = SET_ACCESS;
ExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ExplicitAccess[1].Trustee.pMultipleTrustee = NULL;
ExplicitAccess[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccess[1].Trustee.ptstrName = SystemSid;
//
// Backup
//
ExplicitAccess[2].grfAccessPermissions = GENERIC_ALL;
ExplicitAccess[2].grfAccessMode = SET_ACCESS;
ExplicitAccess[2].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ExplicitAccess[2].Trustee.pMultipleTrustee = NULL;
ExplicitAccess[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExplicitAccess[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccess[2].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccess[2].Trustee.ptstrName = BackupSid;
//
// Everyone
//
ExplicitAccess[3].grfAccessPermissions = GENERIC_READ;
ExplicitAccess[3].grfAccessMode = SET_ACCESS;
ExplicitAccess[3].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ExplicitAccess[3].Trustee.pMultipleTrustee = NULL;
ExplicitAccess[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExplicitAccess[3].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccess[3].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccess[3].Trustee.ptstrName = EverySid;
//
// Create an new ACL by merging the EXPLICIT_ACCESS structure
// with the existing DACL
//
WStatus = SetEntriesInAcl(4, ExplicitAccess, OldDACL, &NewDACL);
CLEANUP1_WS(0, "++ ERROR - SetEntriesInAcl(%ws);", RegName, WStatus, CLEANUP);
//
// attach the new ACL as the object's DACL
//
WStatus = SetNamedSecurityInfo(RegName,
SE_REGISTRY_KEY,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
NewDACL,
NULL);
CLEANUP1_WS(0, "++ ERROR - SetNamedSecurityInfo(%ws);", RegName, WStatus, CLEANUP);
CLEANUP:
if (SD) {
LocalFree((HLOCAL)SD);
}
if(NewDACL) {
LocalFree((HLOCAL)NewDACL);
}
if (AdminsSid) {
FreeSid(AdminsSid);
}
if (SystemSid) {
FreeSid(SystemSid);
}
if (BackupSid) {
FreeSid(BackupSid);
}
if (EverySid) {
FreeSid(EverySid);
}
return WStatus;
}
#define FRS_FULL_ACCESS ( STANDARD_RIGHTS_ALL | \
FILE_READ_DATA | \
FILE_WRITE_DATA | \
FILE_APPEND_DATA | \
FILE_READ_EA | \
FILE_WRITE_EA | \
FILE_EXECUTE | \
FILE_READ_ATTRIBUTES | \
FILE_WRITE_ATTRIBUTES | \
FILE_CREATE_PIPE_INSTANCE | \
FILE_LIST_DIRECTORY | \
FILE_ADD_FILE | \
FILE_ADD_SUBDIRECTORY | \
FILE_DELETE_CHILD | \
FILE_TRAVERSE )
DWORD
FrsRestrictAccessToFileOrDirectory(
PWCHAR Name,
HANDLE Handle,
BOOL NoInherit
)
/*++
Routine Description:
Restrict access to administrators and local system.
Arguments:
Name - File or directory name for error messages
Handle - opened handle for name. If handle is NULL then open 'Name'.
NoInherit - means that we overwrite the ACL on the file or DIR and
do not inherit any ACL state from a parent dir.
We want to do this on the FRS working dir and the staging dir.
Return Value:
WIN32 STATUS
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRestrictAccessToFileOrDirectory"
DWORD WStatus = ERROR_SUCCESS;
HANDLE LocalHandle = NULL;
SECURITY_INFORMATION SecurityInfo;
EXPLICIT_ACCESS ExplicitAccess[2];
PACL NewDACL = NULL;
SID_IDENTIFIER_AUTHORITY SidNtAuthority = SECURITY_NT_AUTHORITY;
PSID SystemSid = NULL;
PSID AdminsSid = NULL;
//
// No file or directory handle?
//
if (!HANDLE_IS_VALID(Handle)) {
//
// Open the directory
//
if (Name == NULL) {
return ERROR_INVALID_PARAMETER;
}
LocalHandle = CreateFile(
Name,
GENERIC_WRITE | WRITE_DAC | FILE_READ_ATTRIBUTES | FILE_TRAVERSE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (!HANDLE_IS_VALID(LocalHandle)) {
return GetLastError();
}
Handle = LocalHandle;
}
//
// Allocate the admins sid
//
if (!AllocateAndInitializeSid(&SidNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdminsSid)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - AllocateAndInitializeSid(ADMINS);", WStatus, CLEANUP);
}
//
// Allocate the system sid
//
if (!AllocateAndInitializeSid(&SidNtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&SystemSid)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - AllocateAndInitializeSid(SYSTEM);", WStatus, CLEANUP);
}
ZeroMemory(ExplicitAccess, sizeof(ExplicitAccess));
ExplicitAccess[0].grfAccessPermissions = FRS_FULL_ACCESS;
ExplicitAccess[0].grfAccessMode = SET_ACCESS;
ExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ExplicitAccess[0].Trustee.pMultipleTrustee = NULL;
ExplicitAccess[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccess[0].Trustee.ptstrName = AdminsSid;
ExplicitAccess[1].grfAccessPermissions = FRS_FULL_ACCESS;
ExplicitAccess[1].grfAccessMode = SET_ACCESS;
ExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ExplicitAccess[1].Trustee.pMultipleTrustee = NULL;
ExplicitAccess[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccess[1].Trustee.ptstrName = SystemSid;
//
// Create new ACL.
//
WStatus = SetEntriesInAcl(2, ExplicitAccess, NULL, &NewDACL);
CLEANUP1_WS(0, "++ ERROR - SetEntriesInAcl(%ws);", Name, WStatus, CLEANUP);
//
// attach the new ACL as the object's DACL
// PROTECTED_DACL_SECURITY_INFORMATION - Means don't inherit ACLs from parent
//
SecurityInfo = DACL_SECURITY_INFORMATION;
if (NoInherit) {
SecurityInfo |= PROTECTED_DACL_SECURITY_INFORMATION;
}
WStatus = SetSecurityInfo(Handle,
SE_FILE_OBJECT,
SecurityInfo,
NULL,
NULL,
NewDACL,
NULL);
CLEANUP1_WS(0, "++ ERROR - SetSecurityInfo(%ws);", Name, WStatus, CLEANUP);
CLEANUP:
if(NewDACL) {
LocalFree((HLOCAL)NewDACL);
}
if (SystemSid) {
FreeSid(SystemSid);
}
if (AdminsSid) {
FreeSid(AdminsSid);
}
FRS_CLOSE(LocalHandle);
return WStatus;
}
ULONG
FrsProcessBackupRestore(
VOID
)
/*++
Routine Description:
Check the registry to see if a restore has transpired.
If so, delete the database and reset the registry as needed.
Arguments:
None.
Return Value:
WIN32 STATUS
--*/
{
#undef DEBSUB
#define DEBSUB "FrsProcessBackupRestore:"
ULONG WStatus;
DWORD KeyIdx;
HKEY hKey;
HKEY HBurKey = 0;
HKEY HCumuKey = 0;
DWORD GblBurFlags;
DWORD BurSetFlags;
WCHAR RegBuf[MAX_PATH + 1];
//
// Check for backup/restore in progress
// FRS_CONFIG_SECTION\backup/restore\Stop NtFrs from Starting
//
WStatus = CfgRegOpenKey(FKC_BKUP_STOP_SECTION_KEY, NULL, 0, &hKey);
if (WIN_SUCCESS(WStatus)) {
DPRINT_WS(0, ":S: WARN - Backup/Restore in progress; retry later.", WStatus);
EPRINT1(EVENT_FRS_CANNOT_START_BACKUP_RESTORE_IN_PROGRESS, ComputerName);
RegCloseKey(hKey);
return ERROR_BUSY;
}
//
// Open FRS_CONFIG_SECTION\backup/restore
// Create it if it doesn't exist and put an ACL on it.
//
WStatus = CfgRegOpenKey(FKC_BKUP_SECTION_KEY, NULL, 0, &HBurKey);
if (!WIN_SUCCESS(WStatus)) {
WStatus = CfgRegOpenKey(FKC_BKUP_SECTION_KEY, NULL, FRS_RKF_CREATE_KEY, &HBurKey);
CLEANUP_WS(0, "ERROR - Failed to create backup/restore key.", WStatus, CLEANUP_OK);
//
// New key; Ensure backup operators have access.
//
WStatus = FrsSetDacl(L"MACHINE\\" FRS_BACKUP_RESTORE_SECTION);
DPRINT_WS(0, "WARN - FrsSetDacl failed on backup/restore key.", WStatus);
//
// Ignore errors
//
WStatus = ERROR_SUCCESS;
}
//
// Move the Bur cumulative replica sets to the standard location
//
// Open FRS_CONFIG_SECTION\backup/restore\Process at Startup\Cumulative Replica Sets
// Enumerate the Replica Sets.
//
CfgRegOpenKey(FKC_BKUP_MV_CUMSETS_SECTION_KEY, NULL, FRS_RKF_CREATE_KEY, &hKey);
KeyIdx = 0;
HCumuKey = 0;
while (hKey) {
WStatus = RegEnumKey(hKey, KeyIdx, RegBuf, MAX_PATH + 1);
if (!WIN_SUCCESS(WStatus)) {
if (WStatus != ERROR_NO_MORE_ITEMS) {
DPRINT_WS(0, "WARN - Backup/restore enum.", WStatus);
}
break;
}
//
// Create the corresponding key in the standard location.
//
// FRS_CONFIG_SECTION\Cumulative Replica Sets\<RegBuf>
//
CfgRegOpenKey(FKC_CUMSET_N_BURFLAGS, RegBuf, FRS_RKF_CREATE_KEY, &HCumuKey);
if (HCumuKey) {
RegCloseKey(HCumuKey);
HCumuKey = 0;
}
//
// Delete key from Backup/Restore section.
//
// FRS_CONFIG_SECTION\backup/restore\Process at Startup\Cumulative Replica Sets\<RegBuf>
//
WStatus = RegDeleteKey(hKey, RegBuf);
if (!WIN_SUCCESS(WStatus)) {
DPRINT2_WS(0, ":S: WARN - RegDeleteKey(%ws\\%ws);",
FRS_BACKUP_RESTORE_MV_CUMULATIVE_SETS_SECTION, RegBuf, WStatus);
++KeyIdx;
}
}
if (hKey) {
RegCloseKey(hKey);
hKey = 0;
}
//
// PROCESS Global Backup/Restore BURFLAGS
//
// FRS_CONFIG_SECTION\backup/restore\Process at Startup\BurFlags
//
WStatus = CfgRegReadDWord(FKC_BKUP_STARTUP_GLOBAL_BURFLAGS, NULL, 0, &GblBurFlags);
CLEANUP_WS(0, "ERROR - Failed to read Global BurFlags.", WStatus, CLEANUP_OK);
//
// Do we need to delete the database?
//
if ((GblBurFlags & NTFRSAPI_BUR_FLAGS_RESTORE) &&
(GblBurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES)) {
DPRINT(4, ":S: Deleting database after full non-auth restore\n");
WStatus = FrsDeletePath(JetPath, ENUMERATE_DIRECTORY_FLAGS_NONE);
CLEANUP1_WS(0, ":S: ERROR - FrsDeletePath(%ws);", JetPath, WStatus, CLEANUP);
DPRINT(4, ":S: Recreating database after full non-auth restore\n");
//
// Create the database directories
//
if (!CreateDirectory(JetPath, NULL)) {
WStatus = GetLastError();
if (!WIN_ALREADY_EXISTS(WStatus)) {
CLEANUP1_WS(0, ":S: ERROR - CreateDirecotry(%ws);", JetPath, WStatus, CLEANUP);
}
}
if (!CreateDirectory(JetSys, NULL)) {
WStatus = GetLastError();
if (!WIN_ALREADY_EXISTS(WStatus)) {
CLEANUP1_WS(0, ":S: ERROR - CreateDirecotry(%ws);", JetSys, WStatus, CLEANUP);
}
}
if (!CreateDirectory(JetTemp, NULL)) {
WStatus = GetLastError();
if (!WIN_ALREADY_EXISTS(WStatus)) {
CLEANUP1_WS(0, ":S: ERROR - CreateDirecotry(%ws);", JetTemp, WStatus, CLEANUP);
}
}
if (!CreateDirectory(JetLog, NULL)) {
WStatus = GetLastError();
if (!WIN_ALREADY_EXISTS(WStatus)) {
CLEANUP1_WS(0, ":S: ERROR - CreateDirecotry(%ws);", JetLog, WStatus, CLEANUP);
}
}
//
// Enumerate the sets under "Cumulative Replica Sets" and mark them as not/primary
// FRS_CONFIG_SECTION\Cumulative Replica Sets
//
CfgRegOpenKey(FKC_CUMSET_SECTION_KEY, NULL, FRS_RKF_CREATE_KEY, &hKey);
CLEANUP_WS(0, "ERROR - Failed to open Cumulative Replica Sets.", WStatus, CLEANUP);
//
// Enumerate the Replica Sets
//
KeyIdx = 0;
while (TRUE) {
WStatus = RegEnumKey(hKey, KeyIdx, RegBuf, MAX_PATH + 1);
if (WStatus == ERROR_NO_MORE_ITEMS) {
break;
}
CLEANUP_WS(0, "WARN - Cumulative Replica Sets enum.", WStatus, CLEANUP);
//
// Save type of restore in BurFlags for this replica set.
// FRS_CONFIG_SECTION\Cumulative Replica Sets\<RegBuf>\BurFlags
//
WStatus = CfgRegWriteDWord(FKC_CUMSET_N_BURFLAGS, RegBuf, 0, GblBurFlags);
DPRINT_WS(0, "WARN - Cumulative Replica Sets BurFlags Write.", WStatus);
++KeyIdx;
}
if (hKey) {
RegCloseKey(hKey);
hKey = 0;
}
} // End of Delete Data Base
//
// Move individual BurFlags into Cumulative Replica Sets
// Open FRS_CONFIG_SECTION\backup/restore\Process at Startup\Replica Sets
// Enumerate the Replica Sets
//
CfgRegOpenKey(FKC_BKUP_MV_SETS_SECTION_KEY, NULL, FRS_RKF_CREATE_KEY, &hKey);
KeyIdx = 0;
while (hKey) {
WStatus = RegEnumKey(hKey, KeyIdx, RegBuf, MAX_PATH + 1);
if (!WIN_SUCCESS(WStatus)) {
if (WStatus != ERROR_NO_MORE_ITEMS) {
DPRINT_WS(0, "WARN - Backup/restore enum.", WStatus);
}
break;
}
//
// Get BurFlags
// FRS_CONFIG_SECTION\backup/restore\Process at Startup\Replica Sets\<RegBuf>\BurFlags
//
WStatus = CfgRegReadDWord(FKC_BKUP_STARTUP_SET_N_BURFLAGS,
RegBuf,
FRS_RKF_CREATE_KEY,
&BurSetFlags);
if (WIN_SUCCESS(WStatus)) {
//
// Write BurFlags
// FRS_CONFIG_SECTION\Cumulative Replica Sets\<RegBuf>\BurFlags
//
CfgRegWriteDWord(FKC_CUMSET_N_BURFLAGS,
RegBuf,
FRS_RKF_CREATE_KEY,
BurSetFlags);
}
//
// Delete source data key.
// FRS_CONFIG_SECTION\backup/restore\Process at Startup\Replica Sets\<RegBuf>
//
WStatus = RegDeleteKey(hKey, RegBuf);
if (!WIN_SUCCESS(WStatus)) {
DPRINT2_WS(0, ":S: WARN - RegDeleteKey(%ws\\%ws);",
FRS_BACKUP_RESTORE_MV_SETS_SECTION, RegBuf, WStatus);
++KeyIdx;
}
}
if (hKey) {
RegCloseKey(hKey);
hKey = 0;
}
//
// Set backup/restore flags to 0
//
// FRS_CONFIG_SECTION\backup/restore\Process at Startup\BurFlags
//
GblBurFlags = NTFRSAPI_BUR_FLAGS_NONE;
WStatus = CfgRegWriteDWord(FKC_BKUP_STARTUP_GLOBAL_BURFLAGS, NULL, 0, GblBurFlags);
CLEANUP_WS(0, "ERROR - Failed to clear Global BurFlags.", WStatus, CLEANUP);
goto CLEANUP;
CLEANUP_OK:
WStatus = ERROR_SUCCESS;
CLEANUP:
if (HBurKey) {
RegCloseKey(HBurKey);
}
return WStatus;
}
#define DEFAULT_MULTI_STRING_WCHARS (4) // at least 8
VOID
FrsCatToMultiString(
IN PWCHAR CatStr,
IN OUT DWORD *IOSize,
IN OUT DWORD *IOIdx,
IN OUT PWCHAR *IOStr
)
/*++
Routine Description:
Add a string + Catenation (if present) to the multi-string value
Arguments:
CatStr - string to concatenate
IOSize - Total size in wide chars of WStr
IOIdx - Current index to terminating \0 following \0 of last string
IOStr - Current string
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCatToMultiString:"
DWORD NewSize;
DWORD CatSize;
PWCHAR Str;
//
// NOP
//
if (!CatStr) {
return;
}
//
// allocate initial buffer
//
if (!*IOStr) {
*IOSize = DEFAULT_MULTI_STRING_WCHARS;
*IOStr = FrsAlloc(*IOSize * sizeof(WCHAR));
(*IOStr)[0] = L'\0';
(*IOStr)[1] = L'\0';
*IOIdx = 1;
}
//
// Extend buffer when needed (note that CatStr overwrites first
// \0 in the terminating \0\0. Hence, CatSize - 1 + 2 == CatSize + 1
//
CatSize = wcslen(CatStr);
while ((CatSize + 1 + *IOIdx) >= *IOSize) {
NewSize = *IOSize << 1;
Str = FrsAlloc(NewSize * sizeof(WCHAR));
CopyMemory(Str, *IOStr, *IOSize * sizeof(WCHAR));
FrsFree(*IOStr);
*IOStr = Str;
*IOSize = NewSize;
}
//
// Concatenate CatStr
//
*IOIdx -= 1;
CopyMemory(&(*IOStr)[*IOIdx], CatStr, CatSize * sizeof(WCHAR));
*IOIdx += CatSize;
//
// Append \0\0 and leave the index addressing the second \0.
//
(*IOStr)[*IOIdx] = L'\0';
*IOIdx += 1;
(*IOStr)[*IOIdx] = L'\0';
FRS_ASSERT(*IOIdx < *IOSize);
}
VOID
FrsAddToMultiString(
IN PWCHAR AddStr,
IN OUT DWORD *IOSize,
IN OUT DWORD *IOIdx,
IN OUT PWCHAR *IOStr
)
/*++
Routine Description:
Add a string + Catenation (if present) to the multi-string value
Arguments:
AddStr - string to add
IOSize - Total size in wide chars of WStr
IOIdx - Current index to terminating \0 following \0 of last string
IOStr - Current string
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsAddToMultiString:"
DWORD NewSize;
DWORD StrSize;
PWCHAR Str;
//
// NOP
//
if (!AddStr) {
return;
}
//
// allocate initial buffer
//
if (!*IOStr) {
*IOSize = DEFAULT_MULTI_STRING_WCHARS;
*IOStr = FrsAlloc(*IOSize * sizeof(WCHAR));
*IOIdx = 0;
}
//
// Extend buffer when needed
//
StrSize = wcslen(AddStr);
while ((StrSize + 2 + *IOIdx) >= *IOSize) {
NewSize = *IOSize << 1;
Str = FrsAlloc(NewSize * sizeof(WCHAR));
CopyMemory(Str, *IOStr, *IOSize * sizeof(WCHAR));
FrsFree(*IOStr);
*IOStr = Str;
*IOSize = NewSize;
}
//
// Append AddStr
//
CopyMemory(&(*IOStr)[*IOIdx], AddStr, StrSize * sizeof(WCHAR));
*IOIdx += StrSize;
//
// Append \0\0 and leave the index addressing the second \0.
//
(*IOStr)[*IOIdx] = L'\0';
*IOIdx += 1;
(*IOStr)[*IOIdx] = L'\0';
FRS_ASSERT(*IOIdx < *IOSize);
}
DWORD
UtilTranslateName(
IN PWCHAR FromName,
IN EXTENDED_NAME_FORMAT FromNameFormat,
IN EXTENDED_NAME_FORMAT ToNameFormat,
OUT PWCHAR *OutToName
)
/*++
Routine Description:
Translate one name format into another
Arguments:
FromName - Input, or source, name
FromNameFormat - Format of FromName
ToNameFormat - Desired format of *OutToName,
OutToName - converted string; free with FrsFree()
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "UtilTranslateName:"
DWORD WStatus;
WCHAR ToNameBuffer[MAX_PATH + 1];
DWORD ToNameSize = MAX_PATH + 1;
PWCHAR ToName = ToNameBuffer;
*OutToName = NULL;
if (!FromName) {
return ERROR_INVALID_PARAMETER;
}
//
// Name -> Name (using stack buffer)
//
if (!TranslateName(FromName, FromNameFormat, ToNameFormat, ToName, &ToNameSize)) {
WStatus = GetLastError();
} else {
WStatus = ERROR_SUCCESS;
}
//
// Name -> Name (using FrsAlloc'ed buffer)
//
while (WIN_BUF_TOO_SMALL(WStatus)) {
ToName = FrsAlloc((ToNameSize + 1) * sizeof(WCHAR));
if (!TranslateName(FromName, FromNameFormat, ToNameFormat, ToName, &ToNameSize)) {
WStatus = GetLastError();
ToName = FrsFree(ToName);
} else {
WStatus = ERROR_SUCCESS;
}
}
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(4, "++ WARN - TranslateName(%ws);", FromName, WStatus);
goto CLEANUP;
}
DPRINT2(5, "++ From -> To: %ws -> %ws\n",
FromName, ToName);
*OutToName = FrsWcsDup(ToName);
CLEANUP:
if (ToName != ToNameBuffer) {
FrsFree(ToName);
}
return WStatus;
}
DWORD
UtilConvertDnToStringSid(
IN PWCHAR Dn,
OUT PWCHAR *OutStringSid
)
/*++
Routine Description:
Retries GetTokenInformation() with larger buffers.
Arguments:
Dn - Dn of computer or user object
OutStringSid - String'ized sid. Free with FrsFree();
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "UtilConvertDnToStringSid:"
DWORD WStatus;
WCHAR SamCompatibleBuffer[MAX_PATH + 1];
DWORD SamCompatibleSize = MAX_PATH + 1;
PWCHAR SamCompatible = SamCompatibleBuffer;
if (OutStringSid) {
*OutStringSid = NULL;
}
if (!Dn) {
return ERROR_INVALID_PARAMETER;
}
//
// Dn -> Account (using stack buffer)
//
if (!TranslateName(Dn,
NameFullyQualifiedDN,
NameSamCompatible,
SamCompatible,
&SamCompatibleSize)) {
WStatus = GetLastError();
} else {
WStatus = ERROR_SUCCESS;
}
//
// Dn -> Account (using FrsAlloc'ed buffer)
//
while (WIN_BUF_TOO_SMALL(WStatus)) {
SamCompatible = FrsAlloc((SamCompatibleSize + 1) * sizeof(WCHAR));
if (!TranslateName(Dn,
NameFullyQualifiedDN,
NameSamCompatible,
SamCompatible,
&SamCompatibleSize)) {
WStatus = GetLastError();
SamCompatible = FrsFree(SamCompatible);
} else {
WStatus = ERROR_SUCCESS;
}
}
CLEANUP1_WS(4, "++ WARN - TranslateName(%ws);", Dn, WStatus, CLEANUP);
DPRINT2(5, "++ Dn -> Account: %ws -> %ws\n", Dn, SamCompatible);
CLEANUP:
if (SamCompatible != SamCompatibleBuffer) {
FrsFree(SamCompatible);
}
return WStatus;
}
DWORD
UtilGetTokenInformation(
IN HANDLE TokenHandle,
IN TOKEN_INFORMATION_CLASS TokenInformationClass,
IN DWORD InitialTokenBufSize,
OUT DWORD *OutTokenBufSize,
OUT PVOID *OutTokenBuf
)
/*++
Routine Description:
Retries GetTokenInformation() with larger buffers.
Arguments:
TokenHandle - From OpenCurrentProcess/Thread()
TokenInformationClass - E.g., TokenUser
InitialTokenBufSize - Initial buffer size; 0 = default
OutTokenBufSize - Resultant returned buf size
OutTokenBuf - free with with FrsFree()
Return Value:
OutTokenBufSize - Size of returned info (NOT THE BUFFER SIZE!)
OutTokenBuf - info of type TokenInformationClass. Free with FrsFree().
--*/
{
#undef DEBSUB
#define DEBSUB "UtilGetTokenInformation:"
DWORD WStatus;
*OutTokenBuf = NULL;
*OutTokenBufSize = 0;
//
// Check inputs
//
if (!HANDLE_IS_VALID(TokenHandle)) {
return ERROR_INVALID_PARAMETER;
}
if (InitialTokenBufSize == 0 ||
InitialTokenBufSize > (1024 * 1024)) {
InitialTokenBufSize = 1024;
}
//
// Retry if buffer is too small
//
*OutTokenBufSize = InitialTokenBufSize;
AGAIN:
//
// Need to check if *OutTokenBufSize == 0 as FrsAlloc asserts if called with 0 as the first parameter (prefix fix).
//
*OutTokenBuf = (*OutTokenBufSize == 0)? NULL : FrsAlloc(*OutTokenBufSize);
WStatus = ERROR_SUCCESS;
if (!GetTokenInformation(TokenHandle,
TokenInformationClass,
*OutTokenBuf,
*OutTokenBufSize,
OutTokenBufSize)) {
WStatus = GetLastError();
DPRINT2_WS(4, "++ WARN - GetTokenInformation(Info %d, Size %d);",
TokenInformationClass, *OutTokenBufSize, WStatus);
*OutTokenBuf = FrsFree(*OutTokenBuf);
if (WIN_BUF_TOO_SMALL(WStatus)) {
goto AGAIN;
}
}
return WStatus;
}
VOID
UtilPrintUser(
IN DWORD Severity
)
/*++
Routine Description:
Print info about the user (privs, user sid).
Arguments:
Severity - for dprint
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "UtilPrintUser:"
DWORD WStatus;
DWORD TokenBufSize;
PVOID TokenBuf = NULL;
HANDLE TokenHandle = NULL;
PWCHAR SidStr;
DWORD i;
TOKEN_PRIVILEGES *Tp;
TOKEN_USER *Tu;
DWORD PrivLen;
WCHAR PrivName[MAX_PATH + 1];
//
// For this process/thread
//
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &TokenHandle)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - OpenProcessToken();", WStatus, CLEANUP);
}
//
// Get the Token privileges from the access token for this thread or process
//
WStatus = UtilGetTokenInformation(TokenHandle,
TokenPrivileges,
0,
&TokenBufSize,
&TokenBuf);
CLEANUP_WS(4, "++ UtilGetTokenInformation(TokenPrivileges);", WStatus, USER);
//
// Print token privileges
//
Tp = (TOKEN_PRIVILEGES *)TokenBuf;
for (i = 0; i < Tp->PrivilegeCount; ++i) {
PrivLen = MAX_PATH + 1;
if (!LookupPrivilegeName(NULL, &Tp->Privileges[i].Luid, PrivName, &PrivLen)) {
DPRINT_WS(0, "++ WARN - LookupPrivilegeName();", WStatus);
continue;
}
DPRINT5(Severity, "++ Priv %2d is %ws :%s:%s:%s:\n",
i,
PrivName,
(Tp->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) ? "Enabled by default" : "",
(Tp->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) ? "Enabled" : "",
(Tp->Privileges[i].Attributes & SE_PRIVILEGE_USED_FOR_ACCESS) ? "Used" : "");
}
TokenBuf = FrsFree(TokenBuf);
//
// Get the TokenUser from the access token for this process
//
USER:
WStatus = UtilGetTokenInformation(TokenHandle,
TokenUser,
0,
&TokenBufSize,
&TokenBuf);
CLEANUP_WS(4, "++ UtilGetTokenInformation(TokenUser);", WStatus, CLEANUP);
Tu = (TOKEN_USER *)TokenBuf;
if (!ConvertSidToStringSid(Tu->User.Sid, &SidStr)) {
WStatus = GetLastError();
DPRINT_WS(4, "++ WARN - ConvertSidToStringSid();", WStatus);
} else {
DPRINT1(Severity, "++ User Sid: %ws\n", SidStr);
LocalFree(SidStr);
}
TokenBuf = FrsFree(TokenBuf);
CLEANUP:
FRS_CLOSE(TokenHandle);
FrsFree(TokenBuf);
}
DWORD
UtilRpcServerHandleToAuthSidString(
IN handle_t ServerHandle,
IN PWCHAR AuthClient,
OUT PWCHAR *AuthSid
)
/*++
Routine Description:
Extract a the string'ized user sid from the rpc server handle
by impersonating the caller and extracting the token info.
Arguments:
ServerHandle - from the rpc serve call
AuthClient - From the rpc server handle; for messages
ClientSid - stringized user sid; free with FrsFree()
Return Value:
Win32 Status.
--*/
{
#undef DEBSUB
#define DEBSUB "UtilRpcServerHandleToAuthSidString:"
DWORD WStatus;
DWORD WStatus2;
DWORD TokenBufSize;
PWCHAR SidStr;
TOKEN_USER *Tu;
PVOID TokenBuf = NULL;
BOOL Impersonated = FALSE;
HANDLE TokenHandle = NULL;
//
// Initialize return value
//
*AuthSid = NULL;
//
// Impersonate the rpc caller
//
WStatus = RpcImpersonateClient(ServerHandle);
CLEANUP1_WS(0, "++ ERROR - RpcImpersonateClient(%ws);", AuthClient, WStatus, CLEANUP);
Impersonated = TRUE;
//
// Open the impersonated thread token
//
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &TokenHandle)) {
WStatus = GetLastError();
CLEANUP_WS(0, "++ WARN - OpenProcessToken();", WStatus, CLEANUP);
}
//
// Get the user sid
//
WStatus = UtilGetTokenInformation(TokenHandle,
TokenUser,
0,
&TokenBufSize,
&TokenBuf);
CLEANUP_WS(4, "++ UtilGetTokenInformation(TokenUser);", WStatus, CLEANUP);
//
// Convert the sid into a string
//
Tu = (TOKEN_USER *)TokenBuf;
if (!ConvertSidToStringSid(Tu->User.Sid, &SidStr)) {
WStatus = GetLastError();
CLEANUP_WS(4, "++ WARN - ConvertSidToStringSid();", WStatus, CLEANUP);
} else {
DPRINT1(5, "++ Client Sid is %ws\n", SidStr);
*AuthSid = FrsWcsDup(SidStr);
LocalFree(SidStr);
}
//
// Done
//
WStatus = ERROR_SUCCESS;
CLEANUP:
TokenBuf = FrsFree(TokenBuf);
FRS_CLOSE(TokenHandle);
if (Impersonated) {
WStatus2 = RpcRevertToSelf();
DPRINT1_WS(0, "++ ERROR IGNORED - RpcRevertToSelf(%ws);", AuthClient, WStatus2);
}
return WStatus;
}
BOOL
FrsSetupPrivileges (
VOID
)
/*++
Routine Description:
Enable the privileges we need to replicate files.
Arguments:
None.
Return Value:
TRUE if got all privileges.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSetupPrivileges:"
NTSTATUS Status;
//
// Get the SE_SECURITY_PRIVILEGE to read/write SACLs on files.
//
Status = SetupOnePrivilege(SE_SECURITY_PRIVILEGE, "Security");
if (!NT_SUCCESS(Status)) {
DPRINT_WS(0, "ERROR - Failed to get Security privilege.",
FrsSetLastNTError(Status));
return FALSE;
}
//
// Get backup/restore privilege to bypass ACL checks.
//
Status = SetupOnePrivilege(SE_BACKUP_PRIVILEGE, "Backup");
if (!NT_SUCCESS(Status)) {
DPRINT_WS(0, "ERROR - Failed to get Backup privilege.",
FrsSetLastNTError(Status));
return FALSE;
}
Status = SetupOnePrivilege(SE_RESTORE_PRIVILEGE, "Restore");
if (!NT_SUCCESS(Status)) {
DPRINT_WS(0, "ERROR - Failed to get Restore privilege.",
FrsSetLastNTError(Status));
return FALSE;
}
return TRUE;
#if 0
//
// Set priority privilege in order to raise our base priority.
//
SetupOnePrivilege(SE_INC_BASE_PRIORITY_PRIVILEGE,
"Increase base priority");
//
// Set quota privilege in order to accommodate large profile buffers.
//
SetupOnePrivilege(SE_INCREASE_QUOTA_PRIVILEGE,
"Increase quotas");
#endif
}
DWORD
FrsMarkHandle(
IN HANDLE VolumeHandle,
IN HANDLE Handle
)
/*++
Routine Description:
Mark the handle as so that the journal record records
a flag that indicates "replication service is altering the file; ignore".
Arguments:
VolumeHandle - Used to check access
Handle - Handle to mark
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsMarkHandle:"
DWORD WStatus;
DWORD BytesReturned;
MARK_HANDLE_INFO MarkHandleInfo;
//
// Mark the handle as one of ours so that the journal thread
// knows to ignore the usn records.
//
MarkHandleInfo.UsnSourceInfo = USN_SOURCE_REPLICATION_MANAGEMENT;
MarkHandleInfo.VolumeHandle = VolumeHandle;
MarkHandleInfo.HandleInfo = 0;
if (!DeviceIoControl(Handle,
FSCTL_MARK_HANDLE,
(LPVOID)&MarkHandleInfo,
(DWORD)sizeof(MarkHandleInfo),
NULL,
0,
(LPDWORD)&BytesReturned,
NULL)) {
WStatus = GetLastError();
DPRINT_WS(0, "++ WARN - DeviceIoControl(MarkHandle);", WStatus);
} else {
WStatus = ERROR_SUCCESS;
//DPRINT(0, "++ TEMP - DeviceIoControl(MarkHandle) Success\n");
}
return WStatus;
}
VOID
FrsCreateJoinGuid(
OUT GUID *OutGuid
)
/*++
Routine Description:
Generate a random session id that is sizeof(GUID) in length.
The session id must be very random becuase it is used to
authenticate packets from our partners after a join.
The join was authenticated using impersonation.
Arguments:
Guid - Address of a guid
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsCreateJoinGuid:"
DWORD WStatus;
HCRYPTPROV hProv;
//
// Acquire the context.
// Consider caching the context if this function is called often.
//
if (!CryptAcquireContext(&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
WStatus = GetLastError();
DPRINT_WS(0, "++ WARN - CryptAcquireContext();", WStatus);
//
// Can't use CryptGenRandom(); try using a guid
//
FrsUuidCreate(OutGuid);
} else {
//
// Generate a random number
//
if (!CryptGenRandom(hProv, sizeof(GUID), (PBYTE)OutGuid)) {
WStatus = GetLastError();
DPRINT_WS(0, "++ WARN - CryptGenRandom();", WStatus);
//
// Can't use CryptGenRandom(); try using a guid
//
FrsUuidCreate(OutGuid);
} else {
DPRINT(5, "++ Created join guid\n");
}
//
// Release the context
//
if (!CryptReleaseContext(hProv, 0)) {
WStatus = GetLastError();
DPRINT_WS(0, "++ ERROR - CryptReleaseContext();", WStatus);
}
}
}
VOID
FrsFlagsToStr(
IN DWORD Flags,
IN PFLAG_NAME_TABLE NameTable,
IN ULONG Length,
OUT PSTR Buffer
)
/*++
Routine Description:
Routine to convert a Flags word to a descriptor string using the
supplied NameTable.
Arguments:
Flags - flags to convert.
NameTable - An array of FLAG_NAME_TABLE structs.
Length - Size of buffer in bytes.
Buffer - buffer with returned string.
Return Value:
Buffer containing printable string.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsFlagsToStr:"
PFLAG_NAME_TABLE pNT = NameTable;
LONG Remaining = Length-1;
FRS_ASSERT((Length > 4) && (Buffer != NULL));
*Buffer = '\0';
if (Flags == 0) {
strncpy(Buffer, "<Flags Clear>", Length);
return;
}
//
// Build a string for each bit set in the Flag name table.
//
while ((Flags != 0) && (pNT->Flag != 0)) {
if ((pNT->Flag & Flags) != 0) {
Remaining -= strlen(pNT->Name);
if (Remaining < 0) {
//
// Out of string buffer. Tack a "..." at the end.
//
Remaining += strlen(pNT->Name);
if (Remaining > 3) {
strcat(Buffer, "..." );
} else {
strcpy(&Buffer[Length-4], "...");
}
return;
}
//
// Tack the name onto the buffer and clear the flag bit so we
// know what is left set when we run out of table.
//
strcat(Buffer, pNT->Name);
ClearFlag(Flags, pNT->Flag);
}
pNT += 1;
}
if (Flags != 0) {
//
// If any flags are still set give them back in hex.
//
sprintf( &Buffer[strlen(Buffer)], "0x%08x ", Flags );
}
return;
}
DWORD
FrsDeleteByHandle(
IN PWCHAR Name,
IN HANDLE Handle
)
/*++
Routine Description:
This routine marks a file for delete, so that when the supplied handle
is closed, the file will actually be deleted.
Arguments:
Name - for error messages
Handle - Supplies a handle to the file that is to be marked for delete.
Return Value:
Win Status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsDeleteByHandle:"
//
// NOTE: This function is at the end of the module because we have to
// undefine DeleteFile to set the flag in the DispositionInfo struct.
//
#undef DeleteFile
FILE_DISPOSITION_INFORMATION DispositionInformation;
IO_STATUS_BLOCK IoStatus;
NTSTATUS NtStatus;
if (!HANDLE_IS_VALID(Handle)) {
return ERROR_SUCCESS;
}
//
// Mark the file for delete. The delete happens when the handle is closed.
//
DispositionInformation.DeleteFile = TRUE;
NtStatus = NtSetInformationFile(Handle,
&IoStatus,
&DispositionInformation,
sizeof(DispositionInformation),
FileDispositionInformation);
DPRINT1_NT(4, "++ Could not delete %ws;", Name, NtStatus);
return FrsSetLastNTError(NtStatus);
}