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.

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