/*++ 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 #pragma hdrstop #include #include #include #include #include #ifdef SECURITY_WIN32 #include #else #define SECURITY_WIN32 #include #undef SECURITY_WIN32 #endif #include "stdarg.h" #include #include extern PGEN_TABLE ReparseTagTable; extern PGEN_TABLE ReplicasByGuid; extern PGEN_TABLE VolSerialNumberToDriveTable; VOID FrsBuildVolSerialNumberToDriveTable( PWCHAR LogicalDrives, BOOL EmptyTable ); BOOL JrnlIsChangeOrderInReplica( IN PCHANGE_ORDER_ENTRY ChangeOrder, IN PLONGLONG DirFileID ); #if 0 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]; } #endif 0 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; } if (_snprintf(Buffer, TIME_STRING_LENGTH, "%s %s %2d, %4d %02d:%02d:%02d", Days[SystemTime.wDayOfWeek],Months[SystemTime.wMonth - 1], SystemTime.wDay,SystemTime.wYear,SystemTime.wHour, SystemTime.wMinute,SystemTime.wSecond) < 0) { Buffer[TIME_STRING_LENGTH - 1] ='\0'; } } 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; } if (_snprintf(Buffer, 9, "%02d:%02d:%02d", SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond) < 0) { Buffer[9-1] = '\0'; } } DWORD GeneralizedTimeToSystemTime( IN PWCHAR szTime, OUT PSYSTEMTIME psysTime ) /*++ Routine Description: Converts a generalized time string to the equivalent system time. (Taken from the repadmin DS code. converted to swscanf) Parameters: szTime - [Supplies] This is string containing generalized time. psysTime - [Returns] This is the SYSTEMTIME struct to be returned. Return Value: Win 32 Error code, note could only result from invalid parameter. --*/ { ULONG len; ULONG yr=0, mo=0, day=0, hr=0, min=0, sec=0; LONG Fields; // // param sanity // if ((szTime == NULL) || (psysTime == NULL)) { return ERROR_INVALID_PARAMETER; } len = wcslen(szTime); if( len < 15 || szTime[14] != '.') { return ERROR_INVALID_PARAMETER; } // initialize memset(psysTime, 0, sizeof(SYSTEMTIME)); // // yyyymmddhhmmss. // Fields = swscanf(szTime, L"%04d%02d%02d%02d%02d%02d", &yr, &mo, &day, &hr, &min, &sec); psysTime->wYear = (USHORT) yr; psysTime->wMonth = (USHORT) mo; psysTime->wDay = (USHORT) day; psysTime->wHour = (USHORT) hr; psysTime->wMinute = (USHORT) min; psysTime->wSecond = (USHORT) sec; if (Fields != 6) { DPRINT2(1, "Time convert error on '%ws', Fields = %d\n", szTime, Fields); DPRINT6(1, "Time results: '%d' '%d' '%d' '%d' '%d' '%d' \n", yr, mo, day, hr, min, sec); } return (Fields == 6 ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER); } VOID FormatGeneralizedTime( IN PWCHAR GTimeStr, IN ULONG Length, OUT PCHAR Buffer ) /*++ Routine Description: Convert a generalized time string to a printable form. (taken from the DS code) Arguments: GTimeStr -- Generalized time string from DS. Length - Size of buffer in bytes. Buffer - buffer with returned string. Return Value: Buffer containing printable string. --*/ { #undef DEBSUB #define DEBSUB "FormatGeneralizedTime:" TIME_ZONE_INFORMATION tz; DWORD WStatus; BOOL bstatus; SYSTEMTIME sysTime, localTime; if ((Length < 12) || (Buffer == NULL) || (GTimeStr == NULL)) { return; } strcpy(Buffer, ""); GeneralizedTimeToSystemTime(GTimeStr, &sysTime); WStatus = GetTimeZoneInformation(&tz); if ( WStatus == TIME_ZONE_ID_INVALID ) { DPRINT_WS(1, "Cannot format time field. ", GetLastError()); } else { bstatus = SystemTimeToTzSpecificLocalTime( (WStatus == TIME_ZONE_ID_UNKNOWN) ? NULL : &tz, &sysTime, &localTime ); if ( bstatus ) { if ( _snprintf(Buffer, Length, "%d/%d/%d %d:%d:%d %S %S [%d]", localTime.wMonth, localTime.wDay, localTime.wYear, localTime.wHour, localTime.wMinute, localTime.wSecond, tz.StandardName, tz.DaylightName, tz.Bias) < 0) { Buffer[Length - 1]='\0'; } } else { if (_snprintf(Buffer, Length, "%d/%d/%d %d:%d:%d UNC", localTime.wMonth, localTime.wDay, localTime.wYear, localTime.wHour, localTime.wMinute, localTime.wSecond) < 0) { Buffer[Length - 1]='\0'; } } } } 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, ""); } } VOID GuidToStrW( IN GUID *pGuid, OUT PWCHAR ws ) /*++ Routine Description: Convert a GUID to a wide string. Functions expects that the passed in string is large enough to hold the string form of a GUID. WCHAR ws[GUID_CHAR_LEN + 1]; 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""); } } 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 = FALSE; 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"); } else { 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_U 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 = 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_U 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 = 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((ULONGLONG)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"", 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"", 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 FrsCreateFileRelativeById2( 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, IN ULONG ShareMode ) /*++ 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 ShareMode - The sharing mode. 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 "FrsCreateFileRelativeById2:" UNICODE_STRING UStr; DWORD WStatus; NTSTATUS NtStatus; NTSTATUS NtStatus2; HANDLE File, DirHandle; 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; // // 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. Also if we are checking the volume for object id support then check that the volume does not share the same Volume Serial Number with other volumes in VolSerialNumberToDriveTable. 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; DWORD VolumeInfoLength; PFILE_FS_VOLUME_INFORMATION VolumeInfo = NULL; PVOLUME_INFO_NODE VolumeInfoNode = NULL; ULONG Colon = 0; WCHAR LogicalDrive[5]; // "D:\" PGEN_ENTRY VolumeInfoNodeEntry = NULL; PWCHAR ListOfVolumes = NULL; PWCHAR TempListOfVolumes = NULL; WCHAR VSNStr[MAX_PATH];// "%04x-%04x" BOOL DuplicateVSNFound = FALSE; 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; // // If we are checking for object ids then we must be // verifying the root volume. Also check that this volume // does not share volumeserialnumber with any other volume // on this computer. Currently (05/18/2002) we rely on // VolumeSerialNumber being unique. We use it to find out // which volume to open the journal on. If there are mutiple // volumes on a machine with the same VSN then we could open // the journal on the wrong volume. Print a eventlog message // and return error. // if (Flags & FILE_SUPPORTS_OBJECT_IDS) { VolumeInfoLength = sizeof(FILE_FS_VOLUME_INFORMATION) + MAXIMUM_VOLUME_LABEL_LENGTH; VolumeInfo = FrsAlloc(VolumeInfoLength); Status = NtQueryVolumeInformationFile(PathHandle, &Iosb, VolumeInfo, VolumeInfoLength, FileFsVolumeInformation); if (NT_SUCCESS(Status)) { // // Look for the volume if the mapping table exists. // if (VolSerialNumberToDriveTable != NULL) { VolumeInfoNode = GTabLookup(VolSerialNumberToDriveTable, &(VolumeInfo->VolumeSerialNumber), NULL); } // // If the volume is not found in the table or if the table is not yet // initialized then initialize the table and add the drive taken from // this path to the list of drives in the table. In most cases the // drive extracted from the path will be same as the volume that hosts the // path. If that is not the case then we re-build the table by // enumerating all the drives on the computer below. // if (VolumeInfoNode == NULL) { // // Create the table and add the prefix drive to the table of drives. // Find the position of colon in the path to extract the drive letter. // The path may be of the form "d:\replicaroot" or "\\?\d:\replicaroot" // Colon = wcscspn(Path, L":"); if (Path[Colon] == L':') { CopyMemory(LogicalDrive, &Path[Colon - 1], 3 * sizeof(WCHAR)); // "D:\" LogicalDrive[3] = L'\0'; LogicalDrive[4] = L'\0'; } // else LogicalDrive remains NULL // // Add the drive and do not empty the table. // FrsBuildVolSerialNumberToDriveTable(LogicalDrive, FALSE); VolumeInfoNode = GTabLookup(VolSerialNumberToDriveTable, &(VolumeInfo->VolumeSerialNumber), NULL); } // // If the volume is still not found in the mapping table then it means that // the prefix of the path (E.g. "D:\" if the path is "D:\replicaroot" ) is not the // volume that hosts the path. In this case load the mapping table with all // the drives on the computer and look for the volume again. // if (VolumeInfoNode == NULL) { // // Could not find the drive in the table. Rebuild the table by enumerating // all the drives on the computer and try again. // DPRINT1(2, "WARN - Enumerating all drives on the computer to find the volume for path %ws\n", Path); // // Enumerate and add all drives to the table. Empty the table before starting. // FrsBuildVolSerialNumberToDriveTable(NULL, TRUE); VolumeInfoNode = GTabLookup(VolSerialNumberToDriveTable, &(VolumeInfo->VolumeSerialNumber), NULL); } if (VolumeInfoNode) { // // Check if there are duplicates. // LOCK_GEN_TABLE(VolSerialNumberToDriveTable); VolumeInfoNodeEntry = GTabLookupEntryNoLock(VolSerialNumberToDriveTable, &(VolumeInfo->VolumeSerialNumber), NULL); if ((VolumeInfoNodeEntry != NULL) && (VolumeInfoNodeEntry->Dups != NULL)) { // // The volume we are verifying shares VSN with // one or more other volumes. Collect data for the // eventlog message. // wsprintf(VSNStr, L"%04x-%04x", ((VolumeInfo->VolumeSerialNumber >> 16) & 0x0000ffff), (VolumeInfo->VolumeSerialNumber & 0x0000ffff)); do { VolumeInfoNode = VolumeInfoNodeEntry->Data; if (ListOfVolumes != NULL) { // // DriveName is of the form \\.\D: // if (wcslen(VolumeInfoNode->DriveName) >= 6) { TempListOfVolumes = FrsWcsCat3(ListOfVolumes,L", ",&VolumeInfoNode->DriveName[4]); } else { TempListOfVolumes = FrsWcsCat3(ListOfVolumes,L", ",VolumeInfoNode->DriveName); } FrsFree(ListOfVolumes); } else { if (wcslen(VolumeInfoNode->DriveName) >= 6) { TempListOfVolumes = FrsWcsDup(&VolumeInfoNode->DriveName[4]); }else{ TempListOfVolumes = FrsWcsDup(VolumeInfoNode->DriveName); } } ListOfVolumes = TempListOfVolumes; VolumeInfoNodeEntry = VolumeInfoNodeEntry->Dups; } while ( VolumeInfoNodeEntry != NULL ); EPRINT2(EVENT_FRS_DUPLICATE_VSN,VSNStr,ListOfVolumes); FrsFree(ListOfVolumes); DuplicateVSNFound = TRUE; } UNLOCK_GEN_TABLE(VolSerialNumberToDriveTable); } else { DPRINT1(0, "ERROR - Volume not found for path %ws\n", Path); WStatus = ERROR_FILE_NOT_FOUND; } } else { DPRINT1_NT(1,"WARN - NtQueryVolumeInformationFile failed for %ws;", Path, Status); WStatus = FrsSetLastNTError(Status); } } 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"" : Path), ((VolumeName == NULL) ? L"" : VolumeName)); } else if (DuplicateVSNFound == FALSE){ // // Something else is wrong. Print generic root // invalid message. // EPRINT1(EVENT_FRS_ROOT_NOT_VALID, Path); } } FrsFree(FsAttributeInfo); FrsFree(VolumeInfo); 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 Assumes that the debug lock is already held. 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_NOLOCK(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 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 FrsGetReparseData( IN HANDLE Handle, OUT PREPARSE_GUID_DATA_BUFFER *ReparseData, OUT ULONG *ReparseTag ) /*++ Routine Description: Return the value of the reparse tag. Arguments: Handle - Handle for a reparse point ReparseData - returned reparse data buffer if ERROR_SUCCESS NOTE: ReparseData can be NULL on success. Return Value: Win32 Status --*/ { #undef DEBSUB #define DEBSUB "FrsGetReparseData:" NTSTATUS NtStatus; DWORD ReparseDataLength; IO_STATUS_BLOCK IoStatusBlock; // // Allocate a buffer and get the information. // ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; *ReparseData = FrsAlloc(ReparseDataLength); // // Query the reparse point. // NtStatus = NtFsControlFile(Handle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_GET_REPARSE_POINT, NULL, 0, (PVOID)*ReparseData, ReparseDataLength ); if (!NT_SUCCESS(NtStatus)) { DPRINT_NT(4, "++ Could not get reparse point;", NtStatus); FrsFree(*ReparseData); return FrsSetLastNTError(NtStatus); } *ReparseTag = (*ReparseData)->ReparseTag; // // We only accept operations on files with certain reparse points. // For example a rename of a SIS file into a replica tree needs to prop // a create CO. // if (!ReparseTagReplicate(*ReparseTag)) { DPRINT1(4, "++ Reparse tag %08x is unsupported.\n", *ReparseTag); FrsFree(*ReparseData); *ReparseData = NULL; return ERROR_GEN_FAILURE; } // // If we are replicating the file data we will // not replicate the reparse poiont. // if(ReparseTagReplicateFileData(*ReparseTag)) { DPRINT1(4, "++ Reparse tag %08x. Will not replicate reparse point.\n", *ReparseTag); FrsFree(*ReparseData); *ReparseData = NULL; } 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 certain reparse points. // For example a rename of a SIS file into a replica tree needs to prop // a create CO. // if (!ReparseTagReplicate(ReparseTag)) { 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 = INVALID_HANDLE_VALUE; 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""); // // 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"", 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"", FirstArg->Length); //DPRINT2(5, "RemainingArg string: %ws {%d)\n", // (RemainingArg->Buffer != NULL) ? RemainingArg->Buffer : L"", 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"", 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"", // 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: if (FrsIsShuttingDown) { DPRINT(0, "WARN - IDTable Load aborted; service shutting down\n"); WStatus = ERROR_PROCESS_ABORTED; goto CLEANUP; } // // 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: if (FrsIsShuttingDown) { DPRINT(0, "WARN - IDTable Load aborted; service shutting down\n"); WStatus = ERROR_PROCESS_ABORTED; goto CLEANUP; } ++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 AppPathAndName, 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: AppPathAndName - Application to launch. Full path. 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 = TRUE; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInfo; DWORD Len; DWORD TLen; WCHAR ExpandedApp[MAX_CMD_LINE+1]; WCHAR ExpandedCmd[MAX_CMD_LINE+1]; SECURITY_ATTRIBUTES SecurityAttributes; TLen = ARRAY_SZ(ExpandedApp); // // Setup the process I/O Handles. // if (!HANDLE_IS_VALID(StandardIn)) { // // Provide a handle to the NUL device for input. // // Set this handle to be inheritable as it is being passed // to a child process that will inherit the handle. // SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttributes.bInheritHandle = TRUE; SecurityAttributes.lpSecurityDescriptor = NULL; // not same as NULL DACL StandardIn = CreateFileW( L"NUL", // lpszName GENERIC_READ | GENERIC_WRITE, // fdwAccess FILE_SHARE_READ | FILE_SHARE_WRITE, // fdwShareMode &SecurityAttributes, // 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(AppPathAndName, ExpandedApp, TLen); if (Len == 0) { WStatus = GetLastError(); DPRINT1_WS(1, "++ ws command not expanded.", AppPathAndName, WStatus); goto RETURN; } Len = ExpandEnvironmentStrings(CommandLine, ExpandedCmd, TLen); if (Len == 0) { WStatus = GetLastError(); DPRINT1_WS(1, "++ ws command not expanded.", CommandLine, WStatus); goto RETURN; } DPRINT2(0,"++ Running: %ws, CommandLine: %ws\n", ExpandedApp, ExpandedCmd); // // Get debug lock so our output stays in one piece. // if (NeedDbgLock) {DebLock();} try { BStatus = CreateProcessW( ExpandedApp, // 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 ) { WStatus = GetLastError(); DPRINT_NOLOCK3(0, "++ CreateProcessW Failed to run: '%ws', CommandLine: '%ws' WStatus: %s", ExpandedApp, ExpandedCmd, ErrLabelW32(WStatus)); __leave; } 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)); } } finally { // // If the above took an exception make sure we drop the lock. // if (NeedDbgLock) {DebUnLock();} } if ( !BStatus ) { // // Create process failed. We're done. // return WStatus; } GetExitCodeProcess( ProcessInfo.hProcess, &WStatus ); if ( BStatus ) { DPRINT2(0, "++ CreateProcess( %ws, %ws) succeeds\n", ExpandedApp, ExpandedCmd); 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, …) // 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, …) // Build the access request SetEntriesInAccessList([in]ExplicitAccess, [in] OldAccessList, [out]NewAccessList) // Add it to the list SetNameSecurityInfoEx([in]object, [in[ NewAccessList…) // 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 InheritFromParent, BOOL PushToChildren ) /*++ 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'. InheritFromParent - FALSE : Protects the DACL from inheriting ACEs. TRUE : Inherits ACEs from the parent whenever applicable. PushToChildren - FALSE : No ineritance. TRUE : Both containers and noncontainer objects that are contained by the primary object inherit the ACE. This flag corresponds to the combination of the CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE flags. 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; if (PushToChildren == TRUE) { ExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; } else { ExplicitAccess[0].grfInheritance = NO_INHERITANCE; } 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; if (PushToChildren == TRUE) { ExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; } else { ExplicitAccess[1].grfInheritance = NO_INHERITANCE; } 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 (InheritFromParent == TRUE) { SecurityInfo |= UNPROTECTED_DACL_SECURITY_INFORMATION; } else { 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 = INVALID_HANDLE_VALUE; HKEY HBurKey = INVALID_HANDLE_VALUE; HKEY HCumuKey = INVALID_HANDLE_VALUE; 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); FRS_REG_CLOSE(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\ // CfgRegOpenKey(FKC_CUMSET_N_BURFLAGS, RegBuf, FRS_RKF_CREATE_KEY, &HCumuKey); FRS_REG_CLOSE(HCumuKey); // // Delete key from Backup/Restore section. // // FRS_CONFIG_SECTION\backup/restore\Process at Startup\Cumulative Replica Sets\ // 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; } } FRS_REG_CLOSE(hKey); // // 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\\BurFlags // WStatus = CfgRegWriteDWord(FKC_CUMSET_N_BURFLAGS, RegBuf, 0, GblBurFlags); DPRINT_WS(0, "WARN - Cumulative Replica Sets BurFlags Write.", WStatus); ++KeyIdx; } FRS_REG_CLOSE(hKey); } // 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\\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\\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\ // 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; } } FRS_REG_CLOSE(hKey); // // 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: FRS_REG_CLOSE(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 FrsRemoveDisabledPrivileges ( VOID ) /*++ Routine Description: Remove all disabled privileges from our token. NOTE: THis capability is not available in WIN2K. Arguments: None. Return Value: TRUE if all disabled privileges were successfully removed. --*/ { #undef DEBSUB #define DEBSUB "FrsRemoveDisabledPrivileges:" NTSTATUS NtStatus = STATUS_SUCCESS; DWORD WStatus = ERROR_SUCCESS; DWORD BufferSize = 0; HANDLE hProcessToken = INVALID_HANDLE_VALUE; PTOKEN_PRIVILEGES pTokenPrivs = NULL; DWORD i = 0; #define PRIVILEGE_NAME_LENGTH MAX_PATH WCHAR PrivilegeName[PRIVILEGE_NAME_LENGTH]; DWORD PrivilegeNameLength = PRIVILEGE_NAME_LENGTH; // // Open the token. // NtStatus = NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken); if (!NT_SUCCESS(NtStatus)) { DPRINT1(0, "Unable to open process token. NtStatus = 0x%08x\n", NtStatus); WStatus = FrsTranslateNtError(NtStatus, FALSE); goto Cleanup; } // // Read the priveleges. // // // First find out the buffer size we need. // GetTokenInformation(hProcessToken, TokenPrivileges, NULL, 0, &BufferSize ); // // Allocate the buffer and get the info // pTokenPrivs = FrsAlloc(BufferSize); if(!GetTokenInformation(hProcessToken, TokenPrivileges, pTokenPrivs, BufferSize, &BufferSize )) { WStatus = GetLastError(); DPRINT1(0, "Unable to get token information. WStatus = %d\n", WStatus); goto Cleanup; } // // Find all non-enabled privileges and mark them for removal // for(i=0; i < pTokenPrivs->PrivilegeCount; i++) { if(!(pTokenPrivs->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED)) { pTokenPrivs->Privileges[i].Attributes = SE_PRIVILEGE_REMOVED; if(!LookupPrivilegeName(NULL, &(pTokenPrivs->Privileges[i].Luid), PrivilegeName, &PrivilegeNameLength )) { DPRINT2(0, "Marking privilege %d-%d for removal.\n", pTokenPrivs->Privileges[i].Luid.HighPart, pTokenPrivs->Privileges[i].Luid.LowPart ); } else { DPRINT1(0, "Marking privilege %ws for removal.\n", PrivilegeName); } } } // // Now, actually remove the privileges // if(!AdjustTokenPrivileges(hProcessToken, FALSE, pTokenPrivs, BufferSize, NULL, NULL)) { WStatus = GetLastError(); DPRINT1(0, "Unable to adjust token privileges. WStatus = %d\n", WStatus); goto Cleanup; } DPRINT(0, "Privileges successfully updated.\n"); Cleanup: FRS_CLOSE(hProcessToken); FrsFree(pTokenPrivs); return WIN_SUCCESS(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 FrsRemoveDisabledPrivileges(); #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, "", 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 if there is // enough room in the buffer. "0xFFFFFFFF " needs 12 characters // including the null. // if ((Length - strlen(Buffer)) >= 12) { 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); } VOID FrsForceDeleteFileByWildCard( PWCHAR DirPath, PWCHAR WildCard ) /*++ Routine Description: Delete all files that match the wildcard. The Path and wildcard are combined. to form the search path. Arguments: Return Value: None --*/ { #undef DEBSUB #define DEBSUB "FrsForceDeleteFileByWildCard:" PWCHAR SearchPath = NULL; HANDLE SearchHandle = INVALID_HANDLE_VALUE; WIN32_FIND_DATA FindData; PWCHAR FilePath = NULL; DWORD WStatus; if ((DirPath == NULL) || (wcslen(DirPath) == 0)) { return; } SearchPath = FrsWcsPath(DirPath, WildCard); SearchHandle = FindFirstFile(SearchPath, &FindData); if (!HANDLE_IS_VALID(SearchHandle)) { WStatus = GetLastError(); if (WStatus != ERROR_FILE_NOT_FOUND) { DPRINT1_WS(1, "++ ERROR - FindFirstFile(%ws);", SearchPath, WStatus); } goto CLEANUP; } do { DPRINT1(4, "++ Deleting file %ws\n", FindData.cFileName); FilePath = FrsWcsPath(DirPath, FindData.cFileName); FrsForceDeleteFile(FilePath); FilePath = FrsFree(FilePath); } while (FindNextFile(SearchHandle, &FindData)); CLEANUP: FRS_FIND_CLOSE(SearchHandle); SearchPath = FrsFree(SearchPath); } VOID FrsDeleteAllTempFiles( ) /*++ Routine Description: This routine enumerates the registry and deletes the preinstall directory and all staging files for all the replica sets. Call this only when we do not intent to use any state in the database. Calling this function while a replica set is in use can cause unexpected results. Arguments: Return Value: Win Status --*/ { #undef DEBSUB #define DEBSUB "FrsDeleteAllTempFiles:" HKEY hKey = INVALID_HANDLE_VALUE; DWORD WStatus = ERROR_SUCCESS; DWORD KeyIdx; WCHAR RegBuf[MAX_PATH + 1]; PWCHAR RootPath = NULL; PWCHAR PreInstallPath = NULL; PWCHAR StagePath = NULL; PWCHAR WildCard = NULL; // // Open the key "System\\CurrentControlSet\\Services\\NtFrs\\Parameters\\Replica Sets" // We will enumerate through all the subkeys under it and cleanup every // replica set. // WStatus = CfgRegOpenKey(FKC_SET_SECTION_KEY, NULL, 0, &hKey); if (!WIN_SUCCESS(WStatus)) { // // No replica sets to cleanup. // return; } KeyIdx = 0; while (TRUE) { WStatus = RegEnumKey(hKey, KeyIdx, RegBuf, MAX_PATH + 1); if (!WIN_SUCCESS(WStatus)) { if (WStatus != ERROR_NO_MORE_ITEMS) { DPRINT_WS(0, "WARN - Replica Sets enum.", WStatus); } break; } ++KeyIdx; WStatus = CfgRegReadString(FKC_SET_N_REPLICA_SET_ROOT, RegBuf, 0, &RootPath); if (!WIN_SUCCESS(WStatus)) { // // Failure reading reg key. // DPRINT2_WS(0, "WARN - Failed to read key %ws for %ws\n", FKC_SET_N_REPLICA_SET_ROOT, RegBuf, WStatus); continue; } PreInstallPath = FrsWcsPath(RootPath,NTFRS_PREINSTALL_DIRECTORY); DPRINT1(4,"++ Deleting Preinstall directory %ws\n", PreInstallPath); FrsDeletePath(PreInstallPath, ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE); RootPath = FrsFree(RootPath); PreInstallPath = FrsFree(PreInstallPath); WStatus = CfgRegReadString(FKC_SET_N_REPLICA_SET_STAGE, RegBuf, 0, &StagePath); if (!WIN_SUCCESS(WStatus)) { // // Failure reading reg key. // DPRINT2_WS(0, "WARN - Failed to read key %ws for %ws\n", FKC_SET_N_REPLICA_SET_ROOT, RegBuf, WStatus); continue; } // // Delete all the staging files in the staging area. // WildCard = FrsWcsCat(GENERIC_PREFIX, L"*.*"); FrsForceDeleteFileByWildCard(StagePath, WildCard); WildCard = FrsFree(WildCard); RootPath = FrsFree(RootPath); PreInstallPath = FrsFree(PreInstallPath); StagePath = FrsFree(StagePath); } RootPath = FrsFree(RootPath); PreInstallPath = FrsFree(PreInstallPath); StagePath = FrsFree(StagePath); FRS_REG_CLOSE(hKey); } BOOL ReparseTagReplicateFileData( DWORD ReparseTag ) { PREPARSE_TAG_TABLE_ENTRY ReparseTagTableEntry = NULL; ReparseTagTableEntry = GTabLookup(ReparseTagTable, &ReparseTag, NULL); if(ReparseTagTableEntry && (0 == _wcsicmp(ReparseTagTableEntry->ReplicationType, REPARSE_TAG_REPLICATION_TYPE_FILE_DATA))) { return TRUE; } return FALSE; } BOOL ReparseTagReplicateReparsePoint( DWORD ReparseTag ) { PREPARSE_TAG_TABLE_ENTRY ReparseTagTableEntry = NULL; ReparseTagTableEntry = GTabLookup(ReparseTagTable, &ReparseTag, NULL); if(ReparseTagTableEntry && (0 == _wcsicmp(ReparseTagTableEntry->ReplicationType, REPARSE_TAG_REPLICATION_TYPE_REPARSE_POINT))) { return TRUE; } return FALSE; } VOID FrsCheckLocalResources() /*++ Routine Description: Checks the available disk space on the database volume and the root and staging volume for each active replica set. Uses the ReplicasByGuid table to enumerte the replicas. If the available disk space is less than 1% of the total disk space it prints the eventlog message EVENT_FRS_OUT_OF_DISK_SPACE. Arguments: None. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsCheckLocalResources:" PREPLICA Replica; PVOID Key; ULARGE_INTEGER FreeBytesAvailableToCaller; ULARGE_INTEGER TotalNumberOfBytes; PWCHAR Volume; if (GetDiskFreeSpaceEx(WorkingPath,&FreeBytesAvailableToCaller,&TotalNumberOfBytes,NULL)) { // // Check space for database. // Print the event log message if the available free space is // less than 1%. // if ((FreeBytesAvailableToCaller.QuadPart*100) < TotalNumberOfBytes.QuadPart) { Volume = FrsWcsVolume(WorkingPath); if ((Volume != NULL) && (wcslen(Volume) >= wcslen(L"\\\\.\\D:"))) { // // If we are able to get the volume in the form // \\.\D: then use the volume in the event log so // that we don't print more than one event log // message per volume. If we can't get the // volume then we print the path. // EPRINT1(EVENT_FRS_OUT_OF_DISK_SPACE, &Volume[4]); }else{ EPRINT1(EVENT_FRS_OUT_OF_DISK_SPACE, WorkingPath); } FrsFree(Volume); } DPRINT3(5, "Disk space check for database. %ws FreeSpace = %08x %08x bytes,TotalDiskSpace = %08x %08x bytes\n", WorkingPath, PRINTQUAD(FreeBytesAvailableToCaller.QuadPart), PRINTQUAD(TotalNumberOfBytes.QuadPart)); FreeBytesAvailableToCaller.QuadPart = FreeBytesAvailableToCaller.QuadPart/(1024*1024); TotalNumberOfBytes.QuadPart = TotalNumberOfBytes.QuadPart/(1024*1024); DPRINT3(4, "Disk space check for database. %ws FreeSpace = %d MB,TotalDiskSpace = %d MB\n", WorkingPath, FreeBytesAvailableToCaller.LowPart, TotalNumberOfBytes.LowPart); } Key = NULL; while (Replica = GTabNextDatum(ReplicasByGuid, &Key)) { if (GetDiskFreeSpaceEx(Replica->Root,&FreeBytesAvailableToCaller,&TotalNumberOfBytes,NULL)) { // // For each replica check free disk space on the // volume hosting the root path. // Print the event log message if the available free space is // less than 1%. // if ((FreeBytesAvailableToCaller.QuadPart*100) < TotalNumberOfBytes.QuadPart) { if ((Replica->Volume != NULL) && (wcslen(Replica->Volume) >= wcslen(L"\\\\.\\D:"))) { // // If we are able to get the volume in the form // \\.\D: then use the volume in the event log so // that we don't print more than one event log // message per volume. If we can't get the // volume then we print the path. // EPRINT1(EVENT_FRS_OUT_OF_DISK_SPACE, &Replica->Volume[4]); }else{ EPRINT1(EVENT_FRS_OUT_OF_DISK_SPACE, Replica->Root); } } DPRINT3(5, "Disk space check for replica root. %ws FreeSpace = %08x %08x bytes,TotalDiskSpace = %08x %08x bytes\n", Replica->Root, PRINTQUAD(FreeBytesAvailableToCaller.QuadPart), PRINTQUAD(TotalNumberOfBytes.QuadPart)); FreeBytesAvailableToCaller.QuadPart = FreeBytesAvailableToCaller.QuadPart/(1024*1024); TotalNumberOfBytes.QuadPart = TotalNumberOfBytes.QuadPart/(1024*1024); DPRINT3(4, "Disk space check for replica root. %ws FreeSpace = %d MB,TotalDiskSpace = %d MB\n", Replica->Root, FreeBytesAvailableToCaller.LowPart, TotalNumberOfBytes.LowPart); } if (GetDiskFreeSpaceEx(Replica->Stage,&FreeBytesAvailableToCaller,&TotalNumberOfBytes,NULL)) { // // For each replica check free disk space on the // volume hosting the staging path. // Print the event log message if the available free space is // less than 1%. // if ((FreeBytesAvailableToCaller.QuadPart*100) < TotalNumberOfBytes.QuadPart) { Volume = FrsWcsVolume(Replica->Stage); if ((Volume != NULL) && (wcslen(Volume) >= wcslen(L"\\\\.\\D:"))) { // // If we are able to get the volume in the form // \\.\D: then use the volume in the event log so // that we don't print more than one event log // message per volume. If we can't get the // volume then we print the path. // EPRINT1(EVENT_FRS_OUT_OF_DISK_SPACE, &Volume[4]); }else{ EPRINT1(EVENT_FRS_OUT_OF_DISK_SPACE, Replica->Stage); } FrsFree(Volume); } DPRINT3(5, "Disk space check for replica stage. %ws FreeSpace = %08x %08x bytes,TotalDiskSpace = %08x %08x bytes\n", Replica->Stage, PRINTQUAD(FreeBytesAvailableToCaller.QuadPart), PRINTQUAD(TotalNumberOfBytes.QuadPart)); FreeBytesAvailableToCaller.QuadPart = FreeBytesAvailableToCaller.QuadPart/(1024*1024); TotalNumberOfBytes.QuadPart = TotalNumberOfBytes.QuadPart/(1024*1024); DPRINT3(4, "Disk space check for replica stage. %ws FreeSpace = %d MB,TotalDiskSpace = %d MB\n", Replica->Stage, FreeBytesAvailableToCaller.LowPart, TotalNumberOfBytes.LowPart); } } } DWORD FrsFreezeForBackup() /*++ Routine Description: This API is called by the FRS writer component. This API sets the FrsFrozenForBackup flag and then waits for the install command server to finish processing all install that are currently in progress. When the install command server completes all currently active installs it sets the FrsNoInstallsInProgressEvent event. This is an synchronous API that does not return until the install command server has drained all currently active installs. Once this API returns it is guaranteed that FRS will not install any more files until the FrsThawAfterBackup API is called at which point it resumes installs. Try not to leave FRS in frozen state for a long time. While FRS is in frozen state no files are installed and staging files can keep increasing. Since the staging reclaim logic does not delete staging files that are marked install-incomplete this can lead to staging full errors. Frozen state is not persistent across service shutdown and startup. There is no reference count on the number of times freeze is called. One call to thaw will cause the service to resume installs even if freeze was called multiple times. Arguments: None. Return Value: WStatus. --*/ { #undef DEBSUB #define DEBSUB "FrsFreezeForBackup:" DPRINT2(4,"Before: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); if (FrsFrozenForBackup) { DPRINT2(4,"Already in frozen state: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); return ERROR_SUCCESS; } ResetEvent(FrsThawEvent); FrsFrozenForBackup = TRUE; ResetEvent(FrsNoInstallsInProgressEvent); if (FrsFilesInInstall >= 0) { DPRINT2(4,"Waiting to freeze: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); WaitForSingleObject(FrsNoInstallsInProgressEvent, INFINITE); } DPRINT2(4,"After: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); return ERROR_SUCCESS; } DWORD FrsThawAfterBackup() /*++ Routine Description: This API is used to take the service out of frozen state. This API sets the FrsThawEvent event. The install command server threads are waiting for this event. See the description of FrsFreezeForBackup for more details. Arguments: None. Return Value: WStatus. --*/ { #undef DEBSUB #define DEBSUB "FrsThawAfterBackup:" DPRINT2(4,"Before: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); if (FrsFrozenForBackup == FALSE) { DPRINT2(4,"Already in thaw state: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); return ERROR_SUCCESS; } FrsFrozenForBackup = FALSE; SetEvent(FrsThawEvent); DPRINT2(4,"After: FrsFrozen = %d, FrsFilesInInstall = %d\n", FrsFrozenForBackup,FrsFilesInInstall); return ERROR_SUCCESS; }