Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

669 lines
17 KiB

  1. /*++
  2. Copyright (c) 1991-2000 Microsoft Corporation
  3. Module Name:
  4. LABEL
  5. Abstract:
  6. Label is a DOS-5 compatible volume label changing utility
  7. Author:
  8. Norbert Kluster (norbertk) 18-Apr-1991
  9. Notes:
  10. Revision History:
  11. --*/
  12. #define _NTAPI_ULIB_
  13. #include "ulib.hxx"
  14. #include "arg.hxx"
  15. #include "array.hxx"
  16. #include "smsg.hxx"
  17. #include "rtmsg.h"
  18. #include "wstring.hxx"
  19. #include "path.hxx"
  20. #include "substrng.hxx"
  21. #include "system.hxx"
  22. #include "ifssys.hxx"
  23. #include "ulibcl.hxx"
  24. extern "C" {
  25. #include "ntioapi.h"
  26. }
  27. CONST MaxLabelLength = 1024;
  28. VOID
  29. DisplayLabelUsage(
  30. IN OUT PMESSAGE Message
  31. )
  32. /*++
  33. Routine Description:
  34. This routine displays the usage for the dos 5 label program.
  35. Arguments:
  36. Message - Supplies an outlet for the messages.
  37. Return Value:
  38. None.
  39. --*/
  40. {
  41. Message->Set(MSG_LBL_INFO);
  42. Message->Display("");
  43. Message->Set(MSG_LBL_USAGE);
  44. Message->Display("");
  45. }
  46. BOOLEAN
  47. OpenDrive(
  48. IN PCWSTRING Drive,
  49. OUT PHANDLE Handle,
  50. OUT PNTSTATUS Status
  51. )
  52. /*++
  53. Routine Description:
  54. This routine opens an NT handle to for the given dos drive name.
  55. Arguments:
  56. Drive - Supplies a dos drive name.
  57. Handle - Returns an NT handle to the drive.
  58. Status - Receives the status code if the function returns FALSE
  59. Return Value:
  60. FALSE - Failure.
  61. TRUE - Success.
  62. --*/
  63. {
  64. DSTRING ntdrive;
  65. UNICODE_STRING string;
  66. OBJECT_ATTRIBUTES oa;
  67. IO_STATUS_BLOCK status_block;
  68. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(Drive, &ntdrive)) {
  69. *Status = STATUS_NO_MEMORY;
  70. return FALSE;
  71. }
  72. if (!(string.Buffer = ntdrive.QueryWSTR())) {
  73. *Status = STATUS_NO_MEMORY;
  74. return FALSE;
  75. }
  76. string.Length = (USHORT) (ntdrive.QueryChCount()*sizeof(WCHAR));
  77. string.MaximumLength = string.Length;
  78. InitializeObjectAttributes( &oa,
  79. &string,
  80. OBJ_CASE_INSENSITIVE,
  81. 0,
  82. 0 );
  83. *Status = NtOpenFile(Handle,
  84. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  85. &oa, &status_block,
  86. FILE_SHARE_READ | FILE_SHARE_WRITE,
  87. FILE_SYNCHRONOUS_IO_ALERT | FILE_WRITE_THROUGH);
  88. return (BOOLEAN) NT_SUCCESS(*Status);
  89. }
  90. BOOLEAN
  91. OpenReadOnlyDrive(
  92. IN PCWSTRING Drive,
  93. OUT PHANDLE Handle
  94. )
  95. /*++
  96. Routine Description:
  97. This routine opens an NT handle to for the given dos drive name.
  98. Arguments:
  99. Drive - Supplies a dos drive name.
  100. Handle - Returns an NT handle to the drive.
  101. Return Value:
  102. FALSE - Failure.
  103. TRUE - Success.
  104. --*/
  105. {
  106. DSTRING ntdrive;
  107. UNICODE_STRING string;
  108. OBJECT_ATTRIBUTES oa;
  109. IO_STATUS_BLOCK status_block;
  110. DSTRING backslash;
  111. NTSTATUS Status;
  112. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(Drive, &ntdrive) ||
  113. !backslash.Initialize("\\") ||
  114. !ntdrive.Strcat(&backslash)) {
  115. return FALSE;
  116. }
  117. if (!(string.Buffer = ntdrive.QueryWSTR())) {
  118. return FALSE;
  119. }
  120. string.Length = (USHORT) (ntdrive.QueryChCount()*sizeof(WCHAR));
  121. string.MaximumLength = string.Length;
  122. InitializeObjectAttributes( &oa,
  123. &string,
  124. OBJ_CASE_INSENSITIVE,
  125. 0,
  126. 0 );
  127. Status = NtOpenFile(Handle,
  128. SYNCHRONIZE | FILE_READ_DATA,
  129. &oa, &status_block,
  130. FILE_SHARE_READ | FILE_SHARE_WRITE,
  131. FILE_SYNCHRONOUS_IO_ALERT);
  132. return (BOOLEAN) NT_SUCCESS(Status);
  133. }
  134. BOOLEAN
  135. GetLabelInput(
  136. IN PCWSTRING DisplayDriveName,
  137. IN PCWSTRING Drive,
  138. OUT PBOOLEAN LabelExists,
  139. OUT PWSTRING Label,
  140. IN OUT PMESSAGE Message
  141. )
  142. /*++
  143. Routine Description:
  144. This routine prints the current label, if any, and the current serial
  145. number. Then a new label is queried from the user.
  146. Arguments:
  147. DisplayDriveName
  148. - The dos stype name to be displayed to the user
  149. Drive - The dos style drive name.
  150. LabelExists - Returns whether or not a label currently exists on
  151. the volume.
  152. Label - Returns the inputted label.
  153. Message - Supplies an outlet for messages.
  154. Return Value:
  155. FALSE - Failure.
  156. TRUE - Success.
  157. --*/
  158. {
  159. CONST length = sizeof(FILE_FS_VOLUME_INFORMATION) + MaxLabelLength;
  160. CONST max_fsname = 40;
  161. // The buffer for FileFsVolumeInformation must be quadword-aligned.
  162. LONGLONG info_buf[length/sizeof(LONGLONG) + 1];
  163. PFILE_FS_VOLUME_INFORMATION info;
  164. IO_STATUS_BLOCK status_block;
  165. PUSHORT p;
  166. DSTRING current_label;
  167. HANDLE Handle;
  168. WCHAR file_system[max_fsname];
  169. MSGID label_prompt_msg;
  170. DSTRING root_dir;
  171. DSTRING slash;
  172. PWSTR proot_dir;
  173. DSTRING fsname;
  174. DSTRING ntfs;
  175. if (!OpenReadOnlyDrive(Drive, &Handle)) {
  176. Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
  177. Message->Display();
  178. return FALSE;
  179. }
  180. info = (PFILE_FS_VOLUME_INFORMATION) info_buf;
  181. if (!NT_SUCCESS(NtQueryVolumeInformationFile(Handle, &status_block,
  182. info, length, FileFsVolumeInformation))) {
  183. NtClose(Handle);
  184. return FALSE;
  185. }
  186. NtClose(Handle);
  187. info->VolumeLabel[info->VolumeLabelLength/sizeof(WCHAR)] = 0;
  188. if (!current_label.Initialize(info->VolumeLabel)) {
  189. return FALSE;
  190. }
  191. if (info->VolumeLabelLength) {
  192. Message->Set(MSG_LBL_THE_LABEL);
  193. Message->Display("%W%W", DisplayDriveName, &current_label);
  194. *LabelExists = TRUE;
  195. } else {
  196. Message->Set(MSG_LBL_NO_LABEL);
  197. Message->Display("%W", DisplayDriveName);
  198. *LabelExists = FALSE;
  199. }
  200. p = (PUSHORT) &info->VolumeSerialNumber;
  201. if (p[1] || p[0]) {
  202. Message->Set(MSG_VOLUME_SERIAL_NUMBER);
  203. Message->Display("%04X%04X", p[1], p[0]);
  204. }
  205. // Figure out which label prompt message to use.
  206. label_prompt_msg = MSG_VOLUME_LABEL_PROMPT;
  207. if (slash.Initialize("\\") &&
  208. root_dir.Initialize(Drive) &&
  209. root_dir.Strcat(&slash) &&
  210. ntfs.Initialize("NTFS") &&
  211. (proot_dir = root_dir.QueryWSTR())) {
  212. if (GetVolumeInformation(proot_dir, NULL, 0, NULL, NULL,
  213. NULL, file_system, max_fsname) &&
  214. fsname.Initialize(file_system) &&
  215. !fsname.Stricmp(&ntfs)) {
  216. label_prompt_msg = MSG_VOLUME_LABEL_NO_MAX;
  217. }
  218. DELETE(proot_dir);
  219. }
  220. Message->Set(label_prompt_msg, ERROR_MESSAGE);
  221. Message->Display();
  222. return Message->QueryStringInput(Label);
  223. }
  224. BOOLEAN
  225. SetLabel(
  226. IN PCWSTRING Drive,
  227. IN PCWSTRING Label,
  228. IN OUT PMESSAGE Message
  229. )
  230. /*++
  231. Routine Description:
  232. This routine sets the supplied label on the supplied drive.
  233. Arguments:
  234. Drive - Supplies the dos drive name.
  235. Label - Supplies a label.
  236. Message - Supplies an outlet for messages.
  237. Return Value:
  238. FALSE - Failure.
  239. TRUE - Success.
  240. --*/
  241. {
  242. CONST length = sizeof(FILE_FS_LABEL_INFORMATION) + MaxLabelLength;
  243. PFILE_FS_LABEL_INFORMATION info;
  244. STR info_buf[length];
  245. IO_STATUS_BLOCK status_block;
  246. NTSTATUS nts;
  247. DSTRING uppercase_label;
  248. HANDLE Handle;
  249. NTSTATUS status;
  250. if (!OpenDrive(Drive, &Handle, &status)) {
  251. if( status == STATUS_ACCESS_DENIED ) {
  252. Message->Set(MSG_DASD_ACCESS_DENIED);
  253. Message->Display("");
  254. } else {
  255. Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
  256. Message->Display("");
  257. }
  258. return FALSE;
  259. }
  260. if (!uppercase_label.Initialize(Label)) {
  261. return FALSE;
  262. }
  263. info = (PFILE_FS_LABEL_INFORMATION) info_buf;
  264. if (!uppercase_label.QueryWSTR(0, TO_END, info->VolumeLabel,
  265. (length - sizeof(ULONG))/sizeof(WCHAR))) {
  266. Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
  267. Message->Display();
  268. return FALSE;
  269. }
  270. info->VolumeLabelLength = uppercase_label.QueryChCount()*sizeof(WCHAR);
  271. nts = NtSetVolumeInformationFile(Handle, &status_block, info,
  272. length, FileFsLabelInformation);
  273. if (!NT_SUCCESS(nts)) {
  274. switch (nts) {
  275. case STATUS_ACCESS_DENIED:
  276. Message->Set(MSG_DASD_ACCESS_DENIED);
  277. Message->Display();
  278. break;
  279. case STATUS_INVALID_VOLUME_LABEL:
  280. Message->Set(MSG_INVALID_LABEL);
  281. Message->Display();
  282. break;
  283. case STATUS_NOT_SUPPORTED:
  284. case STATUS_INVALID_DEVICE_REQUEST:
  285. Message->Set(MSG_LBL_NOT_SUPPORTED);
  286. Message->Display();
  287. break;
  288. case STATUS_DISK_FULL:
  289. Message->Set(MSG_INSUFFICIENT_DISK_SPACE);
  290. Message->Display();
  291. break;
  292. case STATUS_MEDIA_WRITE_PROTECTED:
  293. Message->Set(MSG_LBL_WRITE_PROTECTED_MEDIA);
  294. Message->Display();
  295. break;
  296. case STATUS_CANNOT_MAKE:
  297. Message->Set(MSG_LBL_ROOT_DIRECTORY_FULL);
  298. Message->Display();
  299. break;
  300. case STATUS_REQUEST_ABORTED:
  301. Message->Set(MSG_LBL_CHANGE_CANCEL);
  302. Message->Display();
  303. break;
  304. default:
  305. Message->Set(MSG_INCOMPATIBLE_PARAMETERS);
  306. Message->Display();
  307. break;
  308. }
  309. return FALSE;
  310. }
  311. return TRUE;
  312. }
  313. INT __cdecl
  314. main(
  315. )
  316. /*++
  317. Routine Description:
  318. This routine emulates the dos 5 label command for NT.
  319. Arguments:
  320. None.
  321. Return Value:
  322. 1 - An error occured.
  323. 0 - Success.
  324. --*/
  325. {
  326. STREAM_MESSAGE msg;
  327. ARGUMENT_LEXEMIZER arglex;
  328. ARRAY lex_array;
  329. ARRAY arg_array;
  330. STRING_ARGUMENT progname;
  331. REST_OF_LINE_ARGUMENT other_arg;
  332. FLAG_ARGUMENT help_arg;
  333. FLAG_ARGUMENT mp_arg;
  334. DSTRING label_string;
  335. DSTRING drive_string;
  336. BOOLEAN label_exists;
  337. PWSTRING p;
  338. PATH path;
  339. PATH fullpath;
  340. DSTRING drive_path_string;
  341. CHNUM position;
  342. PATH_ANALYZE_CODE rst;
  343. DSTRING DisplayDriveName;
  344. if (!msg.Initialize(Get_Standard_Output_Stream(),
  345. Get_Standard_Input_Stream(),
  346. Get_Standard_Error_Stream())) {
  347. return 1;
  348. }
  349. if (!lex_array.Initialize() || !arg_array.Initialize()) {
  350. return 1;
  351. }
  352. if (!arglex.Initialize(&lex_array)) {
  353. return 1;
  354. }
  355. arglex.SetCaseSensitive(FALSE);
  356. if (!arglex.PrepareToParse()) {
  357. return 1;
  358. }
  359. if (!progname.Initialize("*") ||
  360. !help_arg.Initialize("/?") ||
  361. !mp_arg.Initialize("/MP") ||
  362. !other_arg.Initialize()) {
  363. return 1;
  364. }
  365. if (!arg_array.Put(&progname) ||
  366. !arg_array.Put(&help_arg) ||
  367. !arg_array.Put(&mp_arg) ||
  368. !arg_array.Put(&other_arg)) {
  369. return 1;
  370. }
  371. if (!arglex.DoParsing(&arg_array)) {
  372. msg.Set(MSG_INVALID_PARAMETER);
  373. msg.Display("%W", p = arglex.QueryInvalidArgument());
  374. return 1;
  375. }
  376. if (help_arg.QueryFlag()) {
  377. DisplayLabelUsage(&msg);
  378. return 0;
  379. }
  380. //
  381. // Figure out what kind of combination of drive and label
  382. //
  383. if (!label_string.Initialize())
  384. return 1;
  385. if (other_arg.IsValueSet()) {
  386. if (!drive_string.Initialize(other_arg.GetRestOfLine()) ||
  387. !path.Initialize(other_arg.GetRestOfLine())) {
  388. return 1;
  389. }
  390. if (mp_arg.IsValueSet()) {
  391. // treat like 'label /mp [<mount point or guid volume name>] [label]'
  392. // note that the mount point or guid volume name is not optional
  393. if (drive_string.QueryChAt(1) == (WCHAR)':' || path.IsGuidVolName()) {
  394. position = drive_string.Strchr(L' ');
  395. if (position != INVALID_CHNUM) {
  396. // split up the string into drive string and label string
  397. if (!label_string.Initialize(&drive_string, position))
  398. return 1;
  399. drive_string.DeleteChAt(position, TO_END);
  400. // get rid of spaces at the beginning
  401. while (label_string.QueryChAt(0) == (WCHAR)' ')
  402. label_string.DeleteChAt(0);
  403. }
  404. } else {
  405. if (!label_string.Initialize(&drive_string) ||
  406. !drive_string.Initialize(L"."))
  407. return 1;
  408. }
  409. } else {
  410. // treat like 'label [<driveletter>:][...label...]
  411. // note that the drive letter is optional
  412. if (drive_string.QueryChAt(1) == (WCHAR)':') {
  413. if (!label_string.Initialize(&drive_string, 2))
  414. return 1;
  415. drive_string.DeleteChAt(2, TO_END);
  416. // get rid of spaces at the beginning
  417. while (label_string.QueryChAt(0) == (WCHAR)' ')
  418. label_string.DeleteChAt(0);
  419. } else if (path.IsGuidVolName()) {
  420. position = drive_string.Strchr(L' ');
  421. if (position != INVALID_CHNUM) {
  422. // split up the string into drive string and label string
  423. if (!label_string.Initialize(&drive_string, position))
  424. return 1;
  425. drive_string.DeleteChAt(position, TO_END);
  426. // get rid of spaces at the beginning
  427. while (label_string.QueryChAt(0) == (WCHAR)' ')
  428. label_string.DeleteChAt(0);
  429. }
  430. } else {
  431. if (!label_string.Initialize(&drive_string) ||
  432. !SYSTEM::QueryCurrentDosDriveName(&drive_string)) {
  433. return 1;
  434. }
  435. }
  436. }
  437. } else {
  438. if (mp_arg.IsValueSet()) {
  439. if (!drive_string.Initialize(L"."))
  440. return 1;
  441. } else {
  442. if (!SYSTEM::QueryCurrentDosDriveName(&drive_string)) {
  443. return 1;
  444. }
  445. }
  446. }
  447. if (!path.Initialize(&drive_string))
  448. return 1;
  449. rst = path.AnalyzePath(&drive_string,
  450. &fullpath,
  451. &drive_path_string);
  452. switch (rst) {
  453. case PATH_OK:
  454. case PATH_COULD_BE_FLOPPY:
  455. if (drive_path_string.QueryChCount() != 0) {
  456. msg.Set(MSG_FMT_INVALID_DRIVE_SPEC);
  457. msg.Display();
  458. return 1;
  459. }
  460. if (path.IsGuidVolName()) {
  461. if (!DisplayDriveName.Initialize(&drive_string))
  462. return 1;
  463. } else {
  464. if (!DisplayDriveName.Initialize(fullpath.GetPathString()))
  465. return 1;
  466. }
  467. if (fullpath.GetPathString()->QueryChCount() == 2 &&
  468. fullpath.GetPathString()->QueryChAt(1) == (WCHAR)':') {
  469. if (!drive_string.Initialize(fullpath.GetPathString()))
  470. return 1;
  471. }
  472. break;
  473. case PATH_OUT_OF_MEMORY:
  474. DebugPrint("Out of memory.\n");
  475. return 1;
  476. case PATH_NO_MOUNT_POINT_FOR_VOLUME_NAME_PATH:
  477. msg.Set(MSG_LBL_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH);
  478. msg.Display();
  479. return 1;
  480. default:
  481. msg.Set(MSG_LBL_INVALID_DRIVE_SPEC);
  482. msg.Display();
  483. return 1;
  484. }
  485. if (label_string.QueryChCount() == 0) {
  486. if (!GetLabelInput(&DisplayDriveName,
  487. &drive_string, &label_exists,
  488. &label_string, &msg)) {
  489. return 1;
  490. }
  491. }
  492. if (!label_string.QueryChCount()) {
  493. if (label_exists) {
  494. msg.Set(MSG_LBL_DELETE_LABEL);
  495. msg.Display("");
  496. if (!msg.IsYesResponse(FALSE)) {
  497. return 0;
  498. }
  499. } else {
  500. return 0;
  501. }
  502. }
  503. return SetLabel(&drive_string, &label_string, &msg) ? 0 : 1;
  504. }