898 lines
30 KiB
C++
898 lines
30 KiB
C++
/*-----------------------------------------------------------------------------
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
PARTICULAR PURPOSE.
|
|
|
|
Copyright (C) 1999 - 2001 Microsoft Corporation. All rights reserved.
|
|
|
|
DLEDIT -- Drive Letter Assignment Editor
|
|
|
|
This program demonstrates how to add or remove persistent drive letter
|
|
assignments in Windows 2000, Windows XP, and Windows Server 2003. These
|
|
drive letter assignments persist through machine reboots.
|
|
|
|
Platforms:
|
|
This program requires Windows 2000 or later.
|
|
|
|
Command-line syntax:
|
|
DLEDIT <drive letter> <NT device name> -- Adds persistent drive letter
|
|
DLEDIT -t <drive letter> <NT device name> -- Adds temporary drive letter
|
|
DLEDIT -r <drive letter> -- Removes a drive letter
|
|
DLEDIT <drive letter> -- Shows drive letter mapping
|
|
DLEDIT -a -- Shows all drive letter mappings
|
|
|
|
Command-line examples:
|
|
|
|
Say that E: refers to CD-ROM drive, and you want to make F: point to that
|
|
CD-ROM drive instead. Use the following two commands:
|
|
|
|
DLEDIT -r E:\
|
|
DLEDIT F:\ \Device\CdRom0
|
|
|
|
To display what device a drive letter is mapped to, use the following
|
|
command:
|
|
|
|
DLEDIT f:
|
|
|
|
|
|
*******************************************************************************
|
|
WARNING: WARNING: WARNING: WARNING: WARNING: WARNING: WARNING: WARNING:
|
|
|
|
This program really will change drive letter assignments, and the changes
|
|
persist through reboots. Do not remove drive letters of your hard disks if
|
|
you don't have this program on a floppy disk or you might not be able to
|
|
access your hard disks again!
|
|
*******************************************************************************
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* reduce number of headers pulled in by windows.h to improve build time */
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define _WIN32_WINNT 0x0500
|
|
|
|
#include <windows.h>
|
|
#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
|
|
#include <winioctl.h>
|
|
#include <TCHAR.H>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
/*-----------------------------------------------------------------------------*/
|
|
#define GPT_BASIC_DATA_ATTRIBUTE_HIDDEN (0x4000000000000000) // from DDK's NTDDDISK.H
|
|
|
|
#define IOCTL_VOLUME_GET_GPT_ATTRIBUTES CTL_CODE(IOCTL_VOLUME_BASE, 14, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
|
|
typedef struct _VOLUME_GET_GPT_ATTRIBUTES_INFORMATION {
|
|
ULONGLONG GptAttributes;
|
|
} VOLUME_GET_GPT_ATTRIBUTES_INFORMATION, *PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION;
|
|
|
|
/*-----------------------------------------------------------------------------*/
|
|
|
|
|
|
#define HIDDEN_PARTITION_FLAG 0x10
|
|
|
|
|
|
/* Private error codes. Follows guidelines in winerror.h */
|
|
const DWORD PRIV_ERROR_DRIVE_LETTER_IN_USE = 0xE0000001;
|
|
//const DWORD PRIV_ERROR_DRIVE_LETTER_NOT_USED = 0xE0000002;
|
|
const DWORD PRIV_ERROR_PARTITION_HIDDEN = 0x60000001;
|
|
const DWORD PRIV_ERROR_PARTITION_NOT_RECOGNIZED = 0x60000002;
|
|
//const DWORD PRIV_ERROR_NOT_PARTITIONABLE_DEVICE = 0x60000003;
|
|
|
|
|
|
|
|
BOOL WINAPI AssignPersistentDriveLetter (LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName);
|
|
BOOL WINAPI RemovePersistentDriveLetter (LPCTSTR pszDriveLetter);
|
|
BOOL WINAPI AssignTemporaryDriveLetter (LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName);
|
|
BOOL WINAPI RemoveTemporaryDriveLetter (LPCTSTR pszDriveLetter);
|
|
|
|
BOOL WINAPI IsPartitionHidden (BYTE partitionType, ULONGLONG partitionAttribs);
|
|
BOOL WINAPI IsDriveLetter (LPCTSTR pszDriveLetter);
|
|
BOOL WINAPI IsWindowsXP_orLater (void);
|
|
|
|
|
|
static void PrintHelp (LPCTSTR pszAppName);
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
main( IN argc, IN argv )
|
|
|
|
Parameters
|
|
argc
|
|
Count of the command-line arguments
|
|
argv
|
|
Array of pointers to the individual command-line arguments
|
|
|
|
This function is the main program. It parses the command-line arguments and
|
|
performs the work of either removing a drive letter or adding a new one.
|
|
-----------------------------------------------------------------------------*/
|
|
extern "C"
|
|
void _tmain (int argc, TCHAR **argv)
|
|
{
|
|
TCHAR * pszDriveLetter,
|
|
* pszNTDevice,
|
|
* pszOptions;
|
|
|
|
/*
|
|
Command-line parsing.
|
|
1) Validate arguments
|
|
2) Determine what user wants to do
|
|
*/
|
|
if (3 == argc && !lstrcmpi (argv[1], _T("-r")) && IsDriveLetter (argv[2]))
|
|
{
|
|
/*
|
|
User wants to remove the drive letter. Command line should be:
|
|
dledit -r <drive letter>
|
|
*/
|
|
pszOptions = argv[1];
|
|
pszDriveLetter = argv[2];
|
|
pszNTDevice = NULL;
|
|
|
|
if (!RemovePersistentDriveLetter (pszDriveLetter))
|
|
{
|
|
if (!RemoveTemporaryDriveLetter (pszDriveLetter))
|
|
{
|
|
switch (GetLastError())
|
|
{
|
|
case ERROR_FILE_NOT_FOUND:
|
|
_tprintf(_T("%s is not in use\n"), pszDriveLetter);
|
|
break;
|
|
|
|
default:
|
|
_tprintf(_T("error %lu: couldn't remove %s\n"),
|
|
GetLastError(), pszDriveLetter);
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* End if remove drive letter mapping */
|
|
|
|
else if (3 == argc && IsDriveLetter (argv[1]))
|
|
{
|
|
/*
|
|
User wants to add a persistent drive letter. Command line should be:
|
|
dledit <drive letter> <NT device name>
|
|
*/
|
|
pszOptions = NULL;
|
|
pszDriveLetter = argv[1];
|
|
pszNTDevice = argv[2];
|
|
|
|
/*
|
|
Try a persistent drive letter; if partition is hidden, the persistent
|
|
mapping will fail--try a temporary mapping and tell user about it.
|
|
|
|
Note: Hidden partitions can be assigned temporary drive letters.
|
|
These are nothing more than symbolic links created with
|
|
DefineDosDevice. Temporary drive letters will be removed when the
|
|
system is rebooted.
|
|
*/
|
|
if (!AssignPersistentDriveLetter (pszDriveLetter, pszNTDevice))
|
|
{
|
|
switch (GetLastError())
|
|
{
|
|
case PRIV_ERROR_PARTITION_HIDDEN:
|
|
if (AssignTemporaryDriveLetter (pszDriveLetter, pszNTDevice))
|
|
_tprintf(_T("%s is hidden; mapped %s temporarily\n"),
|
|
pszNTDevice, pszDriveLetter);
|
|
else
|
|
_tprintf(_T("%s is hidden; couldn't map %s to it\n"),
|
|
pszNTDevice, pszDriveLetter);
|
|
break;
|
|
|
|
case PRIV_ERROR_DRIVE_LETTER_IN_USE:
|
|
_tprintf(_T("%s is in use, can't map it to %s\n"),
|
|
pszDriveLetter, pszNTDevice);
|
|
break;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
_tprintf(_T("%s doesn't exist or can't be opened\n"),
|
|
pszNTDevice);
|
|
break;
|
|
|
|
case ERROR_INVALID_PARAMETER:
|
|
_tprintf(_T("%s already has a drive letter; can't map %s to it\n"),
|
|
pszNTDevice, pszDriveLetter);
|
|
break;
|
|
|
|
default:
|
|
_tprintf(_T("error %lu: couldn't map %s to %s\n"),
|
|
GetLastError(), pszDriveLetter, pszNTDevice);
|
|
}
|
|
}
|
|
} /* End if add persistent drive letter mapping */
|
|
|
|
else if (4 == argc && !lstrcmpi (argv[1], _T("-t")) && IsDriveLetter (argv[2]))
|
|
{
|
|
/*
|
|
User wants to add a temporary drive letter. Command line should be:
|
|
dledit -t <drive letter> <NT device name>
|
|
*/
|
|
pszOptions = argv[1];
|
|
pszDriveLetter = argv[2];
|
|
pszNTDevice = argv[3];
|
|
|
|
if (!AssignTemporaryDriveLetter (pszDriveLetter, pszNTDevice))
|
|
{
|
|
switch (GetLastError())
|
|
{
|
|
case ERROR_FILE_NOT_FOUND:
|
|
_tprintf(_T("%s doesn't exist or can't be opened\n"),
|
|
pszNTDevice);
|
|
break;
|
|
|
|
case PRIV_ERROR_DRIVE_LETTER_IN_USE:
|
|
_tprintf(_T("%s is in use, can't map it to %s\n"),
|
|
pszDriveLetter, pszNTDevice);
|
|
break;
|
|
|
|
default:
|
|
_tprintf(_T("error %lu: couldn't map %s to %s\n"),
|
|
GetLastError(), pszDriveLetter, pszNTDevice);
|
|
}
|
|
}
|
|
} /* End if add temporary drive letter mapping */
|
|
|
|
else if (2 == argc && IsDriveLetter (argv[1]))
|
|
{
|
|
/*
|
|
User wants to show what device is connected to the drive letter.
|
|
Command line should be:
|
|
dledit <drive letter>
|
|
*/
|
|
pszOptions = NULL;
|
|
pszDriveLetter = argv[1];
|
|
pszNTDevice = NULL;
|
|
|
|
TCHAR szNtDeviceName[MAX_PATH];
|
|
TCHAR szDriveLetter[3];
|
|
|
|
/*
|
|
Command-line argument for the drive letter could be in one of two
|
|
formats: C:\ or C:. We normalize this to C: for QueryDosDevice.
|
|
*/
|
|
szDriveLetter[0] = pszDriveLetter[0];
|
|
szDriveLetter[1] = _T(':');
|
|
szDriveLetter[2] = _T('\0');
|
|
|
|
if (QueryDosDevice (szDriveLetter, szNtDeviceName, MAX_PATH))
|
|
{
|
|
_tprintf(_T("%s is mapped to %s\n"), szDriveLetter, szNtDeviceName);
|
|
}
|
|
else
|
|
{
|
|
switch (GetLastError())
|
|
{
|
|
case ERROR_FILE_NOT_FOUND:
|
|
_tprintf(_T("%s is not in use\n"), pszDriveLetter);
|
|
break;
|
|
|
|
default:
|
|
_tprintf(_T("error %lu: couldn't get mapping for %s\n"),
|
|
GetLastError(), pszDriveLetter);
|
|
}
|
|
}
|
|
|
|
} /* End if show drive letter mapping */
|
|
|
|
else if (2 == argc && !lstrcmpi (argv[1], _T("-a")))
|
|
{
|
|
/*
|
|
User wants to show all mappings of drive letters to their respective
|
|
devices. Command line should be:
|
|
dledit -a
|
|
*/
|
|
pszOptions = argv[1];
|
|
pszDriveLetter = NULL;
|
|
pszNTDevice = NULL;
|
|
|
|
/*
|
|
Largest possible array of drive strings is 26 drive letters
|
|
times 4 chars per drive letter plus 1 for the terminating NULL
|
|
*/
|
|
const DWORD MAX_DRIVE_STRING_LENGTH = 26 * 4 + 1;
|
|
|
|
TCHAR szDriveStrings[MAX_DRIVE_STRING_LENGTH];
|
|
DWORD cchDriveStrings;
|
|
|
|
|
|
/*
|
|
Get a list of all current drive letters, then for each, print the
|
|
mapping. Drive letters are returned in the form
|
|
A:\<NULL>B:\<NULL>C:\<NULL>...<NULL>.
|
|
*/
|
|
cchDriveStrings = GetLogicalDriveStrings (MAX_DRIVE_STRING_LENGTH, szDriveStrings);
|
|
if ( (0 != cchDriveStrings) && (cchDriveStrings <= MAX_DRIVE_STRING_LENGTH) )
|
|
{
|
|
TCHAR * pszCurrentDriveLetter;
|
|
TCHAR szNtDeviceName[MAX_PATH];
|
|
TCHAR szDriveLetter[3];
|
|
|
|
|
|
_tprintf(_T("Drive Device\n")
|
|
_T("----- ------\n"));
|
|
|
|
pszCurrentDriveLetter = szDriveStrings;
|
|
while (_T('\0') != *pszCurrentDriveLetter)
|
|
{
|
|
/* QueryDosDevice requires drive letters in the format X: */
|
|
szDriveLetter[0] = pszCurrentDriveLetter[0];
|
|
szDriveLetter[1] = _T(':');
|
|
szDriveLetter[2] = _T('\0');
|
|
|
|
if (QueryDosDevice (szDriveLetter, szNtDeviceName, MAX_PATH))
|
|
_tprintf(_T("%-10s%s\n"), szDriveLetter, szNtDeviceName);
|
|
|
|
pszCurrentDriveLetter += 4; /* size of X:\<NULL> */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_tprintf(_T("couldn't list drive letters and their devices\n"));
|
|
}
|
|
} /* End if show all drive letter mappings */
|
|
|
|
else
|
|
{
|
|
/* User has selected an invalid operation--display help. */
|
|
PrintHelp (argv[0]);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
PrintHelp( IN pszAppName )
|
|
|
|
Parameters
|
|
pszAppName
|
|
The name of the executable. Used in displaying the help for this app.
|
|
|
|
Prints the command-line usage help.
|
|
-----------------------------------------------------------------------------*/
|
|
static void PrintHelp (LPCTSTR pszAppName)
|
|
{
|
|
_tprintf(_T("Adds, removes, queries drive letter assignments\n\n"));
|
|
_tprintf(_T("usage: %s <Drive Letter> <NT device name> add a drive letter\n"),
|
|
pszAppName);
|
|
_tprintf(_T(" %s -t <Drive Letter> <NT device name> add a temporary drive letter\n"),
|
|
pszAppName);
|
|
|
|
_tprintf(_T(" %s -r <Drive Letter> remove a drive letter\n"),
|
|
pszAppName);
|
|
_tprintf(_T(" %s <Drive Letter> show mapping for drive letter\n"),
|
|
pszAppName);
|
|
_tprintf(_T(" %s -a show all mappings\n\n"),
|
|
pszAppName);
|
|
|
|
|
|
_tprintf(_T("example: %s e:\\ \\Device\\CdRom0\n"), pszAppName);
|
|
_tprintf(_T(" %s -r e:\\\n"), pszAppName);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
AssignPersistentDriveLetter (IN pszDriveLetter, IN pszDeviceName)
|
|
|
|
Description:
|
|
Creates a persistent drive letter that refers to a specified device. This
|
|
drive letter will remain even when the system is restarted.
|
|
|
|
Parameters:
|
|
pszDriveLetter
|
|
The new drive letter to create. Must be in the form X: or X:\
|
|
|
|
pszDeviceName
|
|
The NT device name to which the drive letter will be assigned.
|
|
|
|
Return Value:
|
|
Returns TRUE if the drive letter was added, or FALSE if it wasn't.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI AssignPersistentDriveLetter (
|
|
LPCTSTR pszDriveLetter,
|
|
LPCTSTR pszDeviceName)
|
|
{
|
|
BOOL fResult;
|
|
TCHAR szUniqueVolumeName[MAX_PATH];
|
|
TCHAR szDriveLetterAndSlash[4];
|
|
TCHAR szDriveLetter[3];
|
|
|
|
/*
|
|
Make sure we are passed a drive letter and a device name. lstrlen
|
|
is useful because it will return zero if the pointer points to memory
|
|
that can't be read or a string that causes an invalid page fault before
|
|
the terminating NULL.
|
|
*/
|
|
if (0 == lstrlen(pszDriveLetter) ||
|
|
0 == lstrlen(pszDeviceName) ||
|
|
!IsDriveLetter (pszDriveLetter))
|
|
{
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
GetVolumeNameForVolumeMountPoint, SetVolumeMountPoint, and
|
|
DeleteVolumeMountPoint require drive letters to have a trailing backslash.
|
|
However, DefineDosDevice and QueryDosDevice require that the trailing
|
|
backslash be absent. So, we'll set up the following variables:
|
|
|
|
szDriveLetterAndSlash for the mount point APIs
|
|
szDriveLetter for DefineDosDevice
|
|
*/
|
|
szDriveLetterAndSlash[0] = pszDriveLetter[0];
|
|
szDriveLetterAndSlash[1] = _T(':');
|
|
szDriveLetterAndSlash[2] = _T('\\');
|
|
szDriveLetterAndSlash[3] = _T('\0');
|
|
|
|
szDriveLetter[0] = szDriveLetterAndSlash[0];
|
|
szDriveLetter[1] = _T(':');
|
|
szDriveLetter[2] = _T('\0');
|
|
|
|
/*
|
|
Determine if the drive letter is currently in use. If so, return the
|
|
error to the caller. NOTE: we temporarily reuse szUniqueVolumeName
|
|
instead of allocating a large array for this one call.
|
|
*/
|
|
fResult = QueryDosDevice (szDriveLetter, szUniqueVolumeName, MAX_PATH);
|
|
if (fResult)
|
|
{
|
|
SetLastError (PRIV_ERROR_DRIVE_LETTER_IN_USE);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
To map a persistent drive letter, we must make sure that the target
|
|
device is one of the following:
|
|
|
|
A recognized partition and is not hidden
|
|
A dynamic volume
|
|
A non-partitionable device such as CD-ROM
|
|
|
|
Start by using the drive letter as a symbolic link to the device. Then,
|
|
open the device to gather the information necessary to determine if the
|
|
drive can have a persistent drive letter.
|
|
*/
|
|
fResult = DefineDosDevice (DDD_RAW_TARGET_PATH, szDriveLetter, pszDeviceName);
|
|
if (fResult)
|
|
{
|
|
HANDLE hDevice;
|
|
DWORD dwBytesReturned;
|
|
BOOL fResult;
|
|
TCHAR szDriveName[7]; // holds \\.\X: plus NULL.
|
|
|
|
VOLUME_GET_GPT_ATTRIBUTES_INFORMATION volinfo;
|
|
PARTITION_INFORMATION partinfo;
|
|
|
|
|
|
_tcsncpy_s (szDriveName, _countof(szDriveName), _T("\\\\.\\"), _TRUNCATE);
|
|
_tcsncat_s (szDriveName, _countof(szDriveName), szDriveLetter, _TRUNCATE);
|
|
|
|
hDevice = CreateFile (szDriveName, GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE != hDevice)
|
|
{
|
|
/*
|
|
See if drive is partitionable and retrieve the partition type.
|
|
If the device doesn't have a partition, note it by setting the
|
|
partition type to unused.
|
|
*/
|
|
fResult = DeviceIoControl (hDevice, IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL, 0,
|
|
&partinfo, sizeof(partinfo),
|
|
&dwBytesReturned, NULL);
|
|
if (!fResult)
|
|
{
|
|
partinfo.PartitionType = PARTITION_ENTRY_UNUSED;
|
|
}
|
|
|
|
/*
|
|
On Windows XP, partition entries on GUID Partition Table drives
|
|
have an attribute that determines whether partitions are hidden.
|
|
Therefore, we must check this bit on the target partition.
|
|
|
|
If we're running on Windows 2000, there are no GPT drives, so
|
|
set flags to none. This is important for the check for hidden
|
|
partitions later.
|
|
*/
|
|
if (IsWindowsXP_orLater())
|
|
{
|
|
fResult = DeviceIoControl (hDevice, IOCTL_VOLUME_GET_GPT_ATTRIBUTES,
|
|
NULL, 0,
|
|
&volinfo, sizeof(volinfo),
|
|
&dwBytesReturned, NULL);
|
|
if (!fResult)
|
|
volinfo.GptAttributes = 0;
|
|
}
|
|
else
|
|
{
|
|
volinfo.GptAttributes = 0;
|
|
}
|
|
CloseHandle (hDevice);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Remove the drive letter symbolic link we created, let caller know
|
|
we couldn't open the drive.
|
|
*/
|
|
DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|
|
|
DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
|
|
pszDeviceName);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
Now, make sure drive meets requirements for receiving a persistent
|
|
drive letter.
|
|
|
|
Note: on Windows XP, partitions that were hidden when the system
|
|
booted are not assigned a unique volume name. They will not be given
|
|
one until they are marked as not hidden and the system rebooted.
|
|
Therefore, we cannot create a mount point hidden partitions.
|
|
|
|
On Windows 2000, hidden partitions are assigned unique volume names
|
|
and so can have persistent drive letters. However, we will not allow
|
|
that behavior in this tool as hidden partitions should not be assigned
|
|
drive letters.
|
|
*/
|
|
if (IsPartitionHidden (partinfo.PartitionType, volinfo.GptAttributes))
|
|
{
|
|
// remove the drive letter we created, let caller know what happened.
|
|
DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|
|
|
DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
|
|
pszDeviceName);
|
|
|
|
SetLastError (PRIV_ERROR_PARTITION_HIDDEN);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
Verify that the drive letter must refer to a recognized partition,
|
|
a dynamic volume, or a non-partitionable device such as CD-ROM.
|
|
*/
|
|
if (IsRecognizedPartition(partinfo.PartitionType) ||
|
|
PARTITION_LDM == partinfo.PartitionType ||
|
|
PARTITION_ENTRY_UNUSED == partinfo.PartitionType)
|
|
{
|
|
/*
|
|
Now add the drive letter by calling on the volume mount manager.
|
|
Once we have the unique volume name that the new drive letter
|
|
will point to, delete the symbolic link because the Mount Manager
|
|
allows only one reference to a device at a time (the new one to
|
|
be added).
|
|
*/
|
|
fResult = GetVolumeNameForVolumeMountPoint (szDriveLetterAndSlash,
|
|
szUniqueVolumeName,
|
|
MAX_PATH);
|
|
|
|
DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|
|
|
DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
|
|
pszDeviceName);
|
|
|
|
if (fResult)
|
|
fResult = SetVolumeMountPoint (szDriveLetterAndSlash,
|
|
szUniqueVolumeName);
|
|
|
|
return (fResult);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Device doesn't meet the criteria for persistent drive letter.
|
|
Remove the drive letter symbolic link we created.
|
|
*/
|
|
DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|
|
|
DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
|
|
pszDeviceName);
|
|
|
|
SetLastError (PRIV_ERROR_PARTITION_NOT_RECOGNIZED);
|
|
return (FALSE);
|
|
}
|
|
|
|
} /* end if DeviceIoControl to map symbolic link*/
|
|
else
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
RemovePersistentDriveLetter( IN pszDriveLetter)
|
|
|
|
Description:
|
|
Removes a drive letter that was created by AssignPersistentDriveLetter().
|
|
|
|
Parameters:
|
|
pszDriveLetter
|
|
The drive letter to remove. Must be in the format X: or X:\
|
|
|
|
Return Value:
|
|
Returns TRUE if the drive letter was removed, or FALSE if it wasn't.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI RemovePersistentDriveLetter (LPCTSTR pszDriveLetter)
|
|
{
|
|
BOOL fResult;
|
|
TCHAR szDriveLetterAndSlash[4];
|
|
|
|
/* Make sure we have a drive letter. */
|
|
if (0 == lstrlen(pszDriveLetter) || !IsDriveLetter (pszDriveLetter))
|
|
{
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
fResult = FALSE;
|
|
}
|
|
|
|
/*
|
|
pszDriveLetter could be in the format X: or X:\. DeleteVolumeMountPoint
|
|
requires X:\, so add a trailing backslash.
|
|
*/
|
|
szDriveLetterAndSlash[0] = pszDriveLetter[0];
|
|
szDriveLetterAndSlash[1] = _T(':');
|
|
szDriveLetterAndSlash[2] = _T('\\');
|
|
szDriveLetterAndSlash[3] = _T('\0');
|
|
|
|
fResult = DeleteVolumeMountPoint (szDriveLetterAndSlash);
|
|
return (fResult);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
AssignTemporaryDriveLetter( IN pszDriveLetter, IN pszDeviceName)
|
|
|
|
Description:
|
|
Creates a temporary drive letter that refers to a specified device. This
|
|
drive letter will exist only until the system is shut down or restarted.
|
|
|
|
Parameters:
|
|
pszDriveLetter
|
|
The new drive letter to create. Must be in the form X: or X:\
|
|
|
|
pszDeviceName
|
|
The NT device name to which the drive letter will be assigned.
|
|
|
|
Return Value:
|
|
Returns TRUE if the temporary drive letter was assigned, or FALSE if it
|
|
could not be.
|
|
|
|
Notes:
|
|
A temporary drive letter is just a symbolic link. It can be removed at any
|
|
time by deleting the symbolic link. If it exists when the system is shut
|
|
down or restarted, it will be removed automatically.
|
|
|
|
AssignTemporaryDriveLetter requires device to be present.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI AssignTemporaryDriveLetter (LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName)
|
|
{
|
|
TCHAR szDevice[MAX_PATH];
|
|
TCHAR szDriveLetter[3];
|
|
BOOL fResult;
|
|
|
|
/* Verify that caller passed a drive letter and device name. */
|
|
if (0 == lstrlen(pszDriveLetter) ||
|
|
0 == lstrlen(pszDeviceName) ||
|
|
!IsDriveLetter (pszDriveLetter))
|
|
{
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
Make sure the drive letter isn't already in use. If not in use,
|
|
create the symbolic link to establish the temporary drive letter.
|
|
|
|
pszDriveLetter could be in the format X: or X:\; QueryDosDevice and
|
|
DefineDosDevice need X:
|
|
*/
|
|
szDriveLetter[0] = pszDriveLetter[0];
|
|
szDriveLetter[1] = _T(':');
|
|
szDriveLetter[2] = _T('\0');
|
|
|
|
if (!QueryDosDevice (szDriveLetter, szDevice, MAX_PATH))
|
|
{
|
|
/*
|
|
If we can create the symbolic link, verify that it points to a real
|
|
device. If not, remove the link and return an error. CreateFile sets
|
|
the last error code to ERROR_FILE_NOT_FOUND.
|
|
*/
|
|
fResult = DefineDosDevice (DDD_RAW_TARGET_PATH, szDriveLetter, pszDeviceName);
|
|
if (fResult)
|
|
{
|
|
HANDLE hDevice;
|
|
TCHAR szDriveName[7]; // holds \\.\X: plus NULL.
|
|
|
|
_tcsncpy_s (szDriveName, _countof(szDriveName), _T("\\\\.\\"), _TRUNCATE);
|
|
_tcsncat_s (szDriveName, _countof(szDriveName), szDriveLetter, _TRUNCATE);
|
|
|
|
hDevice = CreateFile (szDriveName, GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE != hDevice)
|
|
{
|
|
fResult = TRUE;
|
|
CloseHandle (hDevice);
|
|
}
|
|
else
|
|
{
|
|
DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|
|
|
DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
|
|
pszDeviceName);
|
|
fResult = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLastError (PRIV_ERROR_DRIVE_LETTER_IN_USE);
|
|
fResult = FALSE;
|
|
}
|
|
|
|
return (fResult);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
RemoveTemporaryDriveLetter( IN pszDriveLetter)
|
|
|
|
Description:
|
|
Removes a drive letter that was created by AssignTemporaryDriveLetter().
|
|
|
|
Parameters:
|
|
pszDriveLetter
|
|
The drive letter to remove. Must be in the format X: or X:\
|
|
|
|
Return Value:
|
|
Returns TRUE if the drive letter was removed, or FALSE if it wasn't.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI RemoveTemporaryDriveLetter (LPCTSTR pszDriveLetter)
|
|
{
|
|
BOOL fResult;
|
|
TCHAR szDriveLetter[3];
|
|
TCHAR szDeviceName[MAX_PATH];
|
|
|
|
/* Verify that caller passed a drive letter and device name. */
|
|
|
|
if (0 == lstrlen(pszDriveLetter) || !IsDriveLetter (pszDriveLetter))
|
|
{
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
fResult = FALSE;
|
|
|
|
}
|
|
|
|
/*
|
|
pszDriveLetter could be in the format X: or X:\; DefineDosDevice
|
|
needs X:
|
|
*/
|
|
szDriveLetter[0] = pszDriveLetter[0];
|
|
szDriveLetter[1] = _T(':');
|
|
szDriveLetter[2] = _T('\0');
|
|
|
|
fResult = QueryDosDevice (szDriveLetter, szDeviceName, MAX_PATH);
|
|
if (fResult)
|
|
{
|
|
fResult = DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|
|
|
DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
|
|
szDeviceName);
|
|
}
|
|
|
|
return (fResult);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
IsPartitionHidden( IN partitionType, IN partitionAttribs)
|
|
|
|
Description:
|
|
Determines if the partition hosting a volume is hidden or not.
|
|
|
|
Parameters:
|
|
partitionType
|
|
Specifies the partitition type; this value is obtained from
|
|
IOCTL_DISK_GET_PARTITION_INFO or IOCTL_DISK_GET_DRIVE_GEOMETRY.
|
|
A partition type that has bit 0x10 set is hidden.
|
|
|
|
partitionAttribs
|
|
Specifies the attributes of a GUID Partition Type (GPT)-style partition
|
|
entry. This value is obtained from IOCTL_VOLUME_GET_GPT_ATTRIBUTES
|
|
or IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. A GPT partition that has
|
|
GPT_BASIC_DATA_ATTRIBUTE_HIDDEN set is hidden.
|
|
|
|
Return Value:
|
|
Returns TRUE if the partition is hidden or FALSE if it is not.
|
|
|
|
Notes:
|
|
On Windows XP and Windows Server 2003, partitions that were hidden when
|
|
the system booted are not assigned a unique volume name. They will not
|
|
be given one until they are marked as not hidden and the system rebooted.
|
|
Therefore, we cannot create a mount point hidden partitions.
|
|
|
|
On Windows 2000, hidden partitions are assigned unique volume names
|
|
and so can have persistent drive letters. However, we will not allow
|
|
that behavior in this tool as hidden partitions should not be assigned
|
|
drive letters.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI IsPartitionHidden (BYTE partitionType, ULONGLONG partitionAttribs)
|
|
{
|
|
if ((partitionAttribs & GPT_BASIC_DATA_ATTRIBUTE_HIDDEN) ||
|
|
((partitionType & HIDDEN_PARTITION_FLAG) &&
|
|
IsRecognizedPartition (partitionType & ~HIDDEN_PARTITION_FLAG)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
IsDriveLetter( IN pszDriveLetter)
|
|
|
|
Description:
|
|
Verifies string passed in is of the form X: or X:\ where X is a letter.
|
|
|
|
Parameters:
|
|
pszDriveLetter
|
|
A null terminated string.
|
|
|
|
Return Value:
|
|
TRUE if the string is of the form X: or X:\ where X is a letter. X
|
|
may be upper-case or lower-case. If the string isn't of this from,
|
|
returns FALSE.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI IsDriveLetter (LPCTSTR pszDriveLetter)
|
|
{
|
|
/*
|
|
format must be: X:<null> or X:\<NULL> where X is a letter. lstrlen will
|
|
return 0 if inaccessible pointer is supplied, or if reading bytes of
|
|
string causes an access violation before reaching a terminating NULL.
|
|
*/
|
|
if ( (0 < lstrlen (pszDriveLetter)) &&
|
|
(IsCharAlpha (pszDriveLetter[0])) &&
|
|
(_T(':') == pszDriveLetter[1]) &&
|
|
((_T('\0') == pszDriveLetter[2]) ||
|
|
((_T('\\') == pszDriveLetter[2]) && (_T('\0') == pszDriveLetter[3])))
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
IsWindowsXP_orLater()
|
|
|
|
Description:
|
|
Determines if the currently-running version of Windows is Windows XP or
|
|
Windows Server 2003 or later.
|
|
|
|
Return Value:
|
|
Returns TRUE if the currently-running system is Windows XP or Windows 2003
|
|
Server or later systems. Returns FALSE otherwise.
|
|
-----------------------------------------------------------------------------*/
|
|
BOOL WINAPI IsWindowsXP_orLater (void)
|
|
{
|
|
OSVERSIONINFOEX osvi;
|
|
ULONGLONG comparisonMask;
|
|
|
|
ZeroMemory (&osvi, sizeof(osvi));
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
osvi.dwMajorVersion = 5;
|
|
osvi.dwMinorVersion = 1;
|
|
|
|
comparisonMask = 0;
|
|
comparisonMask = VerSetConditionMask (comparisonMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
|
comparisonMask = VerSetConditionMask (comparisonMask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
|
|
|
return (VerifyVersionInfo (&osvi, VER_MAJORVERSION|VER_MINORVERSION, comparisonMask));
|
|
}
|
|
|
|
|
|
|