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, &sect));
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, &sect));
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