670 lines
17 KiB
C++
670 lines
17 KiB
C++
/*++
|
|
|
|
Copyright (c) 1991-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
LABEL
|
|
|
|
Abstract:
|
|
|
|
Label is a DOS-5 compatible volume label changing utility
|
|
|
|
Author:
|
|
|
|
Norbert Kluster (norbertk) 18-Apr-1991
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define _NTAPI_ULIB_
|
|
|
|
#include "ulib.hxx"
|
|
#include "arg.hxx"
|
|
#include "array.hxx"
|
|
#include "smsg.hxx"
|
|
#include "rtmsg.h"
|
|
#include "wstring.hxx"
|
|
#include "path.hxx"
|
|
#include "substrng.hxx"
|
|
#include "system.hxx"
|
|
#include "ifssys.hxx"
|
|
#include "ulibcl.hxx"
|
|
|
|
extern "C" {
|
|
#include "ntioapi.h"
|
|
}
|
|
|
|
CONST MaxLabelLength = 1024;
|
|
|
|
VOID
|
|
DisplayLabelUsage(
|
|
IN OUT PMESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine displays the usage for the dos 5 label program.
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies an outlet for the messages.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
Message->Set(MSG_LBL_INFO);
|
|
Message->Display("");
|
|
Message->Set(MSG_LBL_USAGE);
|
|
Message->Display("");
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OpenDrive(
|
|
IN PCWSTRING Drive,
|
|
OUT PHANDLE Handle,
|
|
OUT PNTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens an NT handle to for the given dos drive name.
|
|
|
|
Arguments:
|
|
|
|
Drive - Supplies a dos drive name.
|
|
Handle - Returns an NT handle to the drive.
|
|
Status - Receives the status code if the function returns FALSE
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
DSTRING ntdrive;
|
|
UNICODE_STRING string;
|
|
OBJECT_ATTRIBUTES oa;
|
|
IO_STATUS_BLOCK status_block;
|
|
|
|
if (!IFS_SYSTEM::DosDriveNameToNtDriveName(Drive, &ntdrive)) {
|
|
|
|
*Status = STATUS_NO_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(string.Buffer = ntdrive.QueryWSTR())) {
|
|
|
|
*Status = STATUS_NO_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
string.Length = (USHORT) (ntdrive.QueryChCount()*sizeof(WCHAR));
|
|
string.MaximumLength = string.Length;
|
|
|
|
InitializeObjectAttributes( &oa,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
0 );
|
|
|
|
*Status = NtOpenFile(Handle,
|
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
|
&oa, &status_block,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_ALERT | FILE_WRITE_THROUGH);
|
|
|
|
return (BOOLEAN) NT_SUCCESS(*Status);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OpenReadOnlyDrive(
|
|
IN PCWSTRING Drive,
|
|
OUT PHANDLE Handle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens an NT handle to for the given dos drive name.
|
|
|
|
Arguments:
|
|
|
|
Drive - Supplies a dos drive name.
|
|
Handle - Returns an NT handle to the drive.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
DSTRING ntdrive;
|
|
UNICODE_STRING string;
|
|
OBJECT_ATTRIBUTES oa;
|
|
IO_STATUS_BLOCK status_block;
|
|
DSTRING backslash;
|
|
NTSTATUS Status;
|
|
|
|
|
|
if (!IFS_SYSTEM::DosDriveNameToNtDriveName(Drive, &ntdrive) ||
|
|
!backslash.Initialize("\\") ||
|
|
!ntdrive.Strcat(&backslash)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(string.Buffer = ntdrive.QueryWSTR())) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
string.Length = (USHORT) (ntdrive.QueryChCount()*sizeof(WCHAR));
|
|
string.MaximumLength = string.Length;
|
|
|
|
InitializeObjectAttributes( &oa,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
0 );
|
|
|
|
Status = NtOpenFile(Handle,
|
|
SYNCHRONIZE | FILE_READ_DATA,
|
|
&oa, &status_block,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_ALERT);
|
|
|
|
return (BOOLEAN) NT_SUCCESS(Status);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
GetLabelInput(
|
|
IN PCWSTRING DisplayDriveName,
|
|
IN PCWSTRING Drive,
|
|
OUT PBOOLEAN LabelExists,
|
|
OUT PWSTRING Label,
|
|
IN OUT PMESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prints the current label, if any, and the current serial
|
|
number. Then a new label is queried from the user.
|
|
|
|
Arguments:
|
|
|
|
DisplayDriveName
|
|
- The dos stype name to be displayed to the user
|
|
Drive - The dos style drive name.
|
|
LabelExists - Returns whether or not a label currently exists on
|
|
the volume.
|
|
Label - Returns the inputted label.
|
|
Message - Supplies an outlet for messages.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
CONST length = sizeof(FILE_FS_VOLUME_INFORMATION) + MaxLabelLength;
|
|
CONST max_fsname = 40;
|
|
|
|
// The buffer for FileFsVolumeInformation must be quadword-aligned.
|
|
|
|
LONGLONG info_buf[length/sizeof(LONGLONG) + 1];
|
|
PFILE_FS_VOLUME_INFORMATION info;
|
|
|
|
IO_STATUS_BLOCK status_block;
|
|
PUSHORT p;
|
|
DSTRING current_label;
|
|
HANDLE Handle;
|
|
WCHAR file_system[max_fsname];
|
|
MSGID label_prompt_msg;
|
|
DSTRING root_dir;
|
|
DSTRING slash;
|
|
PWSTR proot_dir;
|
|
DSTRING fsname;
|
|
DSTRING ntfs;
|
|
|
|
if (!OpenReadOnlyDrive(Drive, &Handle)) {
|
|
|
|
Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
|
|
Message->Display();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
info = (PFILE_FS_VOLUME_INFORMATION) info_buf;
|
|
|
|
if (!NT_SUCCESS(NtQueryVolumeInformationFile(Handle, &status_block,
|
|
info, length, FileFsVolumeInformation))) {
|
|
|
|
NtClose(Handle);
|
|
return FALSE;
|
|
}
|
|
|
|
NtClose(Handle);
|
|
|
|
info->VolumeLabel[info->VolumeLabelLength/sizeof(WCHAR)] = 0;
|
|
|
|
if (!current_label.Initialize(info->VolumeLabel)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (info->VolumeLabelLength) {
|
|
Message->Set(MSG_LBL_THE_LABEL);
|
|
Message->Display("%W%W", DisplayDriveName, ¤t_label);
|
|
*LabelExists = TRUE;
|
|
} else {
|
|
Message->Set(MSG_LBL_NO_LABEL);
|
|
Message->Display("%W", DisplayDriveName);
|
|
*LabelExists = FALSE;
|
|
}
|
|
|
|
p = (PUSHORT) &info->VolumeSerialNumber;
|
|
|
|
if (p[1] || p[0]) {
|
|
Message->Set(MSG_VOLUME_SERIAL_NUMBER);
|
|
Message->Display("%04X%04X", p[1], p[0]);
|
|
}
|
|
|
|
// Figure out which label prompt message to use.
|
|
|
|
label_prompt_msg = MSG_VOLUME_LABEL_PROMPT;
|
|
|
|
if (slash.Initialize("\\") &&
|
|
root_dir.Initialize(Drive) &&
|
|
root_dir.Strcat(&slash) &&
|
|
ntfs.Initialize("NTFS") &&
|
|
(proot_dir = root_dir.QueryWSTR())) {
|
|
|
|
if (GetVolumeInformation(proot_dir, NULL, 0, NULL, NULL,
|
|
NULL, file_system, max_fsname) &&
|
|
fsname.Initialize(file_system) &&
|
|
!fsname.Stricmp(&ntfs)) {
|
|
|
|
label_prompt_msg = MSG_VOLUME_LABEL_NO_MAX;
|
|
}
|
|
|
|
DELETE(proot_dir);
|
|
}
|
|
|
|
Message->Set(label_prompt_msg, ERROR_MESSAGE);
|
|
Message->Display();
|
|
|
|
return Message->QueryStringInput(Label);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SetLabel(
|
|
IN PCWSTRING Drive,
|
|
IN PCWSTRING Label,
|
|
IN OUT PMESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the supplied label on the supplied drive.
|
|
|
|
Arguments:
|
|
|
|
Drive - Supplies the dos drive name.
|
|
Label - Supplies a label.
|
|
Message - Supplies an outlet for messages.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
CONST length = sizeof(FILE_FS_LABEL_INFORMATION) + MaxLabelLength;
|
|
|
|
PFILE_FS_LABEL_INFORMATION info;
|
|
STR info_buf[length];
|
|
IO_STATUS_BLOCK status_block;
|
|
NTSTATUS nts;
|
|
DSTRING uppercase_label;
|
|
HANDLE Handle;
|
|
NTSTATUS status;
|
|
|
|
|
|
if (!OpenDrive(Drive, &Handle, &status)) {
|
|
|
|
if( status == STATUS_ACCESS_DENIED ) {
|
|
|
|
Message->Set(MSG_DASD_ACCESS_DENIED);
|
|
Message->Display("");
|
|
|
|
} else {
|
|
|
|
Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
|
|
Message->Display("");
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (!uppercase_label.Initialize(Label)) {
|
|
return FALSE;
|
|
}
|
|
|
|
info = (PFILE_FS_LABEL_INFORMATION) info_buf;
|
|
|
|
if (!uppercase_label.QueryWSTR(0, TO_END, info->VolumeLabel,
|
|
(length - sizeof(ULONG))/sizeof(WCHAR))) {
|
|
|
|
Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
|
|
Message->Display();
|
|
return FALSE;
|
|
}
|
|
|
|
info->VolumeLabelLength = uppercase_label.QueryChCount()*sizeof(WCHAR);
|
|
|
|
nts = NtSetVolumeInformationFile(Handle, &status_block, info,
|
|
length, FileFsLabelInformation);
|
|
|
|
if (!NT_SUCCESS(nts)) {
|
|
|
|
switch (nts) {
|
|
case STATUS_ACCESS_DENIED:
|
|
|
|
Message->Set(MSG_DASD_ACCESS_DENIED);
|
|
Message->Display();
|
|
break;
|
|
|
|
case STATUS_INVALID_VOLUME_LABEL:
|
|
|
|
Message->Set(MSG_INVALID_LABEL);
|
|
Message->Display();
|
|
break;
|
|
|
|
case STATUS_NOT_SUPPORTED:
|
|
case STATUS_INVALID_DEVICE_REQUEST:
|
|
|
|
Message->Set(MSG_LBL_NOT_SUPPORTED);
|
|
Message->Display();
|
|
break;
|
|
|
|
case STATUS_DISK_FULL:
|
|
|
|
Message->Set(MSG_INSUFFICIENT_DISK_SPACE);
|
|
Message->Display();
|
|
break;
|
|
|
|
case STATUS_MEDIA_WRITE_PROTECTED:
|
|
|
|
Message->Set(MSG_LBL_WRITE_PROTECTED_MEDIA);
|
|
Message->Display();
|
|
break;
|
|
|
|
case STATUS_CANNOT_MAKE:
|
|
|
|
Message->Set(MSG_LBL_ROOT_DIRECTORY_FULL);
|
|
Message->Display();
|
|
break;
|
|
|
|
case STATUS_REQUEST_ABORTED:
|
|
|
|
Message->Set(MSG_LBL_CHANGE_CANCEL);
|
|
Message->Display();
|
|
break;
|
|
|
|
default:
|
|
|
|
Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
|
|
Message->Display();
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INT __cdecl
|
|
main(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine emulates the dos 5 label command for NT.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
1 - An error occured.
|
|
0 - Success.
|
|
|
|
--*/
|
|
{
|
|
STREAM_MESSAGE msg;
|
|
ARGUMENT_LEXEMIZER arglex;
|
|
ARRAY lex_array;
|
|
ARRAY arg_array;
|
|
STRING_ARGUMENT progname;
|
|
REST_OF_LINE_ARGUMENT other_arg;
|
|
FLAG_ARGUMENT help_arg;
|
|
FLAG_ARGUMENT mp_arg;
|
|
DSTRING label_string;
|
|
DSTRING drive_string;
|
|
BOOLEAN label_exists;
|
|
PWSTRING p;
|
|
PATH path;
|
|
PATH fullpath;
|
|
DSTRING drive_path_string;
|
|
CHNUM position;
|
|
PATH_ANALYZE_CODE rst;
|
|
DSTRING DisplayDriveName;
|
|
|
|
if (!msg.Initialize(Get_Standard_Output_Stream(),
|
|
Get_Standard_Input_Stream(),
|
|
Get_Standard_Error_Stream())) {
|
|
return 1;
|
|
}
|
|
|
|
if (!lex_array.Initialize() || !arg_array.Initialize()) {
|
|
return 1;
|
|
}
|
|
|
|
if (!arglex.Initialize(&lex_array)) {
|
|
return 1;
|
|
}
|
|
|
|
arglex.SetCaseSensitive(FALSE);
|
|
|
|
if (!arglex.PrepareToParse()) {
|
|
return 1;
|
|
}
|
|
|
|
if (!progname.Initialize("*") ||
|
|
!help_arg.Initialize("/?") ||
|
|
!mp_arg.Initialize("/MP") ||
|
|
!other_arg.Initialize()) {
|
|
return 1;
|
|
}
|
|
|
|
if (!arg_array.Put(&progname) ||
|
|
!arg_array.Put(&help_arg) ||
|
|
!arg_array.Put(&mp_arg) ||
|
|
!arg_array.Put(&other_arg)) {
|
|
return 1;
|
|
}
|
|
|
|
if (!arglex.DoParsing(&arg_array)) {
|
|
msg.Set(MSG_INVALID_PARAMETER);
|
|
msg.Display("%W", p = arglex.QueryInvalidArgument());
|
|
return 1;
|
|
}
|
|
|
|
if (help_arg.QueryFlag()) {
|
|
DisplayLabelUsage(&msg);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Figure out what kind of combination of drive and label
|
|
//
|
|
|
|
if (!label_string.Initialize())
|
|
return 1;
|
|
|
|
if (other_arg.IsValueSet()) {
|
|
if (!drive_string.Initialize(other_arg.GetRestOfLine()) ||
|
|
!path.Initialize(other_arg.GetRestOfLine())) {
|
|
return 1;
|
|
}
|
|
if (mp_arg.IsValueSet()) {
|
|
// treat like 'label /mp [<mount point or guid volume name>] [label]'
|
|
// note that the mount point or guid volume name is not optional
|
|
if (drive_string.QueryChAt(1) == (WCHAR)':' || path.IsGuidVolName()) {
|
|
position = drive_string.Strchr(L' ');
|
|
if (position != INVALID_CHNUM) {
|
|
// split up the string into drive string and label string
|
|
if (!label_string.Initialize(&drive_string, position))
|
|
return 1;
|
|
drive_string.DeleteChAt(position, TO_END);
|
|
// get rid of spaces at the beginning
|
|
while (label_string.QueryChAt(0) == (WCHAR)' ')
|
|
label_string.DeleteChAt(0);
|
|
}
|
|
} else {
|
|
if (!label_string.Initialize(&drive_string) ||
|
|
!drive_string.Initialize(L"."))
|
|
return 1;
|
|
}
|
|
} else {
|
|
// treat like 'label [<driveletter>:][...label...]
|
|
// note that the drive letter is optional
|
|
if (drive_string.QueryChAt(1) == (WCHAR)':') {
|
|
if (!label_string.Initialize(&drive_string, 2))
|
|
return 1;
|
|
drive_string.DeleteChAt(2, TO_END);
|
|
// get rid of spaces at the beginning
|
|
while (label_string.QueryChAt(0) == (WCHAR)' ')
|
|
label_string.DeleteChAt(0);
|
|
} else if (path.IsGuidVolName()) {
|
|
position = drive_string.Strchr(L' ');
|
|
if (position != INVALID_CHNUM) {
|
|
// split up the string into drive string and label string
|
|
if (!label_string.Initialize(&drive_string, position))
|
|
return 1;
|
|
drive_string.DeleteChAt(position, TO_END);
|
|
// get rid of spaces at the beginning
|
|
while (label_string.QueryChAt(0) == (WCHAR)' ')
|
|
label_string.DeleteChAt(0);
|
|
}
|
|
} else {
|
|
if (!label_string.Initialize(&drive_string) ||
|
|
!SYSTEM::QueryCurrentDosDriveName(&drive_string)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (mp_arg.IsValueSet()) {
|
|
if (!drive_string.Initialize(L"."))
|
|
return 1;
|
|
} else {
|
|
if (!SYSTEM::QueryCurrentDosDriveName(&drive_string)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!path.Initialize(&drive_string))
|
|
return 1;
|
|
|
|
rst = path.AnalyzePath(&drive_string,
|
|
&fullpath,
|
|
&drive_path_string);
|
|
|
|
switch (rst) {
|
|
case PATH_OK:
|
|
case PATH_COULD_BE_FLOPPY:
|
|
if (drive_path_string.QueryChCount() != 0) {
|
|
msg.Set(MSG_FMT_INVALID_DRIVE_SPEC);
|
|
msg.Display();
|
|
return 1;
|
|
}
|
|
if (path.IsGuidVolName()) {
|
|
if (!DisplayDriveName.Initialize(&drive_string))
|
|
return 1;
|
|
} else {
|
|
if (!DisplayDriveName.Initialize(fullpath.GetPathString()))
|
|
return 1;
|
|
}
|
|
if (fullpath.GetPathString()->QueryChCount() == 2 &&
|
|
fullpath.GetPathString()->QueryChAt(1) == (WCHAR)':') {
|
|
if (!drive_string.Initialize(fullpath.GetPathString()))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case PATH_OUT_OF_MEMORY:
|
|
DebugPrint("Out of memory.\n");
|
|
return 1;
|
|
|
|
case PATH_NO_MOUNT_POINT_FOR_VOLUME_NAME_PATH:
|
|
msg.Set(MSG_LBL_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH);
|
|
msg.Display();
|
|
return 1;
|
|
|
|
default:
|
|
msg.Set(MSG_LBL_INVALID_DRIVE_SPEC);
|
|
msg.Display();
|
|
return 1;
|
|
}
|
|
|
|
if (label_string.QueryChCount() == 0) {
|
|
|
|
if (!GetLabelInput(&DisplayDriveName,
|
|
&drive_string, &label_exists,
|
|
&label_string, &msg)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!label_string.QueryChCount()) {
|
|
if (label_exists) {
|
|
msg.Set(MSG_LBL_DELETE_LABEL);
|
|
msg.Display("");
|
|
if (!msg.IsYesResponse(FALSE)) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return SetLabel(&drive_string, &label_string, &msg) ? 0 : 1;
|
|
}
|