|
|
// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: fileops.cxx
//
// Contents: OBJID and file operations
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include "trklib.hxx"
#include "mountmgr.h"
//+-------------------------------------------------------------------
//
// Function: ConvertToNtPath, public
//
// Synopsis: Convert the path in the buffer to an Nt style path, in the
// other buffer.
//
// Arguments: [pwszVolumePath] -- In. Buffer containing Dos style path.
// [pwszNtPath] -- Out. Buffer for new path.
// [cwcBuf] -- In. Size of buffer in wide chars.
//
// Returns: Return value is length in characters (not including nul)
// of converted path. Zero if not completely converted.
//
//--------------------------------------------------------------------
unsigned ConvertToNtPath(const TCHAR *ptszVolumePath, WCHAR *pwszNtPath, ULONG cwcBuf) { unsigned i=12; // for \DosDevices\ .
WCHAR *pwszWrite = pwszNtPath; ULONG cwcLeft = cwcBuf; BOOL fDone = FALSE; unsigned ret;
if (ptszVolumePath[0] == TEXT('\\') && ptszVolumePath[1] == TEXT('\\')) { i+=3; ptszVolumePath++;
// i = 15
// ptszVolumePath -----\ .
// |
// v
// \\billmo2\rootd
//
}
if (cwcLeft > i) { memcpy(pwszWrite, L"\\DosDevices\\UNC", i*sizeof(WCHAR));
pwszWrite += i; cwcLeft -= i;
while (cwcLeft) { *pwszWrite = (WCHAR) *ptszVolumePath; cwcLeft --;
if (*ptszVolumePath == 0) { // we just copied a null
fDone = TRUE; break; } else { ptszVolumePath++; pwszWrite++; } } }
ret = (fDone ? (unsigned)(pwszWrite - pwszNtPath) : 0);
return(ret); }
//+----------------------------------------------------------------------------
//
// IsLocalObjectVolume
//
// Determine if the specified volume (specified as a mount manager volume
// name) is capable of object IDs (i.e. NTFS5). The tracking service
// currently only supports fixed volumes, so this routine really only
// returns true for fixed NTFS5 volumes.
//
// The input is a "volume name", as opposed to a volume device name
// (i.e. it has a trailing slash). E.g.:
//
// \\?\Volume{8baec120-078b-11d2-824b-000000000000}\
//
// The proper way to implement this routine is to open the filesystem
// on this device, query for its FS attributes, and check for the
// supports-object-ids bit. But the ugly side-effect of this is that
// we end up opening every volume on the system during system bootup,
// including the floppies. So as a workaround, we look up the device
// in the object directory first, and only bother to check for the
// bit on "\Device\HarddiskVolume" type devices.
//
//+----------------------------------------------------------------------------
const TCHAR *s_tszHarddiskDevicePrefix = TEXT("\\Device\\HarddiskVolume");
BOOL IsLocalObjectVolume( const TCHAR *ptszVolumeName ) {
TCHAR tszTargetSymLink[ MAX_PATH ]; TCHAR tszVolumeDeviceName[ MAX_PATH + 1 ]; ULONG cchVolumeName; DWORD dwFsFlags = 0;
// Validate the volume name prefix.
//TrkAssert( !_tcsnicmp( TEXT("\\\\?\\"), ptszVolumeName, 4 ) );
/*
// For the QueryDosDevice call, we need to strip the "\\?\"
// from the beginning of the name.
cchVolumeName = _tcslen( &ptszVolumeName[4] );
memcpy( tszVolumeDeviceName, &ptszVolumeName[4], cchVolumeName * sizeof(TCHAR) );
// Also for the QueryDosDevice call, we nee to strip the
// whack from the end of the name.
TrkAssert( TEXT('\\') == tszVolumeDeviceName[cchVolumeName-1] ); tszVolumeDeviceName[ cchVolumeName - 1 ] = TEXT('\0');
// Query for this device's symlink.
if( !QueryDosDevice( tszVolumeDeviceName, tszTargetSymLink, sizeof(tszTargetSymLink)/sizeof(TCHAR) )) { TrkLog(( TRKDBG_MISC, TEXT("Couldn't query %s for symlink in obj dir (%lu)"), tszVolumeDeviceName, GetLastError() )); return FALSE; }
TrkLog(( TRKDBG_MISC, TEXT("Volume %s is %s"), ptszVolumeName, tszTargetSymLink ));
// Is this a harddisk? I.e., does the symlink have the \Device\HarddiskVolume
// prefix?
if( _tcsnicmp( tszTargetSymLink, s_tszHarddiskDevicePrefix, _tcslen(s_tszHarddiskDevicePrefix) )) { // No, assume therefore that it's not an object (NTFS5) volume.
return FALSE; }
// Otherwise, is it a fixed harddisk?
else if( DRIVE_FIXED != GetDriveType(ptszVolumeName) ) // No - we don't currently handle removeable media.
return FALSE; */
if( DRIVE_FIXED != GetDriveType(ptszVolumeName) ) return FALSE;
// Finally, check to see if it supports object IDs
if( GetVolumeInformation(ptszVolumeName, NULL, 0, NULL, NULL, &dwFsFlags, NULL, 0 ) && (dwFsFlags & FILE_SUPPORTS_OBJECT_IDS) ) { // Yes, it's a fixed harddisk that supports OIDs
return TRUE; } else // It's a fixed harddisk, but it doesn't supports
// OIDs (it's probably FAT).
return FALSE; }
#if 0
BOOL IsLocalObjectVolume( const TCHAR *ptszVolumeDeviceName ) { BOOL fObjectIdCapable = FALSE; TCHAR tszVol[4]; TCHAR tszRootOfVolume[MAX_PATH+1]; DWORD dw; DWORD dwFsFlags; UINT DriveType;
_tcscpy( tszRootOfVolume, ptszVolumeDeviceName ); _tcscat( tszRootOfVolume, TEXT("\\") );
// Get the drive type
DriveType = GetDriveType(tszRootOfVolume);
// Return TRUE if the drive type is fixed, and if
// the filesystem attribute bit is set that indicates
// that object IDs are supported.
return ( ( DRIVE_FIXED == DriveType /*|| DRIVE_REMOVABLE == DriveType*/ ) && GetVolumeInformation(tszRootOfVolume, NULL, 0, NULL, NULL, &dwFsFlags, NULL, 0 ) && (dwFsFlags & FILE_SUPPORTS_OBJECT_IDS) ); } #endif
//+-------------------------------------------------------------------
//
// Function: MapLocalPathToUNC
//
// Synopsis: Convert a volume-relative path to a UNC path.
// Since there could be multiple UNC paths which cover
// this local path, that which provides greatest
// coverage & access will be returns (e.g., "C:\"
// covers more than "C:\Docs".
//
// Arguments: [tszLocalPath] (in)
// A local path, including the drive letter.
// [tszUNC] (out)
// An equivalent UNC path which provides the
// greatest access.
//
// Returns: [HRESULT]
//
// Exceptions: None
//
//--------------------------------------------------------------------
HRESULT MapLocalPathToUNC( RPC_BINDING_HANDLE IDL_handle, const TCHAR *ptszLocalPath, TCHAR *ptszUNC ) { // -----
// Locals
// ------
HRESULT hr = S_OK; // Return value
CShareEnumerator cShareEnum;
ULONG ulBestMerit; TCHAR tszBestShare[ MAX_PATH + 1 ]; TCHAR tszBestPath[ MAX_PATH + 1 ]; ULONG cchBestPath = 0;
// ----------
// Initialize
// ----------
__try { // Open an enumeration of the disk shares on this machine.
// If we early-exit due to an exception, it will clean itself up.
// NOTE: The ptszLocalPath is only provided until we can fix
// GetAccessLevel so that it doesn't have to do opens.
cShareEnum.Initialize( IDL_handle );
ulBestMerit = cShareEnum.GetMinimumMerit() - 1;
// --------------------
// Enumerate the shares
// --------------------
while( cShareEnum.Next() ) { // Does this share cover the local path?
if( cShareEnum.CoversDrivePath( ptszLocalPath )) { // Is this a better share than anything we've seen so far?
if( cShareEnum.GetMerit() > ulBestMerit ) { // We have a new best share. We'll hang on to both
// the share and it's path.
_tcscpy( tszBestShare, cShareEnum.GetShareName() ); _tcscpy( tszBestPath, cShareEnum.GetSharePath() );
ulBestMerit = cShareEnum.GetMerit(); cchBestPath = cShareEnum.QueryCCHSharePath(); } } // if( cenumShares.QueryCCHSharePath() < cchBestPath )
} // while( cenumShares.Next() )
// Did we find a share which encompasses the file?
if( 0 == cchBestPath ) { hr = HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ); goto Exit; }
// -------------------
// Create the UNC path
// -------------------
_tcscpy( ptszUNC, cShareEnum.GetMachineName() ); _tcscat( ptszUNC, TEXT("\\") ); _tcscat( ptszUNC, tszBestShare ); if ( ptszLocalPath[ cchBestPath ] != TEXT('\\') ) _tcscat( ptszUNC, TEXT("\\") ); _tcscat( ptszUNC, &ptszLocalPath[ cchBestPath ] );
hr = S_OK; } // __try
__except( BreakOnDebuggableException() ) { hr = GetExceptionCode(); }
// ----
// Exit
// ----
Exit:
if( FAILED(hr) ) { TrkLog(( TRKDBG_ERROR, TEXT("MapLocalPathToUNC returned hr=%08x"), hr )); }
return( hr );
} // MapLocalPathToUNC
//+-------------------------------------------------------------------
//
// Function: OpenVolume, public
//
// [ptszVolumeDeviceName] is a Win32 name for a volume in the NT
// namespace, *without* the trailing whack. E.g.
//
// \\.\A:
//
// if you append a whack on the end of this, it opens the root
// of the volume, not the volume itself.
//
//--------------------------------------------------------------------
NTSTATUS OpenVolume( const TCHAR *ptszVolumeDeviceName, HANDLE * phVolume ) { NTSTATUS status; FILE_FS_DEVICE_INFORMATION DeviceInfo; IO_STATUS_BLOCK Iosb; HANDLE hDirect = NULL;
// First, open the file in direct mode (by only opening it for
// file_read_attributes). This will open the volume but not cause
// any filesystem to be loaded. This was done for the following scenario:
// A volume gets dismounted and goes offline for some reason. Trkwks
// gets the dismount notification and closes its handles. Something
// attempt to open the volume, and IO loads the RAW filesystem (if no other
// filesystem can be loaded, IO always loads the rawfs). This causes a
// mount notification. Trkwks gets this mount notification and
// tries to reopen its handles. It can open the volume handle, but it's
// just to the rawfs. It tries to open the oid index, but IO returns an
// invalid parameter error. Trkwks then closes all of its handles again,
// including the volume handle. When all handles are closed on rawfs in this
// way, it automatically dismounts (without sending a dismount notification).
// The problem is, when trkwks opened the volume handle, it mounted the
// volume (rawfs) and caused a new mount notification, which it now receives
// and tries to open its handles again. In this way trkwks goes into an
// infinite loop.
//
// The solution is to open the volume direct, and see if it's really mounted.
// If not, don't try to open the volume.
status = TrkCreateFile( ptszVolumeDeviceName, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, &hDirect );
if( !NT_SUCCESS(status) ) goto Exit;
// Check for the current mount status.
status = NtQueryVolumeInformationFile( hDirect, &Iosb, &DeviceInfo, sizeof(DeviceInfo), FileFsDeviceInformation );
NtClose( hDirect ); if( !NT_SUCCESS(status) ) goto Exit;
if( !(FILE_DEVICE_IS_MOUNTED & DeviceInfo.Characteristics) ) { // This volume isn't currently mounted, and we don't want to be
// the ones to mount it.
TrkLog(( TRKDBG_WARNING, TEXT("Attempted to open dismounted volume (%s)"), ptszVolumeDeviceName )); status = STATUS_VOLUME_DISMOUNTED; goto Exit; }
// The filesystem is already mounted, so it's OK for us to
// open our handle.
status = TrkCreateFile( ptszVolumeDeviceName, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, phVolume );
Exit:
TrkAssert(NT_SUCCESS(status) || *phVolume == NULL); return(status); }
//+----------------------------------------------------------------------------
//
// Function: CheckVolumeWriteProtection
//
// Check the filesystem attributes on a volume to see if it's write-
// protected.
//
//+----------------------------------------------------------------------------
NTSTATUS CheckVolumeWriteProtection( const TCHAR *ptszVolumeDeviceName, BOOL *pfWriteProtected ) { NTSTATUS status; HANDLE hVolume = NULL; IO_STATUS_BLOCK Iosb; FILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
status = OpenVolume( ptszVolumeDeviceName, &hVolume ); if( !NT_SUCCESS(status) ) { TrkLog(( TRKDBG_WARNING, TEXT("Couldn't open volume - 0x%08x (%s)"), status, ptszVolumeDeviceName )); goto Exit; }
status = NtQueryVolumeInformationFile( hVolume, &Iosb, &FsAttrs, sizeof(FsAttrs), FileFsAttributeInformation ); if( !NT_SUCCESS(status) && STATUS_BUFFER_OVERFLOW != status) { TrkLog(( TRKDBG_WARNING, TEXT("Couldn't query fs attrs - 0x%08x (%s)"), status, ptszVolumeDeviceName )); goto Exit; }
if( FILE_READ_ONLY_VOLUME & FsAttrs.FileSystemAttributes ) *pfWriteProtected = TRUE; else *pfWriteProtected = FALSE;
status = STATUS_SUCCESS;
Exit:
if( NULL != hVolume ) NtClose( hVolume );
return status;
}
//+-------------------------------------------------------------------
//
// Function: MapVolumeDeviceNameToIndex
//
// Map a volume device name, as defined by the mount manager, to a
// zero-relative index, where 0 represents 'A:'. A "volume device name"
// is e.g.
//
// \\?\Volume{96765fc3-9c72-11d1-b93d-000000000000}
//
// a "volume name" is the volume device name post-pended with a
// whack (it is in this form that the mount manager returns the name).
//
//+-------------------------------------------------------------------
/*
LONG MapVolumeDeviceNameToIndex( TCHAR *ptszVolumeDeviceName ) { // BUGBUG: Rewrite this to use IOCTL_MOUNTMGR_QUERY_POINTS
TCHAR tszVolumeNameOfCaller[ CCH_MAX_VOLUME_NAME + 1 ]; TCHAR tszVolumeNameForRoot[ CCH_MAX_VOLUME_NAME + 1 ]; TCHAR tszRoot[] = TEXT("*:\\");
// Convert the caller's volume name to a volume device name.
_tcscpy( tszVolumeNameOfCaller, ptszVolumeDeviceName ); _tcscat( tszVolumeNameOfCaller, TEXT("\\") );
// Loop through all the possible drive letters, trying to find the
// caller's volume name.
for( LONG iVol = 0; iVol < NUM_VOLUMES; iVol++ ) { tszRoot[0] = TEXT('A') + iVol;
if( GetVolumeNameForVolumeMountPoint( tszRoot, tszVolumeNameForRoot, sizeof(tszVolumeNameForRoot) )) { // We have a real volume with a name. See if it's the name we seek.
if( 0 == _tcscmp( tszVolumeNameForRoot, tszVolumeNameOfCaller )) return( iVol ); }
}
return( -1 );
} */
LONG MapVolumeDeviceNameToIndex( TCHAR *ptszVolumeDeviceName ) { HANDLE hMountManager = INVALID_HANDLE_VALUE; BYTE MountPointBuffer[ sizeof(MOUNTMGR_MOUNT_POINT) + MAX_PATH ]; PMOUNTMGR_MOUNT_POINT pMountPoint = (PMOUNTMGR_MOUNT_POINT) MountPointBuffer; BYTE MountPointsBuffer[ MAX_PATH ]; PMOUNTMGR_MOUNT_POINTS pMountPoints = (PMOUNTMGR_MOUNT_POINTS) MountPointsBuffer; BOOL fQuerySuccessful = FALSE; ULONG cbMountPoints = 0; ULONG cbVolumeDeviceName; LONG iVol = -1;
__try { // Open the mount manager.
hMountManager = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); if( INVALID_HANDLE_VALUE == hMountManager ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open MountManager") )); TrkRaiseLastError(); }
// Initialize the input (pMountPoint)
pMountPoint = (PMOUNTMGR_MOUNT_POINT) MountPointBuffer; memset(pMountPoint, 0, sizeof(MountPointBuffer) );
cbVolumeDeviceName = sizeof(TCHAR) * _tcslen(ptszVolumeDeviceName);
// Load the name of the device for which we wish to query. We convert
// this from Win32 "\\?\" form to NT "\??\" form.
pMountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); pMountPoint->DeviceNameLength = cbVolumeDeviceName;
_tcscpy( (TCHAR*)( MountPointBuffer + pMountPoint->DeviceNameOffset ), TEXT("\\??") ); _tcscat( (TCHAR*)( MountPointBuffer + pMountPoint->DeviceNameOffset ), &ptszVolumeDeviceName[3] );
// Query the mount manager for info on this device.
ULONG cQueryAttempts = 0; // Guarantee no infinite loop.
fQuerySuccessful = FALSE; cbMountPoints = sizeof(MountPointsBuffer);
while( !fQuerySuccessful ) { // Check for an infinite loop.
if( cQueryAttempts > 100 ) { TrkLog(( TRKDBG_ERROR, TEXT("Failed IOCTL_MOUNTMGR_QUERY_POINTS (%lu, loop detect)"), GetLastError() )); TrkRaiseLastError(); }
// Query the mount manager
fQuerySuccessful = DeviceIoControl( hMountManager, IOCTL_MOUNTMGR_QUERY_POINTS, pMountPoint, sizeof(MountPointBuffer), pMountPoints, cbMountPoints, &cbMountPoints, NULL);
// Did it work?
if( fQuerySuccessful ) // Yes, we got the info.
break;
// Otherwise, do we need a bigger out-buf?
else if( ERROR_MORE_DATA == GetLastError() ) { // Yes, the size of the necessary out-buf is at
// the beginning of the out-buf we provided.
cbMountPoints = pMountPoints->Size;
// The initial guess buffer is on the stack, not heap.
if( (PMOUNTMGR_MOUNT_POINTS) MountPointsBuffer != pMountPoints ) delete [] pMountPoints;
pMountPoints = (PMOUNTMGR_MOUNT_POINTS) new BYTE[ cbMountPoints ]; if( NULL == pMountPoints ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't alloc pMountPoints") )); TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY ); } }
// Of it's not a more-data error, then there's nothing more we can do.
else break;
} // while( !fQuerySuccessful )
// Raise if the query failed.
if( !fQuerySuccessful ) { TrkLog(( TRKDBG_WARNING, TEXT("Failed IOCTL_MOUNTMGR_QUERY_POINTS (%lu)"), GetLastError() )); TrkRaiseLastError(); }
// Loop through the returned mount points. There should be 2: one of
// the form "\??\Volume{8baec120-078b-11d2-824b-000000000000}",
// and one of the form "\DosDevices\C:" (for the C drive).
static const WCHAR wszSymLinkPrefix[] = { L"\\DosDevices\\" }; ULONG cchSymLinkPrefix = sizeof(wszSymLinkPrefix)/sizeof(WCHAR) - 1;
for( int i = 0; i < pMountPoints->NumberOfMountPoints; ++i ) { PMOUNTMGR_MOUNT_POINT pOutPoint = &pMountPoints->MountPoints[i]; WCHAR wc; const WCHAR *pwszSymLinkName = (PWCHAR)( (BYTE*)pMountPoints + pOutPoint->SymbolicLinkNameOffset );
if( pOutPoint->SymbolicLinkNameLength/sizeof(WCHAR) >= 14 && 0 == wcsncmp( pwszSymLinkName, wszSymLinkPrefix, cchSymLinkPrefix ) && pOutPoint->UniqueIdLength )
{ wc = pwszSymLinkName[ cchSymLinkPrefix ];
if( TEXT('a') <= wc && wc <= TEXT('z') ) { iVol = wc - TEXT('a'); break; } else if( TEXT('A') <= wc && wc <= TEXT('Z') ) { iVol = wc - TEXT('A'); break; }
} }
} __finally { if( INVALID_HANDLE_VALUE != hMountManager ) CloseHandle( hMountManager );
if( (PMOUNTMGR_MOUNT_POINTS) MountPointsBuffer != pMountPoints && NULL != pMountPoints ) { delete [] pMountPoints; }
}
return iVol; }
//+----------------------------------------------------------------------------
//
// IsSystemVolumeInformation
//
// Is the given volume-relative path under the "System Volume Information"
// directory?
//
// Note: This is hard-coded for now, but will be replaced by a forthcoming
// Rtl export.
//
//+----------------------------------------------------------------------------
BOOL IsSystemVolumeInformation( const TCHAR *ptszPath ) { return 0 == _wcsnicmp( s_tszSystemVolumeInformation, ptszPath, wcslen(s_tszSystemVolumeInformation)/sizeof(WCHAR) ); }
//+-------------------------------------------------------------------
//
// OpenFileById
//
// Given an NTFS5 object ID, open the file and returns its handle.
//
//--------------------------------------------------------------------
NTSTATUS OpenFileById( const TCHAR *ptszVolumeDeviceName, const CObjId &oid, ACCESS_MASK AccessMask, ULONG ShareAccess, ULONG AdditionalCreateOptions, HANDLE *ph) {
NTSTATUS status;
// A buffer for the id-based path. This path is the volume's
// path followed by the 16 byte object ID
TCHAR tszPath[ MAX_PATH + 1 ] = { TEXT('\0') }; // Init to prevent prefix error.
// Parameters for NtCreateFile
OBJECT_ATTRIBUTES ObjectAttr; IO_STATUS_BLOCK IoStatus;
UNICODE_STRING uPath; PVOID pFreeBuffer = NULL;
// Compose a buffer with a Win32-style name with a dummy value where
// the object ID will go (use "12345678" for now). It's Win32-style
// in that we will pass it to RtlDosPathNameToNtPathName below.
TrkAssert( NULL != ptszVolumeDeviceName ); _tcscpy( tszPath, ptszVolumeDeviceName ); _tcscat( tszPath, TEXT("\\") ); _tcscat( tszPath, TEXT("12345678") );
// Convert to the NT path to this volume
if( !RtlDosPathNameToNtPathName_U( tszPath, &uPath, NULL, NULL )) { status = STATUS_OBJECT_NAME_INVALID; goto Exit; } pFreeBuffer = uPath.Buffer;
// Put in the real object ID in place of the "12345678"
TrkAssert( oid.Size() == 16 ); oid.SerializeRaw( reinterpret_cast<BYTE*>(&uPath.Buffer[ (uPath.Length-oid.Size()) / sizeof(uPath.Buffer[0]) ]) );
// And open the file
InitializeObjectAttributes( &ObjectAttr, // Structure
&uPath, // Name (identifier)
OBJ_CASE_INSENSITIVE, // Attributes
0, // Root
0 ); // Security
status = NtCreateFile( ph, AccessMask, &ObjectAttr, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, ShareAccess, FILE_OPEN, FILE_OPEN_BY_FILE_ID | FILE_OPEN_NO_RECALL | FILE_SYNCHRONOUS_IO_NONALERT | AdditionalCreateOptions, NULL, 0 ); if( !NT_SUCCESS(status) ) { *ph = NULL; goto Exit; }
// ----
// Exit
// ----
Exit:
if( !NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND ) { TrkLog(( TRKDBG_MISC, TEXT("OpenFileById returned status=%08X"), status )); }
if( NULL != pFreeBuffer ) RtlFreeHeap( RtlProcessHeap(), 0, pFreeBuffer );
return( status );
} // OpenFileById
//+----------------------------------------------------------------------
//
// Function: SetVolId
//
// Synopsis: Set the volume ID on a local volume.
//
// Returns: NTSTATUS
//
// Note: Setting the volid (and changing a volume's lable) triggers
// a GUID_IO_VOLUME_CHANGE PNP device event.
//
//+----------------------------------------------------------------------------
NTSTATUS SetVolId( const TCHAR *ptszVolumeDeviceName, const CVolumeId &volid ) { NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING usPath; PVOID pFreeBuffer = NULL;
HANDLE hVol = NULL; FILE_FS_OBJECTID_INFORMATION file_fs_objectid_information;
OBJECT_ATTRIBUTES ObjectAttr; IO_STATUS_BLOCK IoStatus;
EnableRestorePrivilege();
// Generate the NT path name to this volume. The VolumeDeviceName has
// no trailing whack, so we'll be opening the volume, not the root dir of
// the volume.
if( !RtlDosPathNameToNtPathName_U( ptszVolumeDeviceName, &usPath, NULL, NULL )) { status = STATUS_OBJECT_NAME_INVALID; goto Exit; } pFreeBuffer = usPath.Buffer;
// Fill in an ObjectAttributes for the NtCreateFile request
InitializeObjectAttributes( &ObjectAttr, // Structure
&usPath, // Name (identifier)
OBJ_CASE_INSENSITIVE, // Attributes
0, // Root
0 ); // Security
// Open the volume.
// You wouldn't think that FILE_SHARE_WRITE would be necessary, but
// without it we encounter a STATUS_UNABLE_TO_DELETE_SECTION error.
// Also, we must use NtOpenFile; if we use NtCreateFile(...,FILE_OPEN,...),
// we get a STATUS_ACCESS_DENIED error on the NtSetVolumeInformationFile
// call.
status = NtOpenFile( &hVol, SYNCHRONIZE | FILE_GENERIC_READ | FILE_GENERIC_WRITE, &ObjectAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT );
if( !NT_SUCCESS(status) ) { hVol = NULL; TrkLog(( TRKDBG_ERROR, TEXT("SetVolId couldn't open the volume %s (status=%08x)"), ptszVolumeDeviceName, status )); goto Exit; }
// Set the Volume ID
file_fs_objectid_information = volid;
status = NtSetVolumeInformationFile( hVol, &IoStatus, &file_fs_objectid_information, sizeof(file_fs_objectid_information), FileFsObjectIdInformation ); if( !NT_SUCCESS(status) ) { TrkLog(( TRKDBG_ERROR, TEXT("SetVolId couldn't set volume ID on volume %s (status=%08x)"), ptszVolumeDeviceName, status )); goto Exit; }
// ----
// Exit
// ----
Exit:
if( NULL != hVol ) NtClose( hVol );
if( NULL != pFreeBuffer ) RtlFreeHeap( RtlProcessHeap(), 0, pFreeBuffer );
return( status ); }
//+----------------------------------------------------------------------------
//
// Function: TrkCreateFile
//
// Synopsis: Creates a file using NtCreateFile.
//
// Arguments: [pwszCompleteDosPath] (in)
// The path to open, in 'dos' format as opposed to
// NT format (e.g. "\\m\s\f" rather than "\DosDevices\..."
// [AccessMask] (in)
// Required access. SYNCRHONIZE is requested automatically.
// [Attributes] (in)
// [ShareAccess] (in)
// [CreationDisposition] (in)
// [CreateOptions] (in)
// [lpSecurityAttributes] (in)
// [ph] (out)
// The resulting file handle. Always set to NULL when function
// is entered.
//
// Returns: NTSTATUS
//
//+----------------------------------------------------------------------------
NTSTATUS TrkCreateFile( const WCHAR *pwszCompleteDosPath, ACCESS_MASK AccessMask, ULONG Attributes, ULONG ShareAccess, ULONG CreationDisposition, ULONG CreateOptions, LPSECURITY_ATTRIBUTES lpSecurityAttributes, HANDLE *ph) {
NTSTATUS status;
// Parameters for NtCreateFile
OBJECT_ATTRIBUTES ObjectAttr; IO_STATUS_BLOCK IoStatus;
// E.g. "\??\D:\..."
UNICODE_STRING uPath; PVOID pFreeBuffer = NULL;
*ph = NULL;
// -------------
// Open the File
// -------------
// Generate the NT path name
if( !RtlDosPathNameToNtPathName_U( pwszCompleteDosPath, &uPath, NULL, NULL )) { status = STATUS_OBJECT_NAME_INVALID; goto Exit; } pFreeBuffer = uPath.Buffer;
// Set up the ObjectAttributes
InitializeObjectAttributes( &ObjectAttr, // Structure
&uPath, // Name (identifier)
OBJ_CASE_INSENSITIVE, // Attributes
0, // Root
0 ); // Security
if( NULL != lpSecurityAttributes ) { ObjectAttr.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor; if ( lpSecurityAttributes->bInheritHandle ) { ObjectAttr.Attributes |= OBJ_INHERIT; } }
// Create/Open the file
status = NtCreateFile( ph, AccessMask | SYNCHRONIZE, &ObjectAttr, &IoStatus, NULL, Attributes, ShareAccess, CreationDisposition, CreateOptions, // | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, // No EA buffer
0 ); if( !NT_SUCCESS(status) ) { *ph = NULL; goto Exit; }
// ----
// Exit
// ----
Exit:
if( NULL != pFreeBuffer ) RtlFreeHeap( RtlProcessHeap(), 0, pFreeBuffer );
return( status );
} // TrkCreateFile
//+----------------------------------------------------------------------------
//
// Function: FindLocalPath
//
// Synopsis: Given a volume index and a file ObjectId, return a volume-relative
// path to the file, and return the file's Birth ID. The returned
// path does not have the drive letter prefix.
//
// Inputs: [ptszVolumeDeviceName] (in)
// The volume on which to search.
// [objid] (in)
// The ObjectID to search for on this volume.
// [pdroidBirth] (out)
// If the function is successful, returns the found file's
// birth ID.
// [ptszLocalPath] (out)
// If the function is successful, returns the volume-relative
// path (includes the drive letter).
//
// Returns: [NTSTATUS]
//
// Exceptions: None.
//
//+----------------------------------------------------------------------------
NTSTATUS FindLocalPath( IN const TCHAR *ptszVolumeDeviceName, IN const CObjId &objid, OUT CDomainRelativeObjId *pdroidBirth, OUT TCHAR *ptszLocalPath ) { // ------
// Locals
// ------
NTSTATUS status; IO_STATUS_BLOCK Iosb; HANDLE hFile = NULL;
// Open the file
status = OpenFileById( ptszVolumeDeviceName, objid, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, &hFile); if( !NT_SUCCESS(status) ) { hFile = NULL; goto Exit; }
// Get the file's birth ID
status = GetBirthId( hFile, pdroidBirth ); if( !NT_SUCCESS(status) ) goto Exit;
// Get the volume-relative path
status = QueryVolRelativePath( hFile, ptszLocalPath ); if( !NT_SUCCESS(status) ) goto Exit;
// When ptszLocalPath is a root directory, no relative path
// will be found. i.e. "d:" will remain "d:", instead of "d:\", which is
// what we want. We have to put the '\' in here.
if(TEXT('\0') == ptszLocalPath[0]) { ptszLocalPath[0] = TEXT('\\'); ptszLocalPath[1] = TEXT('\0'); }
// ----
// Exit
// ----
Exit:
if( NULL != hFile ) NtClose( hFile );
if( !NT_SUCCESS(status) && STATUS_OBJECT_PATH_NOT_FOUND != status && STATUS_OBJECT_NAME_NOT_FOUND != status && STATUS_INVALID_PARAMETER != status // Happens when the objid is really the volid
) { TrkLog(( TRKDBG_MISC, TEXT("FindLocalPath returned status=%08X"), status )); }
return( status ); }
//+----------------------------------------------------------------------------
//
// Function: GetDroids
//
// Synopsis: Get the current and birth domain-relative object IDs from
// a file. If rgoEnum is RGO_GET_OBJECT_ID, then an object ID
// will be generated if necessary. If RGO_READ_OBJECT_ID is
// specified, and the file doesn't already have an object ID,
// then STATUS_OBJECT_NAME_NOT_FOUND will be returned.
//
// Inputs: [tszFile] (in)
// The file who's object ID is to be retrieved.
// [pdroidCurrent] (out)
// The file's CDomainRelativeObjId.
// [pdroidBirth] (out)
// The file's birth CDomainRelativeObjId
// [rgoEnum] (in)
// RGO_READ_OBJECTID => Read the object IDs, return
// STATUS_OBJECT_NAME_NOT_FOUND if none exist.
// RGO_GET_OBJECTID => Get the object IDs, generating
// and setting if necessary.
//
// Returns: NTSTATUS, outputs zero on error
//
// Exceptions: None
//
//+----------------------------------------------------------------------------
NTSTATUS GetDroids( HANDLE hFile, CDomainRelativeObjId *pdroidCurrent, CDomainRelativeObjId *pdroidBirth, RGO_ENUM rgoEnum ) { NTSTATUS status = STATUS_SUCCESS; // Filled by NtQueryInformationFile
FILE_OBJECTID_BUFFER fobOID; IO_STATUS_BLOCK Iosb;
CDomainRelativeObjId droidCurrent; CDomainRelativeObjId droidBirth;
pdroidCurrent->Init(); pdroidBirth->Init();
// -----------------
// Get the Object ID
// -----------------
// Use the file handle to get the file's Object ID
memset( &fobOID, 0, sizeof(fobOID) );
status = NtFsControlFile( hFile, NULL, NULL, NULL, &Iosb, RGO_READ_OBJECTID == rgoEnum ? FSCTL_GET_OBJECT_ID : FSCTL_CREATE_OR_GET_OBJECT_ID, NULL, 0, &fobOID, // Out buffer
sizeof(fobOID) ); // Out buffer size
if( !NT_SUCCESS(status) ) goto Exit;
// ---------------
// Load the Droids
// ---------------
droidBirth.InitFromFOB( fobOID ); droidBirth.GetVolumeId().Normalize();
status = droidCurrent.InitFromFile( hFile, fobOID ); if( !NT_SUCCESS(status) ) goto Exit;
*pdroidCurrent = droidCurrent; *pdroidBirth = droidBirth;
// ----
// Exit
// ----
Exit:
if( !NT_SUCCESS(status) && STATUS_OBJECT_NAME_NOT_FOUND != status // Ignore non-link source
&& STATUS_INVALID_DEVICE_REQUEST != status // Ignore e.g. FAT
&& STATUS_VOLUME_NOT_UPGRADED != status // Ignore NTFS4
) { TrkLog(( TRKDBG_ERROR, TEXT("GetDroids returned ntstatus=%08X"), status )); }
return( status ); }
// Following is a wrapper that takes a filename, opens the file,
// and then calls the above GetDroids with the file handle.
NTSTATUS GetDroids( const TCHAR *ptszFile, CDomainRelativeObjId *pdroidCurrent, CDomainRelativeObjId *pdroidBirth, RGO_ENUM rgoEnum ) { HANDLE hFile = NULL; NTSTATUS status = STATUS_SUCCESS;
__try { status = TrkCreateFile( ptszFile, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_NO_RECALL | FILE_SYNCHRONOUS_IO_NONALERT, NULL, &hFile ); if( !NT_SUCCESS(status )) { hFile = NULL; goto Exit; }
status = GetDroids( hFile, pdroidCurrent, pdroidBirth, rgoEnum ); } __finally { if( NULL != hFile ) NtClose( hFile ); }
Exit:
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: SetObjId
//
// Synopsis: Sets an Object ID (GUID) on a file.
//
// Inputs: [ptszFile] (in)
// The file to be indexed.
// [objid] (in)
// The ID to put on the file.
// [droidBirth] (in)
// The BirthId to put on the file.
//
// Returns: NTSTATUS
//
// Exceptions: None
//
//+----------------------------------------------------------------------------
NTSTATUS SetObjId( const HANDLE hFile, CObjId objid, const CDomainRelativeObjId &droidBirth ) {
// --------------
// Initialization
// --------------
NTSTATUS status = STATUS_SUCCESS;
FILE_OBJECTID_BUFFER fobOID; IO_STATUS_BLOCK IoStatus;
// Initialize the request buffer
memset( &fobOID, 0, sizeof(fobOID) );
droidBirth.SerializeRaw( fobOID.ExtendedInfo ); objid.SerializeRaw( fobOID.ObjectId );
// Send the FSCTL
status = NtFsControlFile( hFile, NULL, NULL, NULL, &IoStatus, FSCTL_SET_OBJECT_ID, &fobOID, sizeof(fobOID), NULL, // Out buffer
0); // Out buffer size
if( !NT_SUCCESS(status) ) goto Exit;
// ----
// Exit
// ----
Exit:
if( !NT_SUCCESS(status) ) { TrkLog(( TRKDBG_ERROR, TEXT("SetObjId returned ntstatus=%08X"), status )); }
return( status ); }
NTSTATUS SetObjId( const TCHAR *ptszFile, CObjId objid, const CDomainRelativeObjId &droidBirth ) { NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL;
__try { // Setting the object ID requires restore privelege, but no
// file access.
EnableRestorePrivilege();
status = TrkCreateFile( ptszFile, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_NO_RECALL | FILE_SYNCHRONOUS_IO_NONALERT, NULL, &hFile ); if( !NT_SUCCESS(status )) { hFile = NULL; goto Exit; }
status = SetObjId( hFile, objid, droidBirth ); } __finally { if( NULL != hFile ) NtClose( hFile ); }
Exit:
return( status ); }
//+----------------------------------------------------------------------------
//
// Function: MakeObjIdReborn
//
// Synopsis: Resets the birth ID on a file to it's current location.
//
// Inputs: [hFile]
// The handle of the file to delete the object id of.
//
// Returns: NTSTATUS
//
// Exceptions: None
//
//+----------------------------------------------------------------------------
NTSTATUS MakeObjIdReborn(const TCHAR *ptszVolumeDeviceName, const CObjId &objid) {
// --------------
// Initialization
// --------------
NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL; IO_STATUS_BLOCK IoStatus;
// Open the file
EnableRestorePrivilege();
status = OpenFileById(ptszVolumeDeviceName, objid, SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_FOR_BACKUP_INTENT, // for FSCTL_DELETE_OBJECT_ID
&hFile); if( !NT_SUCCESS(status) ) { hFile = NULL; TrkLog(( (STATUS_SHARING_VIOLATION == status || STATUS_ACCESS_DENIED == status) ? TRKDBG_MISC : TRKDBG_ERROR, TEXT("Couldn't make born again objid (%s) on %s, failed open (%08x)"), (const TCHAR*)CDebugString(objid), ptszVolumeDeviceName, status )); goto Exit; }
// Clear the file's birth ID
status = MakeObjIdReborn( hFile ); if( !NT_SUCCESS(status) && STATUS_OBJECT_NAME_NOT_FOUND != status ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't make born again Object ID (%s) on %s:, failed delete (%08x)"), (const TCHAR*)CDebugString(objid), ptszVolumeDeviceName, status )); goto Exit; }
// ----
// Exit
// ----
Exit:
if( NULL != hFile ) NtClose( hFile );
#if DBG
if( !NT_SUCCESS(status) && STATUS_SHARING_VIOLATION != status && STATUS_ACCESS_DENIED != status ) { TCHAR tszPath[ MAX_PATH + 1 ]; NTSTATUS statusDebug;
hFile = NULL; statusDebug = OpenFileById( ptszVolumeDeviceName, objid, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_ATTRIBUTE_NORMAL, &hFile ); if( !NT_SUCCESS(statusDebug) ) hFile = NULL;
if( NT_SUCCESS(statusDebug) ) statusDebug = QueryVolRelativePath( hFile, tszPath ); else TrkLog(( TRKDBG_ERROR, TEXT("Couldn't OpenFileById (%08x)"), statusDebug ));
if( NT_SUCCESS(statusDebug) ) TrkLog(( TRKDBG_ERROR, TEXT("Failed to make born again objid on %s:%s"), ptszVolumeDeviceName, tszPath )); else TrkLog(( TRKDBG_ERROR, TEXT("Couldn't QueryVolRelativePath (%08x)"), statusDebug ));
if( NULL != hFile ) NtClose( hFile );
} #endif
return( status ); }
NTSTATUS MakeObjIdReborn(HANDLE hFile ) { NTSTATUS status = STATUS_SUCCESS;
status = SetBirthId( hFile, CDomainRelativeObjId( )); if( !NT_SUCCESS(status) ) goto Exit;
Exit:
return( status );
}
//+----------------------------------------------------------------------------
//
// SetBirthId
//
// The the birth ID on a file. The object ID isn't altered.
//
//+----------------------------------------------------------------------------
NTSTATUS SetBirthId( HANDLE hFile, const CDomainRelativeObjId &droidBirth ) {
// --------------
// Initialization
// --------------
NTSTATUS status = STATUS_SUCCESS; BOOL fOpen = FALSE; CObjId objidNull;
FILE_OBJECTID_BUFFER fobOID; UNICODE_STRING uPath; IO_STATUS_BLOCK IoStatus;
// Initialize the request buffer
memset( &fobOID, 0, sizeof(fobOID) );
droidBirth.SerializeRaw( fobOID.ExtendedInfo ); objidNull.SerializeRaw( fobOID.ObjectId );
// Send the FSCTL
status = NtFsControlFile( hFile, NULL, NULL, NULL, &IoStatus, FSCTL_SET_OBJECT_ID_EXTENDED, &fobOID.ExtendedInfo, sizeof(fobOID.ExtendedInfo), NULL, // Out buffer
0); // Out buffer size
if( !NT_SUCCESS(status) ) goto Exit;
// ----
// Exit
// ----
Exit:
if( !NT_SUCCESS(status) ) { TrkLog(( TRKDBG_ERROR, TEXT("SetBirthId returned ntstatus=%08X"), status )); }
return( status ); }
// Set the birth ID given a path, rather than a handle.
NTSTATUS SetBirthId( const TCHAR *ptszFile, const CDomainRelativeObjId &droidBirth ) { NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL;
__try { EnableRestorePrivilege();
status = TrkCreateFile( ptszFile, SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_NO_RECALL | FILE_SYNCHRONOUS_IO_NONALERT, NULL, &hFile ); if( !NT_SUCCESS(status )) { hFile = NULL; goto Exit; }
status = SetBirthId( hFile, droidBirth ); } __finally { if( NULL != hFile ) NtClose( hFile );
}
Exit:
return( status ); }
//+----------------------------------------------------------------------------
//
// Function: GetBirthId
//
// Synopsis: Get the birth ID from a given file.
//
// Parameters: [hFile] (in)
// The file to query.
// [pdroidBirth] (out)
// The file's birth ID.
//
// Returns: [NTSTATUS]
//
//+----------------------------------------------------------------------------
NTSTATUS GetBirthId( IN HANDLE hFile, OUT CDomainRelativeObjId *pdroidBirth ) { NTSTATUS status = STATUS_SUCCESS; FILE_OBJECTID_BUFFER fobOID; IO_STATUS_BLOCK IoStatus;
TrkAssert( NULL != pdroidBirth ); TrkAssert( INVALID_HANDLE_VALUE != hFile && NULL != hFile );
status = NtFsControlFile( hFile, NULL, NULL, NULL, &IoStatus, FSCTL_GET_OBJECT_ID, NULL, 0, &fobOID, sizeof(fobOID));
if( !NT_SUCCESS(status) ) goto Exit;
// Load the droid from the objid buffer.
pdroidBirth->InitFromFOB( fobOID );
// Clear the bit xvol-move bit, which is not considered part of the ID.
pdroidBirth->GetVolumeId().Normalize();
Exit:
return( status ); }
|