Source code of Windows XP (NT5)
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.

501 lines
13 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. copy.c
  5. Abstract:
  6. This module implements the file copy command.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. BOOLEAN NoCopyPrompt;
  14. BOOLEAN AllowRemovableMedia;
  15. NTSTATUS
  16. pRcGetDeviceInfo(
  17. IN PWSTR FileName, // must be an nt name
  18. IN PFILE_FS_DEVICE_INFORMATION DeviceInfo
  19. )
  20. {
  21. BOOLEAN Removable = FALSE;
  22. IO_STATUS_BLOCK IoStatusBlock;
  23. UNICODE_STRING UnicodeString;
  24. HANDLE Handle;
  25. OBJECT_ATTRIBUTES Obja;
  26. NTSTATUS Status;
  27. PWSTR DeviceName;
  28. PWSTR s;
  29. //
  30. // get the device name from the file name
  31. //
  32. DeviceName = SpDupStringW( FileName );
  33. if (DeviceName == NULL) {
  34. return STATUS_OBJECT_PATH_INVALID;
  35. }
  36. s = wcschr(DeviceName+1,L'\\');
  37. if (!s) {
  38. return STATUS_OBJECT_PATH_INVALID;
  39. }
  40. s = wcschr(s+1,L'\\');
  41. if (s) {
  42. *s = 0;
  43. }
  44. INIT_OBJA(&Obja,&UnicodeString,DeviceName);
  45. Status = ZwCreateFile(
  46. &Handle,
  47. FILE_GENERIC_READ | SYNCHRONIZE,
  48. &Obja,
  49. &IoStatusBlock,
  50. NULL,
  51. FILE_ATTRIBUTE_NORMAL,
  52. FILE_SHARE_READ | FILE_SHARE_WRITE,
  53. FILE_OPEN,
  54. FILE_SYNCHRONOUS_IO_NONALERT,
  55. NULL,
  56. 0
  57. );
  58. SpMemFree(DeviceName);
  59. if(NT_SUCCESS(Status)) {
  60. Status = ZwQueryVolumeInformationFile(
  61. Handle,
  62. &IoStatusBlock,
  63. DeviceInfo,
  64. sizeof(FILE_FS_DEVICE_INFORMATION),
  65. FileFsDeviceInformation
  66. );
  67. ZwClose(Handle);
  68. }
  69. return Status;
  70. }
  71. NTSTATUS
  72. RcIsFileOnRemovableMedia(
  73. IN PWSTR FileName // must be an nt name
  74. )
  75. {
  76. NTSTATUS Status;
  77. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  78. Status = pRcGetDeviceInfo( FileName, &DeviceInfo );
  79. if(NT_SUCCESS(Status)) {
  80. if ((DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA) == 0) {
  81. Status = STATUS_NO_MEDIA;
  82. }
  83. }
  84. return Status;
  85. }
  86. NTSTATUS
  87. RcIsFileOnCDROM(
  88. IN PWSTR FileName // must be an nt name
  89. )
  90. {
  91. NTSTATUS Status;
  92. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  93. Status = pRcGetDeviceInfo( FileName, &DeviceInfo );
  94. if(NT_SUCCESS(Status)) {
  95. if (DeviceInfo.DeviceType != FILE_DEVICE_CD_ROM) {
  96. Status = STATUS_NO_MEDIA;
  97. }
  98. }
  99. return Status;
  100. }
  101. NTSTATUS
  102. RcIsFileOnFloppy(
  103. IN PWSTR FileName // must be an nt name
  104. )
  105. {
  106. NTSTATUS Status;
  107. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  108. Status = pRcGetDeviceInfo( FileName, &DeviceInfo );
  109. if(NT_SUCCESS(Status)) {
  110. if ((DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE) == 0) {
  111. Status = STATUS_NO_MEDIA;
  112. }
  113. }
  114. return Status;
  115. }
  116. BOOLEAN
  117. RcGetNTFileName(
  118. IN LPCWSTR DosPath,
  119. IN LPCWSTR NTPath
  120. )
  121. {
  122. BOOLEAN bResult = FALSE;
  123. extern LPWSTR _NtDrivePrefixes[26];
  124. WCHAR TempBuf[MAX_PATH*2];
  125. ULONG len;
  126. ULONG len2;
  127. LPWSTR Prefix;
  128. PWSTR s = NULL;
  129. Prefix = _NtDrivePrefixes[RcToUpper(DosPath[0])-L'A'];
  130. if (!Prefix) {
  131. return bResult;
  132. }
  133. GetDriveLetterLinkTarget((PWSTR)Prefix, &s);
  134. if (s) {
  135. len = wcslen(s);
  136. len2 = wcslen(DosPath) - 2;
  137. if (((len + len2) * sizeof(WCHAR)) < sizeof(TempBuf)){
  138. RtlZeroMemory(TempBuf,sizeof(TempBuf));
  139. RtlCopyMemory(TempBuf+len,DosPath+2,len2*sizeof(WCHAR));
  140. RtlCopyMemory(TempBuf,s,len*sizeof(WCHAR));
  141. TempBuf[len+len2] = 0;
  142. wcscpy((PWSTR)NTPath,TempBuf);
  143. bResult = TRUE;
  144. }
  145. }
  146. return bResult;
  147. }
  148. ULONG
  149. RcCmdCopy(
  150. IN PTOKENIZED_LINE TokenizedLine
  151. )
  152. {
  153. LPWSTR SrcFile;
  154. LPWSTR DstFile;
  155. LPWSTR SrcDosPath = NULL;
  156. LPWSTR SrcNtPath = NULL;
  157. LPWSTR DstDosPath = NULL;
  158. LPWSTR DstNtPath = NULL;
  159. IO_STATUS_BLOCK IoStatusBlock;
  160. UNICODE_STRING UnicodeString;
  161. HANDLE Handle;
  162. OBJECT_ATTRIBUTES Obja;
  163. NTSTATUS Status;
  164. LPWSTR YesNo;
  165. WCHAR Text[3];
  166. LPWSTR s;
  167. ULONG FileCount = 0;
  168. IO_STATUS_BLOCK status_block;
  169. FILE_BASIC_INFORMATION fileInfo;
  170. WCHAR * pos;
  171. ULONG CopyFlags = COPY_NOVERSIONCHECK;
  172. ASSERT(TokenizedLine->TokenCount >= 1);
  173. if (RcCmdParseHelp( TokenizedLine, MSG_COPY_HELP )) {
  174. return 1;
  175. }
  176. //
  177. // create a good source & destination file name
  178. //
  179. if( TokenizedLine->TokenCount == 2 ) {
  180. SrcFile = TokenizedLine->Tokens->Next->String;
  181. DstFile = NULL;
  182. } else {
  183. SrcFile = TokenizedLine->Tokens->Next->String;
  184. DstFile = TokenizedLine->Tokens->Next->Next->String;
  185. }
  186. if (RcDoesPathHaveWildCards(SrcFile)) {
  187. RcMessageOut(MSG_DIR_WILDCARD_NOT_SUPPORTED);
  188. goto exit;
  189. }
  190. //
  191. // Canonicalize the name once to get a full DOS-style path
  192. // we can print out, and another time to get the NT-style path
  193. // we'll use to actually do the work.
  194. //
  195. if (!RcFormFullPath( SrcFile, _CmdConsBlock->TemporaryBuffer, FALSE )) {
  196. RcMessageOut(MSG_INVALID_PATH);
  197. return 1;
  198. }
  199. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) {
  200. RcMessageOut(MSG_ACCESS_DENIED);
  201. goto exit;
  202. }
  203. SrcDosPath = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  204. if (!RcFormFullPath( SrcFile, _CmdConsBlock->TemporaryBuffer, TRUE )) {
  205. RcMessageOut(MSG_INVALID_PATH);
  206. return 1;
  207. }
  208. SrcNtPath = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  209. //
  210. // see if the source file exists
  211. //
  212. INIT_OBJA( &Obja, &UnicodeString, SrcNtPath );
  213. Status = ZwOpenFile(
  214. &Handle,
  215. FILE_READ_ATTRIBUTES,
  216. &Obja,
  217. &IoStatusBlock,
  218. FILE_SHARE_READ | FILE_SHARE_WRITE,
  219. 0
  220. );
  221. if( NT_SUCCESS(Status) ) {
  222. // check to see if the destination is a directory
  223. Status = ZwQueryInformationFile( Handle,
  224. &status_block,
  225. (PVOID)&fileInfo,
  226. sizeof( FILE_BASIC_INFORMATION ),
  227. FileBasicInformation );
  228. ZwClose( Handle );
  229. if( !NT_SUCCESS(Status) ) {
  230. // something went wrong
  231. RcNtError( Status, MSG_CANT_COPY_FILE );
  232. goto exit;
  233. }
  234. if( fileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  235. RcMessageOut(MSG_DIR_WILDCARD_NOT_SUPPORTED);
  236. goto exit;
  237. }
  238. } else {
  239. RcMessageOut(MSG_FILE_NOT_FOUND2);
  240. goto exit;
  241. }
  242. //
  243. // create a destination file name when the user did not
  244. // provide one. we use the source base file name and
  245. // the current drive and directory.
  246. //
  247. if ((DstFile == NULL) ||
  248. (wcscmp(DstFile, L".") == 0)) {
  249. s = wcsrchr( SrcDosPath, L'\\' );
  250. if( s ) {
  251. RcGetCurrentDriveAndDir( _CmdConsBlock->TemporaryBuffer );
  252. SpConcatenatePaths( _CmdConsBlock->TemporaryBuffer, s );
  253. DstFile = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  254. } else {
  255. RcMessageOut(MSG_INVALID_PATH);
  256. goto exit;
  257. }
  258. }
  259. //
  260. // create the destination paths
  261. //
  262. if (!RcFormFullPath( DstFile, _CmdConsBlock->TemporaryBuffer, FALSE )) {
  263. RcMessageOut(MSG_INVALID_PATH);
  264. return 1;
  265. }
  266. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,FALSE,FALSE)) {
  267. RcMessageOut(MSG_ACCESS_DENIED);
  268. goto exit;
  269. }
  270. DstDosPath = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  271. if (!RcFormFullPath( DstFile, _CmdConsBlock->TemporaryBuffer, TRUE )) {
  272. RcMessageOut(MSG_INVALID_PATH);
  273. return 1;
  274. }
  275. DstNtPath = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  276. //
  277. // check for removable media
  278. //
  279. if (AllowRemovableMedia == FALSE && RcIsFileOnRemovableMedia(DstNtPath) == STATUS_SUCCESS) {
  280. RcMessageOut(MSG_ACCESS_DENIED);
  281. goto exit;
  282. }
  283. //
  284. // see if the destination file already exists
  285. //
  286. INIT_OBJA( &Obja, &UnicodeString, DstNtPath );
  287. Status = ZwOpenFile(
  288. &Handle,
  289. FILE_READ_ATTRIBUTES,
  290. &Obja,
  291. &IoStatusBlock,
  292. FILE_SHARE_READ | FILE_SHARE_WRITE,
  293. 0
  294. );
  295. if( NT_SUCCESS(Status) ) {
  296. // the file exists!
  297. // check to see if the destination is a directory
  298. Status = ZwQueryInformationFile( Handle,
  299. &status_block,
  300. (PVOID)&fileInfo,
  301. sizeof( FILE_BASIC_INFORMATION ),
  302. FileBasicInformation );
  303. ZwClose( Handle );
  304. if( !NT_SUCCESS(Status) ) {
  305. // something went wrong
  306. RcNtError( Status, MSG_CANT_COPY_FILE );
  307. goto exit;
  308. }
  309. if( fileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  310. // yep, it's a directory
  311. // take the fully qualified source file path
  312. // and get the file name from it by finding the
  313. // last occurance of the \\ character
  314. pos = wcsrchr( SrcNtPath, L'\\' );
  315. SpMemFree( (PVOID)DstNtPath );
  316. // append the file name to the directory so that the copy
  317. // will work properly.
  318. if( pos != NULL ) {
  319. wcscat( _CmdConsBlock->TemporaryBuffer, pos );
  320. } else {
  321. wcscat( _CmdConsBlock->TemporaryBuffer, SrcNtPath );
  322. }
  323. DstNtPath = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  324. // now check again for the file's existence
  325. INIT_OBJA( &Obja, &UnicodeString, DstNtPath );
  326. Status = ZwOpenFile(
  327. &Handle,
  328. FILE_READ_ATTRIBUTES,
  329. &Obja,
  330. &IoStatusBlock,
  331. FILE_SHARE_READ | FILE_SHARE_WRITE,
  332. 0
  333. );
  334. if( NT_SUCCESS(Status) ) {
  335. ZwClose( Handle );
  336. //
  337. // Fetch yes/no text
  338. //
  339. if (InBatchMode == FALSE && NoCopyPrompt == FALSE) {
  340. YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNOALL, NULL, 0 );
  341. if( YesNo ) {
  342. s = wcsrchr( DstNtPath, L'\\' );
  343. RcMessageOut( MSG_COPY_OVERWRITE, s ? s+1 : DstNtPath );
  344. if( RcLineIn( Text, 2 ) ) {
  345. if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) {
  346. goto exit;
  347. }
  348. } else {
  349. goto exit;
  350. }
  351. SpMemFree( YesNo );
  352. }
  353. }
  354. }
  355. } else {
  356. //
  357. // If destination file was not compressed, copy it uncompressed.
  358. //
  359. if(!(fileInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED)) {
  360. CopyFlags |= COPY_FORCENOCOMP;
  361. }
  362. // nope the dest wasn't a dir, ask if we should overwrite
  363. //
  364. // Fetch yes/no text
  365. //
  366. if (InBatchMode == FALSE && NoCopyPrompt == FALSE) {
  367. YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNOALL, NULL, 0 );
  368. if( YesNo ) {
  369. s = wcsrchr( DstNtPath, L'\\' );
  370. RcMessageOut( MSG_COPY_OVERWRITE, s ? s+1 : DstNtPath );
  371. if( RcLineIn( Text, 2 ) ) {
  372. if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) {
  373. goto exit;
  374. }
  375. } else {
  376. goto exit;
  377. }
  378. SpMemFree( YesNo );
  379. }
  380. }
  381. }
  382. }
  383. Status = SpCopyFileUsingNames( SrcNtPath, DstNtPath, 0, CopyFlags );
  384. if( NT_SUCCESS(Status) ) {
  385. FileCount += 1;
  386. } else {
  387. RcNtError( Status, MSG_CANT_COPY_FILE );
  388. }
  389. if( FileCount ) {
  390. RcMessageOut( MSG_COPY_COUNT, FileCount );
  391. }
  392. exit:
  393. if( DstFile && TokenizedLine->TokenCount == 2 ) {
  394. SpMemFree( DstFile );
  395. }
  396. if( SrcDosPath ) {
  397. SpMemFree( SrcDosPath );
  398. }
  399. if( SrcNtPath ) {
  400. SpMemFree( SrcNtPath );
  401. }
  402. if( DstDosPath ) {
  403. SpMemFree( DstDosPath );
  404. }
  405. if( DstNtPath ) {
  406. SpMemFree( DstNtPath );
  407. }
  408. return 1;
  409. }