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.

1352 lines
35 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. APIERR err = NO_ERROR;
  515. DWORD cbSize;
  516. CHAR szCanonDir[MAX_PATH];
  517. DBG_ASSERT( pUserData != NULL );
  518. if (pszDir == NULL || *pszDir == '\0') {
  519. //
  520. // Nothing new specified.
  521. //
  522. return ( NO_ERROR);
  523. }
  524. //
  525. // Form the complete virtual path for the given path.
  526. // We have to form a compelete virtual path and sanitize it, because:
  527. // if this new directory is valid, we have to store the
  528. // sanitized path in the CurrentDirectory member of USER_DATA.
  529. //
  530. if ( !IS_PATH_SEP( *pszDir)) {
  531. const CHAR * pszNewDir = pUserData->QueryCurrentDirectory();
  532. //
  533. // This is a relative path. append it to currrent directory
  534. //
  535. if ( strlen(pszNewDir) + strlen(pszDir) + 2 <= MAX_PATH) {
  536. // copy the current directory
  537. wsprintfA( rgchVirtual, "%s/%s",
  538. pszNewDir, pszDir);
  539. pszDir = rgchVirtual;
  540. } else {
  541. // long path --> is not supported.
  542. DBGPRINTF((DBG_CONTEXT, "Long Virtual Path %s---%s\n",
  543. pszNewDir, pszDir));
  544. return ( ERROR_PATH_NOT_FOUND);
  545. }
  546. }
  547. DBG_ASSERT( IS_PATH_SEP(*pszDir));
  548. VirtualpSanitizePath( pszDir);
  549. //
  550. // Canonicalize the new path.
  551. // Canonicalize() automatically updates the current directory if need be
  552. //
  553. cbSize = sizeof(szCanonDir);
  554. err = pUserData->VirtualCanonicalize(szCanonDir,
  555. &cbSize,
  556. pszDir,
  557. AccessTypeRead );
  558. if ( err == ERROR_ACCESS_DENIED) {
  559. //
  560. // this maybe a write only virtual root directory.
  561. // Let us try again to find we have Write access atleast
  562. //
  563. cbSize = sizeof(szCanonDir);
  564. err = pUserData->VirtualCanonicalize(szCanonDir,
  565. &cbSize,
  566. pszDir,
  567. AccessTypeWrite );
  568. }
  569. if( err != NO_ERROR )
  570. {
  571. IF_DEBUG( VIRTUAL_IO )
  572. {
  573. DBGPRINTF(( DBG_CONTEXT,
  574. "cannot canonicalize %s - %s, error %lu\n",
  575. pUserData->QueryCurrentDirectory(),
  576. pszDir,
  577. err ));
  578. }
  579. return err;
  580. }
  581. //
  582. // Try to open the directory and get a handle for the same.
  583. //
  584. // This is possibly a new place to change directory to.
  585. if ( pUserData->ImpersonateUser()) {
  586. HANDLE CurrentDirHandle = INVALID_HANDLE_VALUE;
  587. err = OpenPathForAccess( &CurrentDirHandle,
  588. szCanonDir,
  589. GENERIC_READ,
  590. FILE_SHARE_READ | FILE_SHARE_WRITE,
  591. pUserData->QueryImpersonationToken()
  592. );
  593. if( err == ERROR_ACCESS_DENIED ) {
  594. err = OpenPathForAccess( &CurrentDirHandle,
  595. szCanonDir,
  596. GENERIC_WRITE,
  597. FILE_SHARE_READ | FILE_SHARE_WRITE,
  598. pUserData->QueryImpersonationToken()
  599. );
  600. }
  601. if( err == NO_ERROR ) {
  602. BY_HANDLE_FILE_INFORMATION fileInfo;
  603. BOOL fRet;
  604. fRet = GetFileInformationByHandle( CurrentDirHandle,
  605. &fileInfo);
  606. if ( !fRet) {
  607. err = GetLastError();
  608. // Error in getting the file information.
  609. // close handle and return.
  610. CloseHandle( CurrentDirHandle);
  611. } else {
  612. if ( (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  613. != FILE_ATTRIBUTE_DIRECTORY) {
  614. //
  615. // this file is not a directory.
  616. // Do not change directory. But return error.
  617. //
  618. err = ERROR_DIRECTORY;
  619. CloseHandle( CurrentDirHandle);
  620. } else {
  621. //
  622. // Directory successfully opened. Save the handle
  623. // in the per-user data. This handle is maintained to
  624. // prevent accidental deletion of the directory.
  625. //
  626. if( pUserData->CurrentDirHandle != INVALID_HANDLE_VALUE ) {
  627. IF_DEBUG( VIRTUAL_IO ) {
  628. DBGPRINTF(( DBG_CONTEXT,
  629. "closing dir handle %08lX for %s\n",
  630. pUserData->CurrentDirHandle,
  631. pUserData->QueryCurrentDirectory()));
  632. }
  633. CloseHandle( pUserData->CurrentDirHandle );
  634. }
  635. pUserData->CurrentDirHandle = CurrentDirHandle;
  636. IF_DEBUG( VIRTUAL_IO ) {
  637. DBGPRINTF(( DBG_CONTEXT,
  638. "opened directory %s, handle = %08lX\n",
  639. szCanonDir,
  640. CurrentDirHandle ));
  641. }
  642. // update the current directory
  643. pUserData->SetCurrentDirectory( pszDir );
  644. }
  645. }
  646. }
  647. pUserData->RevertToSelf();
  648. } else {
  649. // Impersonation failed
  650. err = GetLastError();
  651. }
  652. IF_DEBUG( VIRTUAL_IO ) {
  653. DBGPRINTF(( DBG_CONTEXT,
  654. "chdir to %s returns error = %d\n", szCanonDir, err ));
  655. }
  656. return ( err);
  657. } // VirtualChDir()
  658. /*******************************************************************
  659. NAME: VirtualRmDir
  660. SYNOPSIS: Removes an existing directory.
  661. ENTRY: pUserData - The user initiating the request.
  662. pszDir - The name of the directory to remove.
  663. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  664. error code.
  665. HISTORY:
  666. KeithMo 09-Mar-1993 Created.
  667. ********************************************************************/
  668. APIERR
  669. VirtualRmDir(
  670. USER_DATA * pUserData,
  671. LPSTR pszDir
  672. )
  673. {
  674. APIERR err;
  675. DWORD cbSize = MAX_PATH;
  676. CHAR szCanonDir[MAX_PATH];
  677. err = pUserData->VirtualCanonicalize(szCanonDir,
  678. &cbSize,
  679. pszDir,
  680. AccessTypeDelete );
  681. if( err == NO_ERROR )
  682. {
  683. IF_DEBUG( VIRTUAL_IO )
  684. {
  685. DBGPRINTF(( DBG_CONTEXT,
  686. "rmdir %s\n", szCanonDir ));
  687. }
  688. if ( pUserData->ImpersonateUser()) {
  689. if( !RemoveDirectory( szCanonDir ) ) {
  690. err = GetLastError();
  691. IF_DEBUG( VIRTUAL_IO ) {
  692. DBGPRINTF(( DBG_CONTEXT,
  693. "cannot rmdir %s, error %lu\n",
  694. szCanonDir,
  695. err ));
  696. }
  697. }
  698. pUserData->RevertToSelf();
  699. } else {
  700. err = GetLastError();
  701. }
  702. } else {
  703. IF_DEBUG( VIRTUAL_IO )
  704. {
  705. DBGPRINTF(( DBG_CONTEXT,
  706. "cannot canonicalize %s - %s, error %lu\n",
  707. pUserData->QueryCurrentDirectory(),
  708. pszDir,
  709. err ));
  710. }
  711. }
  712. return err;
  713. } // VirtualRmDir
  714. /*******************************************************************
  715. NAME: VirtualMkDir
  716. SYNOPSIS: Creates a new directory.
  717. ENTRY: pUserData - The user initiating the request.
  718. pszDir - The name of the directory to create.
  719. RETURNS: APIERR - NO_ERROR if successful, otherwise a Win32
  720. error code.
  721. HISTORY:
  722. KeithMo 09-Mar-1993 Created.
  723. ********************************************************************/
  724. APIERR
  725. VirtualMkDir(
  726. USER_DATA * pUserData,
  727. LPSTR pszDir
  728. )
  729. {
  730. APIERR err;
  731. DWORD cbSize = MAX_PATH;
  732. CHAR szCanonDir[MAX_PATH];
  733. DBG_ASSERT( pUserData != NULL );
  734. err = pUserData->VirtualCanonicalize(szCanonDir,
  735. &cbSize,
  736. pszDir,
  737. AccessTypeCreate );
  738. if( err == NO_ERROR )
  739. {
  740. IF_DEBUG( VIRTUAL_IO )
  741. {
  742. DBGPRINTF(( DBG_CONTEXT,
  743. "mkdir %s\n", szCanonDir ));
  744. }
  745. if ( pUserData->ImpersonateUser()) {
  746. if( !CreateDirectory( szCanonDir, NULL ) ) {
  747. err = GetLastError();
  748. IF_DEBUG( VIRTUAL_IO )
  749. {
  750. DBGPRINTF(( DBG_CONTEXT,
  751. "cannot mkdir %s, error %lu\n",
  752. szCanonDir,
  753. err ));
  754. }
  755. }
  756. pUserData->RevertToSelf();
  757. } else {
  758. err = GetLastError();
  759. }
  760. } else {
  761. IF_DEBUG( VIRTUAL_IO ) {
  762. DBGPRINTF(( DBG_CONTEXT,
  763. "cannot canonicalize %s - %s, error %lu\n",
  764. pUserData->QueryCurrentDirectory(),
  765. pszDir,
  766. err ));
  767. }
  768. }
  769. return err;
  770. } // VirtualMkDir
  771. //
  772. // Private constants.
  773. //
  774. #define ACTION_NOTHING 0x00000000
  775. #define ACTION_EMIT_CH 0x00010000
  776. #define ACTION_EMIT_DOT_CH 0x00020000
  777. #define ACTION_EMIT_DOT_DOT_CH 0x00030000
  778. #define ACTION_BACKUP 0x00040000
  779. #define ACTION_MASK 0xFFFF0000
  780. //
  781. // Private globals.
  782. //
  783. INT p_StateTable[4][4] =
  784. {
  785. { // state 0
  786. 1 | ACTION_EMIT_CH, // "\"
  787. 0 | ACTION_EMIT_CH, // "."
  788. 4 | ACTION_EMIT_CH, // EOS
  789. 0 | ACTION_EMIT_CH // other
  790. },
  791. { // state 1
  792. 1 | ACTION_NOTHING, // "\"
  793. 2 | ACTION_NOTHING, // "."
  794. 4 | ACTION_EMIT_CH, // EOS
  795. 0 | ACTION_EMIT_CH // other
  796. },
  797. { // state 2
  798. 1 | ACTION_NOTHING, // "\"
  799. 3 | ACTION_NOTHING, // "."
  800. 4 | ACTION_EMIT_CH, // EOS
  801. 0 | ACTION_EMIT_DOT_CH // other
  802. },
  803. { // state 3
  804. 1 | ACTION_BACKUP, // "\"
  805. 0 | ACTION_EMIT_DOT_DOT_CH, // "."
  806. 4 | ACTION_BACKUP, // EOS
  807. 0 | ACTION_EMIT_DOT_DOT_CH // other
  808. }
  809. };
  810. /*******************************************************************
  811. NAME: VirtualpSanitizePath
  812. SYNOPSIS: Sanitizes a path by removing bogus path elements.
  813. As expected, "/./" entries are simply removed, and
  814. "/../" entries are removed along with the previous
  815. path element.
  816. To maintain compatibility with URL path semantics
  817. additional transformations are required. All backward
  818. slashes "\\" are converted to forward slashes. Any
  819. repeated forward slashes (such as "///") are mapped to
  820. single backslashes. Also, any trailing path elements
  821. consisting solely of dots "/....." are removed.
  822. Thus, the path "/foo\./bar/../tar\....\......" is
  823. mapped to "/foo/tar".
  824. A state table (see the p_StateTable global at the
  825. beginning of this file) is used to perform most of
  826. the transformations. The table's rows are indexed
  827. by current state, and the columns are indexed by
  828. the current character's "class" (either slash, dot,
  829. NULL, or other). Each entry in the table consists
  830. of the new state tagged with an action to perform.
  831. See the ACTION_* constants for the valid action
  832. codes.
  833. After the FSA is finished with the path, we make one
  834. additional pass through it to remove any trailing
  835. backslash, and to remove any trailing path elements
  836. consisting solely of dots.
  837. ENTRY: pszPath - The path to sanitize.
  838. HISTORY:
  839. KeithMo 07-Sep-1994 Created.
  840. MuraliK 28-Apr-1995 Adopted this for symbolic paths
  841. ********************************************************************/
  842. VOID
  843. VirtualpSanitizePath(
  844. CHAR * pszPath
  845. )
  846. {
  847. CHAR * pszSrc;
  848. CHAR * pszDest;
  849. CHAR * pszHead;
  850. CHAR ch;
  851. INT State;
  852. INT Class;
  853. BOOL fDBCS = FALSE;
  854. //
  855. // Ensure we got a valid symbolic path (something starting "/"
  856. //
  857. DBG_ASSERT( pszPath != NULL );
  858. // DBG_ASSERT( pszPath[0] == '/');
  859. //
  860. // Start our scan at the first "/.
  861. //
  862. pszHead = pszSrc = pszDest = pszPath;
  863. //
  864. // State 0 is the initial state.
  865. //
  866. State = 0;
  867. //
  868. // Loop until we enter state 4 (the final, accepting state).
  869. //
  870. while( State != 4 )
  871. {
  872. //
  873. // Grab the next character from the path and compute its
  874. // character class. While we're at it, map any forward
  875. // slashes to backward slashes.
  876. //
  877. ch = *pszSrc++;
  878. switch( ch )
  879. {
  880. case '\\' :
  881. //
  882. // fDBCS is always false for non-DBCS system
  883. //
  884. if ( fDBCS )
  885. {
  886. Class = 3;
  887. break;
  888. }
  889. ch = '/'; // convert it to symbolic URL path separator char.
  890. /* fall through */
  891. case '/' :
  892. Class = 0;
  893. break;
  894. case '.' :
  895. Class = 1;
  896. break;
  897. case '\0' :
  898. Class = 2;
  899. break;
  900. default :
  901. Class = 3;
  902. break;
  903. }
  904. //
  905. // Advance to the next state.
  906. //
  907. State = p_StateTable[State][Class];
  908. //
  909. // Perform the action associated with the state.
  910. //
  911. switch( State & ACTION_MASK )
  912. {
  913. case ACTION_EMIT_DOT_DOT_CH :
  914. *pszDest++ = '.';
  915. /* fall through */
  916. case ACTION_EMIT_DOT_CH :
  917. *pszDest++ = '.';
  918. /* fall through */
  919. case ACTION_EMIT_CH :
  920. *pszDest++ = ch;
  921. /* fall through */
  922. case ACTION_NOTHING :
  923. break;
  924. case ACTION_BACKUP :
  925. if( pszDest > ( pszHead + 1 ) )
  926. {
  927. pszDest--;
  928. DBG_ASSERT( *pszDest == '/' );
  929. *pszDest = '\0';
  930. pszDest = strrchr( pszPath, '/') + 1;
  931. }
  932. *pszDest = '\0';
  933. break;
  934. default :
  935. DBG_ASSERT( !"Invalid action code in state table!" );
  936. State = 4;
  937. *pszDest++ = '\0';
  938. break;
  939. }
  940. State &= ~ACTION_MASK;
  941. if ( !fDBCS )
  942. {
  943. if ( IsDBCSLeadByte( ch ) )
  944. {
  945. fDBCS = TRUE;
  946. }
  947. }
  948. else
  949. {
  950. fDBCS = FALSE;
  951. }
  952. }
  953. //
  954. // Remove any trailing slash.
  955. //
  956. pszDest -= 2;
  957. if( ( strlen( pszPath ) > 3 ) && ( *pszDest == '/' ) )
  958. {
  959. *pszDest = '\0';
  960. }
  961. //
  962. // If the final path elements consists solely of dots, remove them.
  963. //
  964. while( strlen( pszPath ) > 3 )
  965. {
  966. pszDest = strrchr( pszPath, '/');
  967. DBG_ASSERT( pszDest != NULL );
  968. pszHead = pszDest;
  969. pszDest++;
  970. while( ch = *pszDest++ )
  971. {
  972. if( ch != '.' )
  973. {
  974. break;
  975. }
  976. }
  977. if( ch == '\0' )
  978. {
  979. //
  980. // this is probably dead code left over from
  981. // when we used physical paths.
  982. //
  983. if( pszHead == ( pszPath + 2 ) )
  984. {
  985. pszHead++;
  986. }
  987. // end of dead code
  988. //
  989. // Don't remove the first '/'
  990. //
  991. if ( pszHead == pszPath )
  992. {
  993. pszHead++;
  994. }
  995. *pszHead = '\0';
  996. }
  997. else
  998. {
  999. break;
  1000. }
  1001. }
  1002. } // VirtualpSanitizePath
  1003.