windows-nt/Source/XPSP1/NT/com/svcdlls/trksvcs/common/fileops.cxx
2020-09-26 16:20:57 +08:00

1757 lines
49 KiB
C++

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