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.

1323 lines
34 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. virtual.cxx
  7. This module contains the virtual I/O package.
  8. Under Win32, the "current directory" is an attribute of a process,
  9. not a thread. This causes some grief for the FTPD service, since
  10. it is impersonating users on the server side. The users must
  11. "think" they can change current directory at will. We'll provide
  12. this behaviour in this package.
  13. Functions exported by this module:
  14. VirtualCreateFile
  15. VirtualCreateUniqueFile
  16. Virtual_fopen
  17. VirtualDeleteFile
  18. VirtualRenameFile
  19. VirtualChDir
  20. VirtualRmDir
  21. VirtualMkDir
  22. FILE HISTORY:
  23. KeithMo 09-Mar-1993 Created.
  24. MuraliK 28-Mar-1995 Enabled FILE_FLAG_OVERLAPPED in OpenFile()
  25. MuraliK 28-Apr-1995 modified to use new canonicalization
  26. 11-May-1995 made parameters to be const unless otherwise
  27. required.
  28. 12-May-1995 eliminated the old log file access
  29. */
  30. #include "ftpdp.hxx"
  31. //
  32. // Private prototypes.
  33. //
  34. VOID
  35. VirtualpSanitizePath(
  36. CHAR * pszPath
  37. );
  38. //
  39. // Public functions.
  40. //
  41. /*******************************************************************
  42. NAME: VirtualCreateFile
  43. SYNOPSIS: Creates a new (or overwrites an existing) file.
  44. Also handles moving the file pointer and truncating the
  45. file in the case of a REST command sequence.
  46. ENTRY: pUserData - The user initiating the request.
  47. phFile - Will receive the file handle. Will be
  48. INVALID_HANDLE_VALUE if an error occurs.
  49. pszFile - The name of the new file.
  50. fAppend - If TRUE, and pszFile already exists, then
  51. append to the existing file. Otherwise, create
  52. a new file. Note that FALSE will ALWAYS create
  53. a new file, potentially overwriting an existing
  54. file.
  55. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  56. error code.
  57. HISTORY:
  58. KeithMo 09-Mar-1993 Created.
  59. MuraliK 28-Apr-1995 modified to use new canonicalization
  60. ********************************************************************/
  61. APIERR
  62. VirtualCreateFile(
  63. USER_DATA * pUserData,
  64. HANDLE * phFile,
  65. LPSTR pszFile,
  66. BOOL fAppend
  67. )
  68. {
  69. HANDLE hFile = INVALID_HANDLE_VALUE;
  70. APIERR err;
  71. CHAR szCanonPath[MAX_PATH];
  72. DWORD cbSize = MAX_PATH;
  73. DWORD dwOffset;
  74. DBG_ASSERT( pUserData != NULL );
  75. DBG_ASSERT( phFile != NULL );
  76. DBG_ASSERT( pszFile != NULL );
  77. dwOffset = pUserData->QueryCurrentOffset();
  78. // We'll want to do pretty much the same thing whether we're
  79. // actually appending or just starting at an offset due to a REST
  80. // command, so combine them here.
  81. fAppend = fAppend || (dwOffset != 0);
  82. err = pUserData->VirtualCanonicalize(szCanonPath,
  83. &cbSize,
  84. pszFile,
  85. AccessTypeCreate );
  86. if( err == NO_ERROR ) {
  87. IF_DEBUG( VIRTUAL_IO )
  88. {
  89. DBGPRINTF(( DBG_CONTEXT,
  90. "creating %s\n", szCanonPath ));
  91. }
  92. if ( pUserData->ImpersonateUser()) {
  93. hFile = CreateFile( szCanonPath,
  94. GENERIC_READ | GENERIC_WRITE,
  95. FILE_SHARE_READ,
  96. NULL,
  97. fAppend ? OPEN_ALWAYS : CREATE_ALWAYS,
  98. FILE_ATTRIBUTE_NORMAL,
  99. NULL );
  100. //
  101. // Disallow usage of short names
  102. //
  103. pUserData->RevertToSelf();
  104. if ( hFile != INVALID_HANDLE_VALUE )
  105. {
  106. if ( GetFileType( hFile ) != FILE_TYPE_DISK )
  107. {
  108. DBG_REQUIRE( CloseHandle( hFile ) );
  109. SetLastError( ERROR_ACCESS_DENIED );
  110. hFile = INVALID_HANDLE_VALUE;
  111. }
  112. else if ( strchr( szCanonPath, '~' )) {
  113. BOOL fShort;
  114. DWORD err;
  115. err = CheckIfShortFileName( (UCHAR *) szCanonPath,
  116. pUserData->QueryImpersonationToken(),
  117. &fShort );
  118. if ( !err && fShort ) {
  119. err = ERROR_FILE_NOT_FOUND;
  120. }
  121. if ( err ) {
  122. DBG_REQUIRE( CloseHandle( hFile ));
  123. hFile = INVALID_HANDLE_VALUE;
  124. SetLastError( err );
  125. }
  126. }
  127. }
  128. }
  129. if( hFile == INVALID_HANDLE_VALUE ) {
  130. err = GetLastError();
  131. }
  132. if( fAppend && ( err == NO_ERROR ) ) {
  133. if (dwOffset == 0) {
  134. // This is a real append, not a restart sequence.
  135. if( SetFilePointer( hFile,
  136. 0,
  137. NULL,
  138. FILE_END )
  139. == (DWORD)-1L ) {
  140. err = GetLastError();
  141. }
  142. } else {
  143. // We're in part of a restart sequence. Set the file pointer
  144. // to the offset, and truncate the file there.
  145. if (SetFilePointer( hFile,
  146. dwOffset,
  147. NULL,
  148. FILE_BEGIN)
  149. == (DWORD)-1L &&
  150. (dwOffset != (DWORD)-1L) ) {
  151. err = GetLastError();
  152. }
  153. else {
  154. if (SetEndOfFile( hFile ) == 0) {
  155. err = GetLastError();
  156. }
  157. }
  158. }
  159. if (err != NO_ERROR ) {
  160. CloseHandle( hFile );
  161. hFile = INVALID_HANDLE_VALUE;
  162. }
  163. }
  164. }
  165. if ( err != NO_ERROR) {
  166. IF_DEBUG( VIRTUAL_IO )
  167. {
  168. DBGPRINTF(( DBG_CONTEXT,
  169. "cannot create %s, error %lu\n",
  170. szCanonPath,
  171. err ));
  172. }
  173. }
  174. *phFile = hFile;
  175. return err;
  176. } // VirtualCreateFile
  177. /*******************************************************************
  178. NAME: VirtualCreateUniqueFile
  179. SYNOPSIS: Creates a new unique (temporary) file in the current
  180. virtual directory.
  181. ENTRY: pUserData - The user initiating the request.
  182. phFile - Will receive the file handle. Will be
  183. INVALID_HANDLE_VALUE if an error occurs.
  184. pszTmpFile - Will receive the name of the temporary
  185. file. This buffer MUST be at least MAX_PATH
  186. characters long.
  187. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  188. error code.
  189. HISTORY:
  190. KeithMo 16-Mar-1993 Created.
  191. MuraliK 28-Apr-1995 modified to use new canonicalization
  192. ********************************************************************/
  193. APIERR
  194. VirtualCreateUniqueFile(
  195. USER_DATA * pUserData,
  196. HANDLE * phFile,
  197. LPSTR pszTmpFile
  198. )
  199. {
  200. HANDLE hFile = INVALID_HANDLE_VALUE;
  201. APIERR err = NO_ERROR;
  202. DWORD cbSize = MAX_PATH;
  203. CHAR szCanon[MAX_PATH];
  204. DBG_ASSERT( pUserData != NULL );
  205. DBG_ASSERT( phFile != NULL );
  206. DBG_ASSERT( pszTmpFile != NULL );
  207. //
  208. // Obtain the virtual to real path conversion.
  209. //
  210. err = pUserData->VirtualCanonicalize(szCanon,
  211. &cbSize,
  212. "", // current directory
  213. AccessTypeCreate);
  214. if( err == NO_ERROR )
  215. {
  216. IF_DEBUG( VIRTUAL_IO ) {
  217. DBGPRINTF(( DBG_CONTEXT,
  218. "creating unique file %s\n", pszTmpFile ));
  219. }
  220. if ( pUserData->ImpersonateUser()) {
  221. if ( GetTempFileName(szCanon,
  222. "FTPD",
  223. 0, pszTmpFile ) != 0
  224. ) {
  225. hFile = CreateFile( pszTmpFile,
  226. GENERIC_READ | GENERIC_WRITE,
  227. FILE_SHARE_READ,
  228. NULL,
  229. CREATE_ALWAYS,
  230. FILE_ATTRIBUTE_NORMAL,
  231. NULL );
  232. }
  233. pUserData->RevertToSelf();
  234. }
  235. if( hFile == INVALID_HANDLE_VALUE ) {
  236. err = GetLastError();
  237. }
  238. }
  239. if( err != 0 )
  240. {
  241. IF_DEBUG( VIRTUAL_IO )
  242. {
  243. DBGPRINTF(( DBG_CONTEXT,
  244. "cannot create unique file, error %lu\n",
  245. err ));
  246. }
  247. }
  248. *phFile = hFile;
  249. return err;
  250. } // VirtualCreateUniqueFile()
  251. /*******************************************************************
  252. NAME: Virtual_fopen
  253. SYNOPSIS: Opens an file stream.
  254. ENTRY: pUserData - The user initiating the request.
  255. pszFile - The name of the file to open.
  256. pszMode - The type of access required.
  257. RETURNS: FILE * - The open file stream, NULL if file cannot
  258. be opened.
  259. NOTES: Since this is only used for accessing the ~FTPSVC~.CKM
  260. annotation files, we don't log file accesses here.
  261. HISTORY:
  262. KeithMo 07-May-1993 Created.
  263. MuraliK 28-Apr-1995 modified to use new canonicalization
  264. ********************************************************************/
  265. FILE *
  266. Virtual_fopen(
  267. USER_DATA * pUserData,
  268. LPSTR pszFile,
  269. LPSTR pszMode
  270. )
  271. {
  272. FILE * pfile = NULL;
  273. APIERR err;
  274. CHAR szCanonPath[MAX_PATH];
  275. DWORD cbSize = MAX_PATH;
  276. DBG_ASSERT( pUserData != NULL );
  277. DBG_ASSERT( pszFile != NULL );
  278. DBG_ASSERT( pszMode != NULL );
  279. err = pUserData->VirtualCanonicalize(szCanonPath,
  280. &cbSize,
  281. pszFile,
  282. *pszMode == 'r'
  283. ? AccessTypeRead
  284. : AccessTypeWrite );
  285. if( err == NO_ERROR )
  286. {
  287. IF_DEBUG( VIRTUAL_IO )
  288. {
  289. DBGPRINTF(( DBG_CONTEXT,
  290. "opening %s\n", szCanonPath ));
  291. }
  292. if ( pUserData->ImpersonateUser()) {
  293. pfile = fopen( szCanonPath, pszMode );
  294. pUserData->RevertToSelf();
  295. }
  296. if( pfile == NULL )
  297. {
  298. err = ERROR_FILE_NOT_FOUND; // best guess
  299. }
  300. }
  301. IF_DEBUG( VIRTUAL_IO )
  302. {
  303. if( err != NO_ERROR )
  304. {
  305. DBGPRINTF(( DBG_CONTEXT,
  306. "cannot open %s, error %lu\n",
  307. pszFile,
  308. err ));
  309. }
  310. }
  311. return pfile;
  312. } // Virtual_fopen
  313. /*******************************************************************
  314. NAME: VirtualDeleteFile
  315. SYNOPSIS: Deletes an existing file.
  316. ENTRY: pUserData - The user initiating the request.
  317. pszFile - The name of the file.
  318. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  319. error code.
  320. HISTORY:
  321. KeithMo 09-Mar-1993 Created.
  322. ********************************************************************/
  323. APIERR
  324. VirtualDeleteFile(
  325. USER_DATA * pUserData,
  326. LPSTR pszFile
  327. )
  328. {
  329. APIERR err;
  330. DWORD cbSize = MAX_PATH;
  331. CHAR szCanonPath[MAX_PATH];
  332. DWORD dwAccessMask = 0;
  333. DBG_ASSERT( pUserData != NULL );
  334. //
  335. // We'll canonicalize the path, asking for *read* access. If
  336. // the path canonicalizes correctly, we'll then try to open the
  337. // file to ensure it exists. Only then will we check for delete
  338. // access to the path. This mumbo-jumbo is necessary to get the
  339. // proper error codes if someone trys to delete a nonexistent
  340. // file on a read-only volume.
  341. //
  342. err = pUserData->VirtualCanonicalize(szCanonPath,
  343. &cbSize,
  344. pszFile,
  345. AccessTypeRead,
  346. &dwAccessMask);
  347. if( err == NO_ERROR )
  348. {
  349. HANDLE hFile = INVALID_HANDLE_VALUE;
  350. if ( pUserData->ImpersonateUser()) {
  351. hFile = CreateFile( szCanonPath,
  352. GENERIC_READ,
  353. FILE_SHARE_READ,
  354. NULL,
  355. OPEN_EXISTING,
  356. FILE_ATTRIBUTE_NORMAL,
  357. NULL );
  358. pUserData->RevertToSelf();
  359. }
  360. if( hFile == INVALID_HANDLE_VALUE )
  361. {
  362. err = GetLastError();
  363. }
  364. else
  365. {
  366. //
  367. // The file DOES exist. Close the handle, then check
  368. // to ensure we really have delete access.
  369. //
  370. CloseHandle( hFile );
  371. if( !PathAccessCheck( AccessTypeDelete,
  372. dwAccessMask,
  373. TEST_UF(pUserData, READ_ACCESS),
  374. TEST_UF(pUserData, WRITE_ACCESS)
  375. )
  376. ) {
  377. err = ERROR_ACCESS_DENIED;
  378. }
  379. }
  380. }
  381. if( err == NO_ERROR )
  382. {
  383. IF_DEBUG( VIRTUAL_IO )
  384. {
  385. DBGPRINTF(( DBG_CONTEXT,
  386. "deleting %s\n", szCanonPath ));
  387. }
  388. if ( pUserData->ImpersonateUser()) {
  389. if( !DeleteFile( szCanonPath ) ) {
  390. err = GetLastError();
  391. IF_DEBUG( VIRTUAL_IO ) {
  392. DBGPRINTF(( DBG_CONTEXT,
  393. "cannot delete %s, error %lu\n",
  394. szCanonPath,
  395. err ));
  396. }
  397. }
  398. pUserData->RevertToSelf();
  399. } else {
  400. err = GetLastError();
  401. }
  402. }
  403. else
  404. {
  405. IF_DEBUG( VIRTUAL_IO )
  406. {
  407. DBGPRINTF(( DBG_CONTEXT,
  408. "cannot canonicalize %s - %s, error %lu\n",
  409. pUserData->QueryCurrentDirectory(),
  410. pszFile,
  411. err ));
  412. }
  413. }
  414. return err;
  415. } // VirtualDeleteFile()
  416. /*******************************************************************
  417. NAME: VirtualRenameFile
  418. SYNOPSIS: Renames an existing file or directory.
  419. ENTRY: pUserData - The user initiating the request.
  420. pszExisting - The name of an existing file or directory.
  421. pszNew - The new name for the file or directory.
  422. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  423. error code.
  424. HISTORY:
  425. KeithMo 10-Mar-1993 Created.
  426. ********************************************************************/
  427. APIERR
  428. VirtualRenameFile(
  429. USER_DATA * pUserData,
  430. LPSTR pszExisting,
  431. LPSTR pszNew
  432. )
  433. {
  434. APIERR err;
  435. DWORD cbSize = MAX_PATH;
  436. CHAR szCanonExisting[MAX_PATH];
  437. DBG_ASSERT( pUserData != NULL );
  438. err = pUserData->VirtualCanonicalize(szCanonExisting,
  439. &cbSize,
  440. pszExisting,
  441. AccessTypeDelete );
  442. if( err == NO_ERROR ) {
  443. CHAR szCanonNew[MAX_PATH];
  444. cbSize = MAX_PATH;
  445. err = pUserData->VirtualCanonicalize(szCanonNew,
  446. &cbSize,
  447. pszNew,
  448. AccessTypeCreate );
  449. if( err == NO_ERROR ) {
  450. IF_DEBUG( VIRTUAL_IO ) {
  451. DBGPRINTF(( DBG_CONTEXT,
  452. "renaming %s to %s\n",
  453. szCanonExisting,
  454. szCanonNew ));
  455. }
  456. if ( pUserData->ImpersonateUser()) {
  457. if( !MoveFileEx( szCanonExisting,
  458. szCanonNew,
  459. pUserData->QueryInstance()->AllowReplaceOnRename()
  460. ? MOVEFILE_REPLACE_EXISTING
  461. : 0 )
  462. ){
  463. err = GetLastError();
  464. IF_DEBUG( VIRTUAL_IO ) {
  465. DBGPRINTF(( DBG_CONTEXT,
  466. "cannot rename %s to %s, error %lu\n",
  467. szCanonExisting,
  468. szCanonNew,
  469. err ));
  470. }
  471. }
  472. pUserData->RevertToSelf();
  473. } else {
  474. err = GetLastError();
  475. }
  476. } else {
  477. IF_DEBUG( VIRTUAL_IO ) {
  478. DBGPRINTF(( DBG_CONTEXT,
  479. "cannot canonicalize %s - %s, error %lu\n",
  480. pUserData->QueryCurrentDirectory(),
  481. pszExisting,
  482. err ));
  483. }
  484. }
  485. } else {
  486. IF_DEBUG( VIRTUAL_IO ) {
  487. DBGPRINTF(( DBG_CONTEXT,
  488. "cannot canonicalize %s - %s, error %lu\n",
  489. pUserData->QueryCurrentDirectory(),
  490. pszExisting,
  491. err ));
  492. }
  493. }
  494. return err;
  495. } // VirtualRenameFile()
  496. APIERR
  497. VirtualChDir(
  498. USER_DATA * pUserData,
  499. LPSTR pszDir
  500. )
  501. /*++
  502. This function sets the current directory to newly specified directory.
  503. Arguments:
  504. pUserData -- the user initiating the request
  505. pszDir -- pointer to null-terminated buffer containing the
  506. new directory name.
  507. Returns:
  508. APIERR -- NO_ERROR if successful, otherwise a Win32 error code.
  509. History:
  510. MuraliK 28-Apr-1995 Modified to use symbolic roots.
  511. --*/
  512. {
  513. CHAR rgchVirtual[MAX_PATH];
  514. DWORD cbVirtSize;
  515. APIERR err = NO_ERROR;
  516. DWORD cbSize;
  517. CHAR szCanonDir[MAX_PATH];
  518. DBG_ASSERT( pUserData != NULL );
  519. if (pszDir == NULL || *pszDir == '\0') {
  520. //
  521. // Nothing new specified.
  522. //
  523. return ( NO_ERROR);
  524. }
  525. //
  526. // Canonicalize the new path.
  527. //
  528. cbSize = sizeof(szCanonDir);
  529. cbVirtSize = sizeof(rgchVirtual);
  530. err = pUserData->VirtualCanonicalize(szCanonDir,
  531. &cbSize,
  532. pszDir,
  533. AccessTypeRead,
  534. NULL,
  535. rgchVirtual,
  536. &cbVirtSize);
  537. if ( err == ERROR_ACCESS_DENIED) {
  538. //
  539. // this maybe a write only virtual root directory.
  540. // Let us try again to find we have Write access atleast
  541. //
  542. cbSize = sizeof(szCanonDir);
  543. cbVirtSize = sizeof(rgchVirtual);
  544. err = pUserData->VirtualCanonicalize(szCanonDir,
  545. &cbSize,
  546. pszDir,
  547. AccessTypeWrite,
  548. NULL,
  549. rgchVirtual,
  550. &cbVirtSize);
  551. }
  552. if( err != NO_ERROR )
  553. {
  554. IF_DEBUG( VIRTUAL_IO )
  555. {
  556. DBGPRINTF(( DBG_CONTEXT,
  557. "cannot canonicalize %s - %s, error %lu\n",
  558. pUserData->QueryCurrentDirectory(),
  559. pszDir,
  560. err ));
  561. }
  562. return err;
  563. }
  564. //
  565. // Try to open the directory and get a handle for the same.
  566. //
  567. // This is possibly a new place to change directory to.
  568. if ( pUserData->ImpersonateUser()) {
  569. HANDLE CurrentDirHandle = INVALID_HANDLE_VALUE;
  570. err = OpenPathForAccess( &CurrentDirHandle,
  571. szCanonDir,
  572. GENERIC_READ,
  573. FILE_SHARE_READ | FILE_SHARE_WRITE,
  574. pUserData->QueryImpersonationToken()
  575. );
  576. if( err == ERROR_ACCESS_DENIED ) {
  577. err = OpenPathForAccess( &CurrentDirHandle,
  578. szCanonDir,
  579. GENERIC_WRITE,
  580. FILE_SHARE_READ | FILE_SHARE_WRITE,
  581. pUserData->QueryImpersonationToken()
  582. );
  583. }
  584. if( err == NO_ERROR ) {
  585. BY_HANDLE_FILE_INFORMATION fileInfo;
  586. BOOL fRet;
  587. fRet = GetFileInformationByHandle( CurrentDirHandle,
  588. &fileInfo);
  589. if ( !fRet) {
  590. err = GetLastError();
  591. // Error in getting the file information.
  592. // close handle and return.
  593. CloseHandle( CurrentDirHandle);
  594. } else {
  595. if ( (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  596. != FILE_ATTRIBUTE_DIRECTORY) {
  597. //
  598. // this file is not a directory.
  599. // Do not change directory. But return error.
  600. //
  601. err = ERROR_DIRECTORY;
  602. CloseHandle( CurrentDirHandle);
  603. } else {
  604. //
  605. // Directory successfully opened. Save the handle
  606. // in the per-user data. This handle is maintained to
  607. // prevent accidental deletion of the directory.
  608. //
  609. if( pUserData->CurrentDirHandle != INVALID_HANDLE_VALUE ) {
  610. IF_DEBUG( VIRTUAL_IO ) {
  611. DBGPRINTF(( DBG_CONTEXT,
  612. "closing dir handle %08lX for %s\n",
  613. pUserData->CurrentDirHandle,
  614. pUserData->QueryCurrentDirectory()));
  615. }
  616. CloseHandle( pUserData->CurrentDirHandle );
  617. }
  618. pUserData->CurrentDirHandle = CurrentDirHandle;
  619. IF_DEBUG( VIRTUAL_IO ) {
  620. DBGPRINTF(( DBG_CONTEXT,
  621. "opened directory %s, handle = %08lX\n",
  622. szCanonDir,
  623. CurrentDirHandle ));
  624. }
  625. // update the current directory
  626. pUserData->SetCurrentDirectory( rgchVirtual );
  627. }
  628. }
  629. }
  630. pUserData->RevertToSelf();
  631. } else {
  632. // Impersonation failed
  633. err = GetLastError();
  634. }
  635. IF_DEBUG( VIRTUAL_IO ) {
  636. DBGPRINTF(( DBG_CONTEXT,
  637. "chdir to %s returns error = %d\n", szCanonDir, err ));
  638. }
  639. return ( err);
  640. } // VirtualChDir()
  641. /*******************************************************************
  642. NAME: VirtualRmDir
  643. SYNOPSIS: Removes an existing directory.
  644. ENTRY: pUserData - The user initiating the request.
  645. pszDir - The name of the directory to remove.
  646. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  647. error code.
  648. HISTORY:
  649. KeithMo 09-Mar-1993 Created.
  650. ********************************************************************/
  651. APIERR
  652. VirtualRmDir(
  653. USER_DATA * pUserData,
  654. LPSTR pszDir
  655. )
  656. {
  657. APIERR err;
  658. DWORD cbSize = MAX_PATH;
  659. CHAR szCanonDir[MAX_PATH];
  660. err = pUserData->VirtualCanonicalize(szCanonDir,
  661. &cbSize,
  662. pszDir,
  663. AccessTypeDelete );
  664. if( err == NO_ERROR )
  665. {
  666. IF_DEBUG( VIRTUAL_IO )
  667. {
  668. DBGPRINTF(( DBG_CONTEXT,
  669. "rmdir %s\n", szCanonDir ));
  670. }
  671. if ( pUserData->ImpersonateUser()) {
  672. if( !RemoveDirectory( szCanonDir ) ) {
  673. err = GetLastError();
  674. IF_DEBUG( VIRTUAL_IO ) {
  675. DBGPRINTF(( DBG_CONTEXT,
  676. "cannot rmdir %s, error %lu\n",
  677. szCanonDir,
  678. err ));
  679. }
  680. }
  681. pUserData->RevertToSelf();
  682. } else {
  683. err = GetLastError();
  684. }
  685. } else {
  686. IF_DEBUG( VIRTUAL_IO )
  687. {
  688. DBGPRINTF(( DBG_CONTEXT,
  689. "cannot canonicalize %s - %s, error %lu\n",
  690. pUserData->QueryCurrentDirectory(),
  691. pszDir,
  692. err ));
  693. }
  694. }
  695. return err;
  696. } // VirtualRmDir
  697. /*******************************************************************
  698. NAME: VirtualMkDir
  699. SYNOPSIS: Creates a new directory.
  700. ENTRY: pUserData - The user initiating the request.
  701. pszDir - The name of the directory to create.
  702. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  703. error code.
  704. HISTORY:
  705. KeithMo 09-Mar-1993 Created.
  706. ********************************************************************/
  707. APIERR
  708. VirtualMkDir(
  709. USER_DATA * pUserData,
  710. LPSTR pszDir
  711. )
  712. {
  713. APIERR err;
  714. DWORD cbSize = MAX_PATH;
  715. CHAR szCanonDir[MAX_PATH];
  716. DBG_ASSERT( pUserData != NULL );
  717. err = pUserData->VirtualCanonicalize(szCanonDir,
  718. &cbSize,
  719. pszDir,
  720. AccessTypeCreate );
  721. if( err == NO_ERROR )
  722. {
  723. IF_DEBUG( VIRTUAL_IO )
  724. {
  725. DBGPRINTF(( DBG_CONTEXT,
  726. "mkdir %s\n", szCanonDir ));
  727. }
  728. if ( pUserData->ImpersonateUser()) {
  729. if( !CreateDirectory( szCanonDir, NULL ) ) {
  730. err = GetLastError();
  731. IF_DEBUG( VIRTUAL_IO )
  732. {
  733. DBGPRINTF(( DBG_CONTEXT,
  734. "cannot mkdir %s, error %lu\n",
  735. szCanonDir,
  736. err ));
  737. }
  738. }
  739. pUserData->RevertToSelf();
  740. } else {
  741. err = GetLastError();
  742. }
  743. } else {
  744. IF_DEBUG( VIRTUAL_IO ) {
  745. DBGPRINTF(( DBG_CONTEXT,
  746. "cannot canonicalize %s - %s, error %lu\n",
  747. pUserData->QueryCurrentDirectory(),
  748. pszDir,
  749. err ));
  750. }
  751. }
  752. return err;
  753. } // VirtualMkDir
  754. //
  755. // Private constants.
  756. //
  757. #define ACTION_NOTHING 0x00000000
  758. #define ACTION_EMIT_CH 0x00010000
  759. #define ACTION_EMIT_DOT_CH 0x00020000
  760. #define ACTION_EMIT_DOT_DOT_CH 0x00030000
  761. #define ACTION_BACKUP 0x00040000
  762. #define ACTION_MASK 0xFFFF0000
  763. //
  764. // Private globals.
  765. //
  766. INT p_StateTable[4][4] =
  767. {
  768. { // state 0
  769. 1 | ACTION_EMIT_CH, // "\"
  770. 0 | ACTION_EMIT_CH, // "."
  771. 4 | ACTION_EMIT_CH, // EOS
  772. 0 | ACTION_EMIT_CH // other
  773. },
  774. { // state 1
  775. 1 | ACTION_NOTHING, // "\"
  776. 2 | ACTION_NOTHING, // "."
  777. 4 | ACTION_EMIT_CH, // EOS
  778. 0 | ACTION_EMIT_CH // other
  779. },
  780. { // state 2
  781. 1 | ACTION_NOTHING, // "\"
  782. 3 | ACTION_NOTHING, // "."
  783. 4 | ACTION_EMIT_CH, // EOS
  784. 0 | ACTION_EMIT_DOT_CH // other
  785. },
  786. { // state 3
  787. 1 | ACTION_BACKUP, // "\"
  788. 0 | ACTION_EMIT_DOT_DOT_CH, // "."
  789. 4 | ACTION_BACKUP, // EOS
  790. 0 | ACTION_EMIT_DOT_DOT_CH // other
  791. }
  792. };
  793. /*******************************************************************
  794. NAME: VirtualpSanitizePath
  795. SYNOPSIS: Sanitizes a path by removing bogus path elements.
  796. As expected, "/./" entries are simply removed, and
  797. "/../" entries are removed along with the previous
  798. path element.
  799. To maintain compatibility with URL path semantics
  800. additional transformations are required. All backward
  801. slashes "\\" are converted to forward slashes. Any
  802. repeated forward slashes (such as "///") are mapped to
  803. single backslashes. Also, any trailing path elements
  804. consisting solely of dots "/....." are removed.
  805. Thus, the path "/foo\./bar/../tar\....\......" is
  806. mapped to "/foo/tar".
  807. A state table (see the p_StateTable global at the
  808. beginning of this file) is used to perform most of
  809. the transformations. The table's rows are indexed
  810. by current state, and the columns are indexed by
  811. the current character's "class" (either slash, dot,
  812. NULL, or other). Each entry in the table consists
  813. of the new state tagged with an action to perform.
  814. See the ACTION_* constants for the valid action
  815. codes.
  816. After the FSA is finished with the path, we make one
  817. additional pass through it to remove any trailing
  818. backslash, and to remove any trailing path elements
  819. consisting solely of dots.
  820. ENTRY: pszPath - The path to sanitize.
  821. HISTORY:
  822. KeithMo 07-Sep-1994 Created.
  823. MuraliK 28-Apr-1995 Adopted this for symbolic paths
  824. ********************************************************************/
  825. VOID
  826. VirtualpSanitizePath(
  827. CHAR * pszPath
  828. )
  829. {
  830. CHAR * pszSrc;
  831. CHAR * pszDest;
  832. CHAR * pszHead;
  833. CHAR ch;
  834. INT State;
  835. INT Class;
  836. BOOL fDBCS = FALSE;
  837. //
  838. // Ensure we got a valid symbolic path (something starting "/"
  839. //
  840. DBG_ASSERT( pszPath != NULL );
  841. // DBG_ASSERT( pszPath[0] == '/');
  842. //
  843. // Start our scan at the first "/.
  844. //
  845. pszHead = pszSrc = pszDest = pszPath;
  846. //
  847. // State 0 is the initial state.
  848. //
  849. State = 0;
  850. //
  851. // Loop until we enter state 4 (the final, accepting state).
  852. //
  853. while( State != 4 )
  854. {
  855. //
  856. // Grab the next character from the path and compute its
  857. // character class. While we're at it, map any forward
  858. // slashes to backward slashes.
  859. //
  860. ch = *pszSrc++;
  861. switch( ch )
  862. {
  863. case '\\' :
  864. //
  865. // fDBCS is always false for non-DBCS system
  866. //
  867. if ( fDBCS )
  868. {
  869. Class = 3;
  870. break;
  871. }
  872. ch = '/'; // convert it to symbolic URL path separator char.
  873. /* fall through */
  874. case '/' :
  875. Class = 0;
  876. break;
  877. case '.' :
  878. Class = 1;
  879. break;
  880. case '\0' :
  881. Class = 2;
  882. break;
  883. default :
  884. Class = 3;
  885. break;
  886. }
  887. //
  888. // Advance to the next state.
  889. //
  890. State = p_StateTable[State][Class];
  891. //
  892. // Perform the action associated with the state.
  893. //
  894. switch( State & ACTION_MASK )
  895. {
  896. case ACTION_EMIT_DOT_DOT_CH :
  897. *pszDest++ = '.';
  898. /* fall through */
  899. case ACTION_EMIT_DOT_CH :
  900. *pszDest++ = '.';
  901. /* fall through */
  902. case ACTION_EMIT_CH :
  903. *pszDest++ = ch;
  904. /* fall through */
  905. case ACTION_NOTHING :
  906. break;
  907. case ACTION_BACKUP :
  908. if( pszDest > ( pszHead + 1 ) )
  909. {
  910. pszDest--;
  911. DBG_ASSERT( *pszDest == '/' );
  912. *pszDest = '\0';
  913. pszDest = strrchr( pszPath, '/') + 1;
  914. }
  915. *pszDest = '\0';
  916. break;
  917. default :
  918. DBG_ASSERT( !"Invalid action code in state table!" );
  919. State = 4;
  920. *pszDest++ = '\0';
  921. break;
  922. }
  923. State &= ~ACTION_MASK;
  924. if ( !fDBCS )
  925. {
  926. if ( IsDBCSLeadByte( ch ) )
  927. {
  928. fDBCS = TRUE;
  929. }
  930. }
  931. else
  932. {
  933. fDBCS = FALSE;
  934. }
  935. }
  936. //
  937. // Remove any trailing slash.
  938. //
  939. pszDest -= 2;
  940. if( ( strlen( pszPath ) > 3 ) && ( *pszDest == '/' ) )
  941. {
  942. *pszDest = '\0';
  943. }
  944. //
  945. // If the final path elements consists solely of dots, remove them.
  946. //
  947. while( strlen( pszPath ) > 3 )
  948. {
  949. pszDest = strrchr( pszPath, '/');
  950. DBG_ASSERT( pszDest != NULL );
  951. pszHead = pszDest;
  952. pszDest++;
  953. while( ch = *pszDest++ )
  954. {
  955. if( ch != '.' )
  956. {
  957. break;
  958. }
  959. }
  960. if( ch == '\0' )
  961. {
  962. //
  963. // this is probably dead code left over from
  964. // when we used physical paths.
  965. //
  966. if( pszHead == ( pszPath + 2 ) )
  967. {
  968. pszHead++;
  969. }
  970. // end of dead code
  971. //
  972. // Don't remove the first '/'
  973. //
  974. if ( pszHead == pszPath )
  975. {
  976. pszHead++;
  977. }
  978. *pszHead = '\0';
  979. }
  980. else
  981. {
  982. break;
  983. }
  984. }
  985. } // VirtualpSanitizePath
  986.