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.

1129 lines
27 KiB

  1. //+--------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 1996.
  5. //
  6. // File: dir.cxx
  7. //
  8. // Contents: Directory Functions
  9. //
  10. //---------------------------------------------------------------
  11. #include "msfhead.cxx"
  12. #include "h/dirfunc.hxx"
  13. #include "mread.hxx"
  14. #define DEB_DIR (DEB_ITRACE | 0x00040000)
  15. //+-------------------------------------------------------------------------
  16. //
  17. // Member: CMStream::KillStream, public
  18. //
  19. // Synopsis: Eliminate a given chain
  20. //
  21. // Arguments: [sectStart] -- Beginning of chain to eliminate
  22. //
  23. // Returns: S_OK if call completed OK.
  24. //
  25. // Algorithm:
  26. //
  27. // Notes:
  28. //
  29. //--------------------------------------------------------------------------
  30. inline SCODE CMStream::KillStream(SECT sectStart, ULONG ulSize)
  31. {
  32. CFat *pfat;
  33. pfat = (ulSize < MINISTREAMSIZE) ?&_fatMini: &_fat;
  34. return pfat->SetChainLength(sectStart, 0);
  35. }
  36. //+-------------------------------------------------------------------------
  37. //
  38. // Member: GetNewDirEntryArray, public
  39. //
  40. // Synopsis: Obtain a new array of CDirEntry(s)
  41. //
  42. // Arguments: [cbSector] -- size of a sector
  43. //
  44. // Returns: New CDirEntry array
  45. //
  46. // Algorithm: calculates the number of entries need for the array and
  47. // allocate it
  48. //
  49. // Notes:
  50. //
  51. //--------------------------------------------------------------------------
  52. inline CDirEntry* GetNewDirEntryArray(USHORT cbSector)
  53. {
  54. CDirEntry *temp;
  55. DIROFFSET cdeEntries = (USHORT) (cbSector / sizeof(CDirEntry));
  56. temp = new CDirEntry[cdeEntries];
  57. return temp;
  58. }
  59. //+-------------------------------------------------------------------------
  60. //
  61. // Member: CDirEntry::Init, public
  62. //
  63. // Synopsis: Initializes member data
  64. //
  65. // Arguments: [mse] -- multi-stream entry flags for the directory entry
  66. //
  67. // Returns: void
  68. //
  69. // Algorithm:
  70. //
  71. // Notes:
  72. //
  73. //--------------------------------------------------------------------------
  74. inline void CDirEntry::Init(MSENTRYFLAGS mse)
  75. {
  76. msfAssert(sizeof(CDirEntry) == DIRENTRYSIZE);
  77. msfAssert(mse <= 0xff);
  78. _mse = (BYTE) mse;
  79. _bflags = 0;
  80. _dfn.Set((WORD)0, (BYTE *)NULL);
  81. _sidLeftSib = _sidRightSib = _sidChild = NOSTREAM;
  82. if (STORAGELIKE(_mse))
  83. {
  84. _clsId = IID_NULL;
  85. _dwUserFlags = 0;
  86. }
  87. if (STREAMLIKE(_mse))
  88. {
  89. _sectStart = ENDOFCHAIN;
  90. _ulSize = 0;
  91. }
  92. }
  93. //+-------------------------------------------------------------------------
  94. //
  95. // Method: CDirEntry::CDirEntry, public
  96. //
  97. // Synopsis: Constructor for CDirEntry class
  98. //
  99. // Effects:
  100. //
  101. // Notes:
  102. //
  103. //--------------------------------------------------------------------------
  104. CDirEntry::CDirEntry()
  105. {
  106. msfAssert(sizeof(CDirEntry) == DIRENTRYSIZE);
  107. Init(STGTY_INVALID);
  108. }
  109. //+-------------------------------------------------------------------------
  110. //
  111. // Method: CDirSect::Init, public
  112. //
  113. // Synopsis: Initializer for directory sectors
  114. //
  115. // Arguments: [cdeEntries] -- Number of DirEntries in the sector
  116. //
  117. // Returns: S_OK if call completed OK.
  118. //
  119. // Notes:
  120. //
  121. //--------------------------------------------------------------------------
  122. SCODE CDirSect::Init(USHORT cbSector)
  123. {
  124. msfDebugOut((DEB_DIR,"Allocating sector with size %u\n",cbSector));
  125. DIROFFSET cdeEntries = (USHORT) (cbSector / sizeof(CDirEntry));
  126. for (ULONG i = 0; i < cdeEntries; i++)
  127. {
  128. _adeEntry[i].Init(STGTY_INVALID);
  129. }
  130. return S_OK;
  131. }
  132. //+-------------------------------------------------------------------------
  133. //
  134. // Method: CDirectory::CDirectory
  135. //
  136. // Synopsis: Default constructor
  137. //
  138. // Notes:
  139. //
  140. //--------------------------------------------------------------------------
  141. CDirectory::CDirectory(USHORT cbSector)
  142. : _pmsParent(NULL),
  143. _dv(cbSector)
  144. {
  145. _cdsTable = _cdeEntries = 0;
  146. _sidFirstFree = 0;
  147. }
  148. //+---------------------------------------------------------------------------
  149. //
  150. // Member: CDirectory::Empty, public
  151. //
  152. // Synopsis: Empty all the control structures of this instance
  153. //
  154. // Arguments: None.
  155. //
  156. // Returns: void.
  157. //
  158. //----------------------------------------------------------------------------
  159. void CDirectory::Empty(void)
  160. {
  161. _dv.Empty();
  162. _pmsParent = NULL;
  163. _cdsTable = 0;
  164. _cdeEntries = 0;
  165. _sidFirstFree = 0;
  166. }
  167. //+-------------------------------------------------------------------------
  168. //
  169. // Member: CDirectory::GetFree, public
  170. //
  171. // Synposis: Locates a free directory entry
  172. //
  173. // Arguments: [psid] Stream ID of free directory entry.
  174. //
  175. // Returns: S_OK if successful
  176. //
  177. // Algorithm: Do a linear search of all available directories.
  178. // If no free spot is found, resize the directory and
  179. // perform the search again.
  180. //
  181. // Notes:
  182. //
  183. //---------------------------------------------------------------------------
  184. SCODE CDirectory::GetFree(SID* psid)
  185. {
  186. msfDebugOut((DEB_DIR,"In CDirectory::GetFree()\n"));
  187. SCODE sc = S_OK;
  188. SID sidRet = NOSTREAM;
  189. CDirSect * pds;
  190. DIRINDEX ipdsStart;
  191. DIROFFSET ideStart;
  192. SidToPair(_sidFirstFree, &ipdsStart, &ideStart);
  193. while (TRUE)
  194. {
  195. DIRINDEX ipds;
  196. for (ipds = ipdsStart; ipds < _cdsTable; ipds++)
  197. {
  198. msfChk(_dv.GetTable(ipds, FB_NONE, &pds));
  199. for (DIROFFSET ide = ideStart; ide < _cdeEntries; ide++)
  200. {
  201. if (pds->GetEntry(ide)->IsFree())
  202. {
  203. msfDebugOut((DEB_ITRACE,"GetFree found sid %lu\n",
  204. PairToSid(ipds,ide)));
  205. *psid = PairToSid(ipds, ide);
  206. _sidFirstFree = *psid + 1;
  207. _dv.ReleaseTable(ipds);
  208. return S_OK;
  209. }
  210. }
  211. _dv.ReleaseTable(ipds);
  212. ideStart = 0;
  213. }
  214. ipdsStart = ipds;
  215. msfChk(Resize(_cdsTable+1));
  216. }
  217. Err:
  218. return sc;
  219. }
  220. //+-------------------------------------------------------------------------
  221. //
  222. // Member: CDirectory::FindGreaterEntry
  223. //
  224. // Synopsis: finds next entry (for iteration)
  225. //
  226. // Arguments: [sidStart] -- child sid to start looking
  227. // [pdfn] -- previous entry name
  228. // [psidResult] -- place holder for returned sid
  229. //
  230. // Requires: sidStart != NOSTREAM
  231. //
  232. // Returns: S_OK, STG_E_NOMOREFILES, or other error
  233. //
  234. // Modifies: psidResult
  235. //
  236. // Algorithm: Iterate by returning the sid that has a name larger
  237. // than the given name.
  238. //
  239. // Notes: This method is called recursively
  240. //
  241. //--------------------------------------------------------------------------
  242. SCODE CDirectory::FindGreaterEntry(SID sidStart, CDfName const *pdfn,
  243. SID *psidResult)
  244. {
  245. SCODE sc;
  246. CDirEntry *pde;
  247. msfAssert(sidStart != NOSTREAM);
  248. msfChk(GetDirEntry(sidStart, FB_NONE, &pde));
  249. int iCmp;
  250. iCmp = NameCompare(pdfn, pde->GetName());
  251. if (iCmp < 0)
  252. {
  253. // Since the last name returned is less than this name,
  254. // the sid to return must either be to our left or this sid
  255. SID sidLeft = pde->GetLeftSib();
  256. // We can't hold onto sidStart as we recurse, (because we'll ask for
  257. // a page each time we recurse)
  258. ReleaseEntry(sidStart);
  259. if (sidLeft == sidStart)
  260. {
  261. //Corrupt docfile - return error.
  262. return STG_E_DOCFILECORRUPT;
  263. }
  264. if ((sidLeft == NOSTREAM) ||
  265. (sc = FindGreaterEntry(sidLeft, pdfn, psidResult)) == STG_E_NOMOREFILES)
  266. {
  267. // There was no left child with a name greater than pdfn, so
  268. // we return ourself
  269. *psidResult = sidStart;
  270. sc = S_OK;
  271. }
  272. }
  273. else
  274. {
  275. // The last name returned is greater than this one, so we've already
  276. // returned this sidStart. Look in the right subtree.
  277. SID sidRight = pde->GetRightSib();
  278. // We can't hold onto sidStart as we recurse, (because we'll ask for
  279. // a page each time we recurse)
  280. ReleaseEntry(sidStart);
  281. if (sidRight == sidStart)
  282. {
  283. //Corrupt docfile - return error.
  284. return STG_E_DOCFILECORRUPT;
  285. }
  286. if (sidRight == NOSTREAM)
  287. sc = STG_E_NOMOREFILES;
  288. else
  289. sc = FindGreaterEntry(sidRight, pdfn, psidResult);
  290. }
  291. Err:
  292. return(sc);
  293. }
  294. //+-------------------------------------------------------------------------
  295. //
  296. // Method: CDirectory::SetStart, public
  297. //
  298. // Synopsis: Set starting sector for a dir entry
  299. //
  300. // Arguments: [sid] -- SID of entry to be modified
  301. // [sect] -- New starting sector for entry
  302. //
  303. // Returns: SID of modified entry
  304. //
  305. // Notes:
  306. //
  307. //--------------------------------------------------------------------------
  308. SCODE CDirectory::SetStart(const SID sid, const SECT sect)
  309. {
  310. SCODE sc;
  311. CDirEntry *pde;
  312. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  313. pde->SetStart(sect);
  314. ReleaseEntry(sid);
  315. Err:
  316. return sc;
  317. }
  318. //+-------------------------------------------------------------------------
  319. //
  320. // Member: CDirectory::SetChild, public
  321. //
  322. // Synposis: Set the child SID of an entry
  323. //
  324. // Effects: Modifies a single directory entry. Causes a one sector
  325. // stream write.
  326. //
  327. // Arguments: [sid] -- Stream ID of entry to be set
  328. // [sidChild] -- SID of first child of this stream
  329. //
  330. // Returns: SID of modified entry
  331. //
  332. // Algorithm: Change child field on entry, then write to stream.
  333. //
  334. //---------------------------------------------------------------------------
  335. SCODE CDirectory::SetChild(const SID sid, const SID sidChild)
  336. {
  337. SCODE sc;
  338. CDirEntry *pde;
  339. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  340. pde->SetChild(sidChild);
  341. ReleaseEntry(sid);
  342. Err:
  343. return sc;
  344. }
  345. //+-------------------------------------------------------------------------
  346. //
  347. // Member: CDirectory::SetSize, public
  348. //
  349. // Synposis: Set the size of an entry
  350. //
  351. // Effects: Modifies a single directory entry. Causes a one sector
  352. // stream write.
  353. //
  354. // Arguments: [sid] -- Stream ID of entry to be set
  355. // [cbSize] -- Size
  356. //
  357. // Returns: SID of modified entry
  358. //
  359. // Algorithm: Change size field on entry, then write to stream.
  360. //
  361. //---------------------------------------------------------------------------
  362. SCODE CDirectory::SetSize(const SID sid, const ULONG cbSize)
  363. {
  364. SCODE sc;
  365. CDirEntry *pde;
  366. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  367. pde->SetSize(cbSize);
  368. ReleaseEntry(sid);
  369. Err:
  370. return sc;
  371. }
  372. //+-------------------------------------------------------------------------
  373. //
  374. // Member: CDirectory::SetTime, public
  375. //
  376. // Synposis: Set the time of an entry
  377. //
  378. // Effects: Modifies a single directory entry. Causes a one sector
  379. // stream write.
  380. //
  381. // Arguments: [sid] -- Stream ID of entry to be set
  382. // [tt] - WT_*
  383. // [nt] - New time
  384. //
  385. // Returns: SID of modified entry
  386. //
  387. // Algorithm: Change time field on entry, then write to stream.
  388. //
  389. //---------------------------------------------------------------------------
  390. SCODE CDirectory::SetTime(const SID sid, WHICHTIME tt, TIME_T nt)
  391. {
  392. SCODE sc;
  393. CDirEntry *pde;
  394. // We don't support ACCESS times, so just ignore sets
  395. if (tt == WT_ACCESS)
  396. return S_OK;
  397. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  398. pde->SetTime(tt, nt);
  399. ReleaseEntry(sid);
  400. Err:
  401. return sc;
  402. }
  403. //+-------------------------------------------------------------------------
  404. //
  405. // Member: CDirectory::SetFlags, public
  406. //
  407. // Synposis: Set the flags of an entry
  408. //
  409. // Effects: Modifies a single directory entry. Causes a one sector
  410. // stream write.
  411. //
  412. // Arguments: [sid] -- Stream ID of entry to be set
  413. // [mse] - New flags
  414. //
  415. // Returns: Status code
  416. //
  417. // Algorithm: Change Luid field on entry, then write to stream.
  418. //
  419. //---------------------------------------------------------------------------
  420. SCODE CDirectory::SetFlags(const SID sid, const MSENTRYFLAGS mse)
  421. {
  422. SCODE sc;
  423. CDirEntry *pde;
  424. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  425. pde->SetFlags(mse);
  426. ReleaseEntry(sid);
  427. Err:
  428. return sc;
  429. }
  430. //+-------------------------------------------------------------------------
  431. //
  432. // Member: CDirectory::SetClassId, public
  433. //
  434. // Synposis: Set the class ID of an entry
  435. //
  436. // Effects: Modifies a single directory entry. Causes a one sector
  437. // stream write.
  438. //
  439. // Arguments: [sid] -- Stream ID of entry to be set
  440. // [cls] - Class ID
  441. //
  442. // Returns: Appropriate status code
  443. //
  444. //---------------------------------------------------------------------------
  445. SCODE CDirectory::SetClassId(const SID sid, const GUID cls)
  446. {
  447. SCODE sc;
  448. CDirEntry *pde;
  449. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  450. pde->SetClassId(cls);
  451. ReleaseEntry(sid);
  452. Err:
  453. return sc;
  454. }
  455. //+-------------------------------------------------------------------------
  456. //
  457. // Member: CDirectory::SetUserFlags, public
  458. //
  459. // Synposis: Set the user flags of an entry
  460. //
  461. // Effects: Modifies a single directory entry. Causes a one sector
  462. // stream write.
  463. //
  464. // Arguments: [sid] -- Stream ID of entry to be set
  465. // [dwUserFlags] - Flags
  466. // [dwMask] - Mask
  467. //
  468. // Returns: Appropriate status code
  469. //
  470. //---------------------------------------------------------------------------
  471. SCODE CDirectory::SetUserFlags(SID const sid,
  472. DWORD dwUserFlags,
  473. DWORD dwMask)
  474. {
  475. SCODE sc;
  476. CDirEntry *pde;
  477. msfChk(GetDirEntry(sid, FB_DIRTY, &pde));
  478. pde->SetUserFlags(dwUserFlags, dwMask);
  479. ReleaseEntry(sid);
  480. Err:
  481. return sc;
  482. }
  483. //+-------------------------------------------------------------------------
  484. //
  485. // Member: CDirectory::resize, private
  486. //
  487. // Synposis: Resize a directory.
  488. //
  489. // Effects: Reallocates space for directory table, copying over
  490. // old pointers as necessary. Any new tables needed are
  491. // created here.
  492. //
  493. // Arguments: [uNewsize] -- New size for Directory
  494. //
  495. // Returns: void
  496. //
  497. // Algorithm: Allocate a new array of pointers of the necessary size.
  498. // Then, copy over all pointers from old array and allocate
  499. // new CDirSects for all new tables.
  500. //
  501. // Notes:
  502. //
  503. //---------------------------------------------------------------------------
  504. SCODE CDirectory::Resize(DIRINDEX uNewsize)
  505. {
  506. msfDebugOut((DEB_DIR,"In CDirectory::Resize(%i)\n",uNewsize));
  507. SCODE sc;
  508. if (uNewsize == _cdsTable) return S_OK;
  509. SECT sect;
  510. //GetESect call will make sure we have enough Fat space.
  511. msfChk(_pmsParent->GetESect(SIDDIR, uNewsize - 1, &sect));
  512. msfChk(_pmsParent->SetSize());
  513. msfChk(_dv.Resize(uNewsize));
  514. ULONG ipds;
  515. for (ipds = _cdsTable; ipds < uNewsize; ipds++)
  516. {
  517. CDirSect *pds;
  518. msfChk(_dv.GetTable(ipds, FB_NEW, &pds));
  519. SECT sect;
  520. msfChk(_pmsParent->GetESect(SIDDIR, ipds, &sect));
  521. _dv.SetSect(ipds, sect);
  522. _dv.ReleaseTable(ipds);
  523. }
  524. _cdsTable = uNewsize;
  525. Err:
  526. return sc;
  527. }
  528. //+-------------------------------------------------------------------------
  529. //
  530. // Member: CDirectory::Init, public
  531. //
  532. // Synposis: Sets up a Directory instance and reads in all tables
  533. // from the stream
  534. //
  535. // Arguments: [cSect] -- Number of sectors in directory
  536. //
  537. // Returns: S_OK if call completed OK.
  538. // STG_E_READFAULT if not enough bytes were read for
  539. // a DirSector
  540. // Error code of read if read returned an error.
  541. //
  542. // Algorithm: Create array to hold appropriate number of tables.
  543. // Read in each table from disk.
  544. //
  545. //---------------------------------------------------------------------------
  546. SCODE CDirectory::Init(
  547. CMStream *pmsParent,
  548. DIRINDEX cSect)
  549. {
  550. msfDebugOut((DEB_DIR,"In CDirectory::Init(%lu)\n",cSect));
  551. SCODE sc;
  552. _pmsParent = pmsParent;
  553. _cdeEntries = (DIROFFSET)
  554. ( _pmsParent->GetSectorSize() / sizeof(CDirEntry));
  555. msfChk(_dv.Init(_pmsParent, cSect));
  556. _cdsTable = cSect;
  557. msfDebugOut((DEB_DIR,"Out CDirectory::Init()\n"));
  558. Err:
  559. return sc;
  560. }
  561. //+-------------------------------------------------------------------------
  562. //
  563. // Member: CDirectory::InitNew, public
  564. //
  565. // Synposis: Sets up a new Directory instance for a new Mstream
  566. //
  567. // Arguments: None.
  568. //
  569. // Returns: S_OK if call completed OK.
  570. // STG_E_WRITEFAULT if not enough bytes were written.
  571. // Error code of write if write failed.
  572. //
  573. // Algorithm: Write initial DirSector to disk.
  574. //
  575. // Notes:
  576. //
  577. //---------------------------------------------------------------------------
  578. SCODE CDirectory::InitNew(CMStream *pmsParent)
  579. {
  580. SCODE sc;
  581. #ifndef _MSC_VER
  582. #define ROOT_ENTRY "Root Entry"
  583. WCHAR *wcsRoot = new WCHAR[sizeof(ROOT_ENTRY)+1];
  584. _tbstowcs(wcsRoot, ROOT_ENTRY, sizeof(ROOT_ENTRY));
  585. CDfName const dfnRoot(wcsRoot);
  586. #else
  587. CDfName const dfnRoot(L"Root Entry");
  588. #endif
  589. msfDebugOut((DEB_DIR,"In CDirectory::setupnew()\n"));
  590. _pmsParent = pmsParent;
  591. _cdeEntries = (DIROFFSET)
  592. (_pmsParent->GetSectorSize() / sizeof(CDirEntry));
  593. msfChk(_dv.Init(_pmsParent, 1));
  594. CDirSect *pds;
  595. msfChk(_dv.GetTable(0, FB_NEW, &pds));
  596. _dv.SetSect(0, _pmsParent->GetHeader()->GetDirStart());
  597. _dv.ReleaseTable(0);
  598. _cdsTable = 1;
  599. SID sidRoot;
  600. msfChk(GetFree(&sidRoot));
  601. CDirEntry *pdeTemp;
  602. msfChk(GetDirEntry(sidRoot, FB_DIRTY, &pdeTemp));
  603. pdeTemp->Init(STGTY_ROOT);
  604. msfAssert(sidRoot == SIDROOT);
  605. pdeTemp->SetName(&dfnRoot);
  606. ReleaseEntry(sidRoot);
  607. msfDebugOut((DEB_DIR,"Exiting CDirectory::setupnew()\n"));
  608. Err:
  609. return sc;
  610. }
  611. //+-------------------------------------------------------------------------
  612. //
  613. // Method: CDirectory::CreateEntry, public
  614. //
  615. // Synopsis: Create a new directory entry
  616. //
  617. // Arguments: [sidParent] -- SID of parent for new entry
  618. // [pwcsName] -- Name of new entry
  619. // [mef] -- Flags for new entry
  620. // [psidNew] -- Return location for new SID
  621. //
  622. // Returns: S_OK if call completed OK.
  623. //
  624. // Algorithm: Search directory for entry of the same name. If one
  625. // is found, return STG_E_FILEALREADYEXISTS.
  626. // If not, create a new entry and return its SID.
  627. //
  628. //--------------------------------------------------------------------------
  629. SCODE CDirectory::CreateEntry(
  630. SID sidParent,
  631. CDfName const *pdfn,
  632. MSENTRYFLAGS mef,
  633. SID *psidNew)
  634. {
  635. SCODE sc;
  636. SID sidNew;
  637. CDirEntry *pdeNew;
  638. SEntryBuffer eb;
  639. sc = IsEntry(sidParent, pdfn, &eb);
  640. if (sc != STG_E_FILENOTFOUND)
  641. {
  642. if (SUCCEEDED(sc))
  643. sc = STG_E_FILEALREADYEXISTS;
  644. return(sc);
  645. }
  646. // Allocate new sid
  647. msfChk(GetFree(psidNew));
  648. sidNew = *psidNew;
  649. msfChk(GetDirEntry(sidNew, FB_DIRTY, &pdeNew));
  650. // Initialize new entry
  651. pdeNew->Init(mef);
  652. TIME_T timetemp;
  653. DfGetTOD(&timetemp);
  654. pdeNew->SetTime(WT_CREATION, timetemp);
  655. pdeNew->SetTime(WT_MODIFICATION, timetemp);
  656. pdeNew->SetName(pdfn);
  657. ReleaseEntry(sidNew);
  658. // Insert new entry into the tree
  659. msfChk(InsertEntry(sidParent, sidNew, pdfn));
  660. Err:
  661. return sc;
  662. }
  663. //+-------------------------------------------------------------------------
  664. //
  665. // Member: CDirectory::RenameEntry, public
  666. //
  667. // Synopsis: Rename an entry
  668. //
  669. // Arguments: [sidParent] -- Sid of parent of entry to be renamed
  670. // [pwcsName] -- Old name of entry to be renamed
  671. // [pwcsName] -- New name
  672. //
  673. // Returns: S_OK if call completed OK.
  674. //
  675. // Algorithm: Remove old entry
  676. // Rename entry
  677. // Insert as new entry
  678. //
  679. // Notes:
  680. //
  681. //--------------------------------------------------------------------------
  682. SCODE CDirectory::RenameEntry(SID const sidParent,
  683. CDfName const *pdfn,
  684. CDfName const *pdfnNew)
  685. {
  686. // Make sure new name doesn't already exist
  687. SCODE sc;
  688. SEntryBuffer eb;
  689. sc = IsEntry(sidParent, pdfnNew, &eb);
  690. if (sc != STG_E_FILENOTFOUND)
  691. {
  692. if (SUCCEEDED(sc))
  693. {
  694. // Entry did exist - fail this call
  695. sc = STG_E_ACCESSDENIED;
  696. }
  697. return(sc);
  698. }
  699. // We can't just rename in place (because the tree is ordered)
  700. CDirEntry *pdeRename;
  701. SEntryBuffer ebRename;
  702. msfChk(FindEntry(sidParent, pdfn, DEOP_REMOVE, &ebRename));
  703. sc = GetDirEntry(ebRename.sid, FB_DIRTY, &pdeRename);
  704. msfAssert(SUCCEEDED(sc) && aMsg("Could get dir entry to rename"));
  705. msfChk(sc);
  706. pdeRename->SetName(pdfnNew);
  707. ReleaseEntry(ebRename.sid);
  708. // If this InsertEntry fails, we've potentially lost the entry. This
  709. // doesn't matter becase:
  710. // a) The only way we could fail is if we couldn't read or write
  711. // the disk (hard error)
  712. // b) No one's going to call RenameEntry anyways
  713. // c) If we're transacted, the whole operation is made robust by
  714. // CopyOnWrite mode
  715. // d) If we're direct, we already know we can fail in ways that leave
  716. // the Docfile corrupt.
  717. sc = InsertEntry(sidParent, ebRename.sid, pdfnNew);
  718. msfAssert(SUCCEEDED(sc) && aMsg("Couldn't reinsert renamed dir entry"));
  719. msfChk(sc);
  720. Err:
  721. return sc;
  722. }
  723. //+-------------------------------------------------------------------------
  724. //
  725. // Member: CDirectory::DestroyAllChildren
  726. //
  727. // Synopsis: destroy all child entries
  728. //
  729. // Effects: destroys child tree
  730. //
  731. // Arguments: [sidParent] -- storage entry
  732. //
  733. // Returns: S_OK or error code
  734. //
  735. // Modifies: sidParent's entry
  736. //
  737. // Algorithm: While there's a child
  738. // destroy it
  739. //
  740. // Notes: We may want to consider a more efficient implementation
  741. //
  742. //--------------------------------------------------------------------------
  743. SCODE CDirectory::DestroyAllChildren(
  744. SID const sidParent)
  745. {
  746. SCODE sc;
  747. CDirEntry *pdeParent, *pdeChild;
  748. SID sidChild;
  749. CDfName dfnChild;
  750. for (;;)
  751. {
  752. CDfName dfnChild;
  753. msfChk(GetDirEntry(sidParent, FB_NONE, &pdeParent));
  754. sidChild = pdeParent->GetChild();
  755. ReleaseEntry(sidParent);
  756. if (sidChild == NOSTREAM)
  757. break;
  758. msfChk(GetDirEntry(sidChild, FB_NONE, &pdeChild));
  759. dfnChild.Set(pdeChild->GetName());
  760. ReleaseEntry(sidChild);
  761. msfChk(DestroyChild(sidParent, &dfnChild));
  762. }
  763. Err:
  764. return(sc);
  765. }
  766. //+-------------------------------------------------------------------------
  767. //
  768. // Member: CDirectory::DestroyChild
  769. //
  770. // Synopsis: destroy a named child
  771. //
  772. // Effects: destroys named child's entry
  773. //
  774. // Arguments: [sidParent] -- storage entry
  775. // [pdfn] -- child name
  776. //
  777. // Returns: S_OK, STG_E_FILENOTFOUND, or other error code
  778. //
  779. // Modifies: child's entry
  780. //
  781. // Algorithm: Find and remove child
  782. // Free child entry
  783. //
  784. //--------------------------------------------------------------------------
  785. SCODE CDirectory::DestroyChild(
  786. SID const sidParent,
  787. CDfName const *pdfn)
  788. {
  789. SCODE sc;
  790. SEntryBuffer ebChild;
  791. msfAssert(pdfn != NULL);
  792. // remove the entry from the tree
  793. msfChk(FindEntry(sidParent, pdfn, DEOP_REMOVE, &ebChild));
  794. msfAssert(ebChild.sid != NOSTREAM);
  795. // Before we remove this entry, we need to destroy it (including all
  796. // its children). Note that we can't hold onto the entry because it
  797. // might have children which get destroyed, which have children which
  798. // get destroyed, etc.
  799. if (STORAGELIKE(ebChild.dwType))
  800. {
  801. msfChk(DestroyAllChildren(ebChild.sid));
  802. }
  803. CDirEntry *pdeChild;
  804. msfChk(GetDirEntry(ebChild.sid, FB_DIRTY, &pdeChild));
  805. if (STREAMLIKE(ebChild.dwType))
  806. {
  807. // Deallocate any used streams
  808. msfChkTo(EH_Rel, _pmsParent->KillStream(pdeChild->GetStart(),
  809. pdeChild->GetSize()));
  810. }
  811. pdeChild->SetFlags(STGTY_INVALID);
  812. if (ebChild.sid < _sidFirstFree)
  813. {
  814. _sidFirstFree = ebChild.sid;
  815. }
  816. EH_Rel:
  817. ReleaseEntry(ebChild.sid);
  818. Err:
  819. return(sc);
  820. }
  821. //+-------------------------------------------------------------------------
  822. //
  823. // Method: CDirectory::StatEntry
  824. //
  825. // Synopsis: For a given handle, fill in the Multistream specific
  826. // information of a STATSTG.
  827. //
  828. // Arguments: [sid] -- SID that information is requested on.
  829. // [pName] -- name of the next key to fill in
  830. // [pstatstg] -- STATSTG to fill in.
  831. //
  832. // Returns: S_OK
  833. //
  834. // Modifies: [pName] -- if it is not null
  835. // [pstatstg] -- if it is not null
  836. //
  837. // Algorithm: Fill in time information and size and then return
  838. //
  839. //--------------------------------------------------------------------------
  840. SCODE CDirectory::StatEntry(SID const sid,
  841. CDfName *pName,
  842. STATSTGW *pstatstg)
  843. {
  844. SCODE sc;
  845. CDirEntry *pde;
  846. msfChk(GetDirEntry(sid, FB_NONE, &pde));
  847. if (pName)
  848. {
  849. pName->Set(pde->GetName());
  850. }
  851. if (pstatstg)
  852. {
  853. pstatstg->type = pde->GetFlags();
  854. // allocate memory
  855. msfChk(DfAllocWCS((WCHAR *)pde->GetName()->GetBuffer(),
  856. &pstatstg->pwcsName));
  857. wcscpy(pstatstg->pwcsName, (WCHAR*) pde->GetName()->GetBuffer());
  858. pstatstg->mtime = pde->GetTime(WT_MODIFICATION);
  859. pstatstg->ctime = pde->GetTime(WT_CREATION);
  860. pstatstg->atime = pstatstg->mtime; // don't currently keep access times
  861. // Don't use REAL_STGTY here because we want this
  862. // to function properly for both property and non-property builds
  863. if ((pstatstg->type & STGTY_REAL) == STGTY_STORAGE)
  864. {
  865. ULISet32(pstatstg->cbSize, 0);
  866. pstatstg->clsid = pde->GetClassId();
  867. pstatstg->grfStateBits = pde->GetUserFlags();
  868. }
  869. else
  870. {
  871. ULISet32(pstatstg->cbSize, pde->GetSize());
  872. pstatstg->clsid = CLSID_NULL;
  873. pstatstg->grfStateBits = 0;
  874. }
  875. }
  876. Err:
  877. ReleaseEntry(sid);
  878. return sc;
  879. }
  880. //+-------------------------------------------------------------------------
  881. //
  882. // Member: CDirectory::GetDirEntry
  883. //
  884. // Synopsis: Get a directory entry with given permissions
  885. //
  886. // Arguments: [sid] -- SID
  887. // [dwFlags] -- permissions
  888. // [ppde] -- placeholder for directory entry
  889. //
  890. // Returns: S_OK if call completed OK.
  891. //
  892. // Algorithm:
  893. //
  894. //--------------------------------------------------------------------------
  895. SCODE CDirectory::GetDirEntry(
  896. const SID sid,
  897. const DWORD dwFlags,
  898. CDirEntry **ppde)
  899. {
  900. SCODE sc;
  901. CDirSect *pds;
  902. DIRINDEX id = sid / _cdeEntries;
  903. msfChk(_dv.GetTable(id, dwFlags, &pds));
  904. *ppde = pds->GetEntry((DIROFFSET)(sid % _cdeEntries));
  905. Err:
  906. return sc;
  907. }
  908. //+-------------------------------------------------------------------------
  909. //
  910. // Member: CDirectory::ReleaseEntry
  911. //
  912. // Synopsis: Releases a directory entry
  913. //
  914. // Arguments: [sid] -- SID
  915. //
  916. // Returns: S_OK if call completed OK.
  917. //
  918. // Algorithm:
  919. //
  920. //--------------------------------------------------------------------------
  921. void CDirectory::ReleaseEntry(SID sid)
  922. {
  923. _dv.ReleaseTable(sid / _cdeEntries);
  924. }