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