Windows NT 4.0 source code leak
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.

1109 lines
27 KiB

4 years ago
  1. /*****************************************************************************
  2. *
  3. * FSWRITE.CPP
  4. *
  5. * Copyright (C) Microsoft Corporation 1990-1995.
  6. * All Rights reserved.
  7. *
  8. ******************************************************************************
  9. *
  10. * Module Intent
  11. *
  12. * File System Manager functions for writing.
  13. *
  14. *****************************************************************************/
  15. #include "stdafx.h"
  16. #include "fspriv.h"
  17. #ifdef _DEBUG
  18. #include <io.h> // for tell()
  19. #endif
  20. #ifdef _DEBUG
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. /*****************************************************************************
  25. * *
  26. * Defines *
  27. * *
  28. *****************************************************************************/
  29. #define CDROM_ALIGN 2048 // CDROM alignment block size
  30. /*****************************************************************************
  31. * *
  32. * Prototypes *
  33. * *
  34. *****************************************************************************/
  35. static int FASTCALL LcbCdRomPadding(int lif, int lcbOffset);
  36. static BOOL STDCALL FCloseOrFlushDirtyQrwfo(QRWFO qrwfo, BOOL fClose, int lcbOffset);
  37. static int STDCALL LcbGetFree(QFSHR qfshr, QRWFO qrwfo, int lcbOffset);
  38. /***************************************************************************\
  39. * *
  40. * Private Functions *
  41. * *
  42. \***************************************************************************/
  43. /***************************************************************************\
  44. *
  45. * Function: FFreeBlock( qfshr, lifThis )
  46. *
  47. * Purpose: Free the block beginning at lifThis.
  48. *
  49. * Method: Insert into free list in sorted order. If this block is
  50. * adjacent to another free block, merge them. If this block
  51. * is at the end of the file, truncate the file.
  52. *
  53. * ASSUMES
  54. *
  55. * returns: fTruth or FALSEhood of success
  56. * If FALSE is returned, free list could be corrupted
  57. *
  58. * args IN: qfshr - pointer to file system header dealie
  59. * - qfshr->fid is valid (plunged)
  60. * lifThis - valid index of nonfree block
  61. *
  62. * PROMISES
  63. *
  64. * args OUT: qfshr - free list has a new entry, fModified flag set
  65. *
  66. * NOTES This function got hacked when I realized that I'd have to
  67. * deal with the case where the block being freed is
  68. * adjacent to EOF and to the last block on the free list.
  69. * Probably this could be done more clearly and cleanly.
  70. *
  71. \***************************************************************************/
  72. BOOL STDCALL FFreeBlock(QFSHR qfshr, LONG lifThis)
  73. {
  74. FID fid;
  75. FH fh;
  76. FREE_HEADER free_header_PrevPrev, free_header_Prev;
  77. FREE_HEADER free_header_This, free_header_Next;
  78. int lifPrevPrev = lifNil, lifPrev, lifNext;
  79. BOOL fWritePrev, fAtEof;
  80. if (lifThis < sizeof(FSH) ||
  81. lifThis + (int) sizeof(FH) > (int) qfshr->fsh.lifEof ||
  82. LSeekFid(qfshr->fid, lifThis, SEEK_SET) != lifThis ||
  83. LcbReadFid(qfshr->fid, &fh, sizeof(FH)) != sizeof(FH)) {
  84. if (SetFSErrorRc(RcGetIOError()) == RC_Success)
  85. SetFSErrorRc(RC_Invalid);
  86. return FALSE;
  87. }
  88. SetFSErrorRc(RC_Failure);
  89. fid = qfshr->fid;
  90. free_header_This.lcbBlock = fh.lcbBlock;
  91. fAtEof = (lifThis + free_header_This.lcbBlock == qfshr->fsh.lifEof);
  92. lifPrev = qfshr->fsh.lifFirstFree;
  93. if (lifPrev == lifNil || lifThis < lifPrev) {
  94. free_header_This.lifNext = lifNext = lifPrev;
  95. qfshr->fsh.lifFirstFree = lifThis;
  96. fWritePrev = FALSE;
  97. }
  98. else {
  99. if (LSeekFid(fid, lifPrev, SEEK_SET) != lifPrev
  100. ||
  101. LcbReadFid(fid, &free_header_Prev, sizeof(FREE_HEADER))
  102. !=
  103. sizeof(FREE_HEADER)) {
  104. if (RcGetIOError() != RC_Success)
  105. SetFSErrorRc(RcGetIOError());
  106. return FALSE;
  107. }
  108. lifNext = free_header_Prev.lifNext;
  109. for (;;) {
  110. ASSERT(lifPrev < lifThis);
  111. ASSERT(free_header_Prev.lifNext == lifNext);
  112. if (lifNext == lifNil || lifThis < lifNext) {
  113. free_header_This.lifNext = lifNext;
  114. free_header_Prev.lifNext = lifThis;
  115. fWritePrev = TRUE;
  116. break;
  117. }
  118. if (fAtEof) {
  119. lifPrevPrev = lifPrev;
  120. free_header_PrevPrev = free_header_Prev;
  121. }
  122. lifPrev = lifNext;
  123. if (LSeekFid(fid, lifPrev, SEEK_SET) != lifNext
  124. ||
  125. LcbReadFid(fid, &free_header_Prev, sizeof(FREE_HEADER))
  126. !=
  127. sizeof(FREE_HEADER)) {
  128. if (RcGetIOError() != RC_Success)
  129. SetFSErrorRc(RcGetIOError());
  130. return FALSE;
  131. }
  132. lifNext = free_header_Prev.lifNext;
  133. }
  134. ASSERT(lifNext == lifNil || lifNext > lifThis);
  135. ASSERT(lifPrev != lifNil);
  136. ASSERT(lifPrev < lifThis);
  137. ASSERT(fWritePrev);
  138. if (lifPrev + free_header_Prev.lcbBlock == lifThis) {
  139. free_header_This.lcbBlock += free_header_Prev.lcbBlock;
  140. lifThis = lifPrev;
  141. if (fAtEof) {
  142. free_header_Prev = free_header_PrevPrev;
  143. lifPrev = lifPrevPrev;
  144. fWritePrev = (lifPrev != lifNil);
  145. }
  146. else {
  147. fWritePrev = FALSE;
  148. }
  149. }
  150. }
  151. if (fAtEof) {
  152. if (SetFSErrorRc(RcChSizeFid(fid, lifThis)) != RC_Success)
  153. return FALSE;
  154. qfshr->fsh.lifEof = lifThis;
  155. ASSERT((lifPrev == lifNil) != fWritePrev);
  156. if (lifPrev == lifNil)
  157. qfshr->fsh.lifFirstFree = lifNil;
  158. else
  159. free_header_Prev.lifNext = lifNil;
  160. }
  161. else {
  162. if (lifThis + free_header_This.lcbBlock == lifNext) {
  163. if (LSeekFid(fid, lifNext, SEEK_SET) != lifNext
  164. ||
  165. LcbReadFid(fid, &free_header_Next, sizeof(FREE_HEADER))
  166. !=
  167. sizeof(FREE_HEADER)) {
  168. if (RcGetIOError() != RC_Success)
  169. SetFSErrorRc(RcGetIOError());
  170. return FALSE;
  171. }
  172. free_header_This.lcbBlock += free_header_Next.lcbBlock;
  173. free_header_This.lifNext = free_header_Next.lifNext;
  174. }
  175. if (LSeekFid(fid, lifThis, SEEK_SET) != lifThis
  176. ||
  177. LcbWriteFid(fid, &free_header_This, sizeof(FREE_HEADER))
  178. !=
  179. sizeof(FREE_HEADER)) {
  180. if (RcGetIOError() != RC_Success)
  181. SetFSErrorRc(RcGetIOError());
  182. return FALSE;
  183. }
  184. }
  185. if (fWritePrev) {
  186. if (LSeekFid(fid, lifPrev, SEEK_SET) != lifPrev
  187. ||
  188. LcbWriteFid(fid, &free_header_Prev, sizeof(FREE_HEADER))
  189. !=
  190. sizeof(FREE_HEADER)) {
  191. if (RcGetIOError() != RC_Success)
  192. SetFSErrorRc(RcGetIOError());
  193. return FALSE;
  194. }
  195. }
  196. qfshr->fsh.bFlags |= FS_DIRTY;
  197. SetFSErrorRc(RC_Success);
  198. return TRUE;
  199. }
  200. /***************************************************************************\
  201. *
  202. * Function: LcbGetFree( qfshr, qrwfo, lcbOffset )
  203. *
  204. * Purpose: Get an adequate block from the free list.
  205. *
  206. * ASSUMES
  207. *
  208. * args IN: qfshr - pointer to file system header
  209. * qrwfo->lcbFile - (+header) is size we need to allocate
  210. *
  211. * PROMISES
  212. *
  213. * returns: actual size of allocated block
  214. *
  215. * globals OUT: rcFSError
  216. *
  217. * args OUT: qfshr->lifFirstFree - a block is allocated from free list
  218. * ->fModified - set to TRUE
  219. *
  220. * qrwfo->lifBase - set to new block index
  221. *
  222. * ALSO: if FS_CDROM is set for the file, we align it on a
  223. * (MOD 2K) - 9 byte boundary so the |Topic file blocks are all
  224. * properly aligned.
  225. * +++
  226. *
  227. * Method: First Fit:
  228. * Walk the free list. If a block is found that is
  229. * big enough, remove it from the free list, plug its
  230. * lif into qrwfo, and return the actual size.
  231. * If a block isn't found, grow the file and make
  232. * a new block starting at the old EOF.
  233. *
  234. * Bugs: The leftover part of the block isn't left on
  235. * the free list. This is the whole point of First Fit.
  236. * If aligning for CDROM, the padding part is not
  237. * added to the free list. This breaks the FS abstraction
  238. * and creates a permanent hole in the FS. This is evil.
  239. *
  240. \***************************************************************************/
  241. static int STDCALL LcbGetFree(QFSHR qfshr, QRWFO qrwfo, int lcbOffset)
  242. {
  243. FID fid;
  244. FREE_HEADER free_header_this, free_header_prev;
  245. int lifPrev, lifThis;
  246. int lcb = qrwfo->lcbFile + sizeof(FH);
  247. int lcbPadding; // padding for file alignment
  248. fid = qfshr->fid;
  249. ASSERT(fid != HFILE_ERROR);
  250. lifPrev = lifNil;
  251. lifThis = qfshr->fsh.lifFirstFree;
  252. for (;;) {
  253. if (lifThis == lifNil) {
  254. // end of free list
  255. // cut the new block
  256. lifThis = qfshr->fsh.lifEof;
  257. if (qrwfo->bFlags & FS_CDROM)
  258. lcbPadding = LcbCdRomPadding(lifThis, lcbOffset);
  259. else
  260. lcbPadding = 0;
  261. if (lifThis != LSeekFid(fid, lifThis, SEEK_SET))
  262. goto error_return;
  263. // Put the hole in the free list someday?-Tom
  264. lifThis += lcbPadding;
  265. qfshr->fsh.lifEof += lcb + lcbPadding;
  266. if (RcChSizeFid(fid, qfshr->fsh.lifEof) != RC_Success) {
  267. qfshr->fsh.lifEof -= lcb + lcbPadding;
  268. goto error_return;
  269. }
  270. break;
  271. }
  272. else {
  273. // get header of this free block
  274. if (LSeekFid(fid, lifThis, SEEK_SET) != lifThis)
  275. goto error_return;
  276. if (LcbReadFid(fid, &free_header_this, (int) sizeof(FREE_HEADER))
  277. !=
  278. (int) sizeof(FREE_HEADER)) {
  279. goto error_return;
  280. }
  281. // Check for alignment requirements:
  282. if (qrwfo->bFlags & FS_CDROM)
  283. lcbPadding = LcbCdRomPadding(lifThis, lcbOffset);
  284. else
  285. lcbPadding = 0;
  286. if (lcb + lcbPadding <= free_header_this.lcbBlock) {
  287. // this block is big enough: take it
  288. /*
  289. * Someday break the free block into two (or three): one to
  290. * return and the leftover piece(s) left in the free list.
  291. */
  292. lcb = free_header_this.lcbBlock;
  293. if (lifThis == qfshr->fsh.lifFirstFree) {
  294. // lFirst = this->next;
  295. qfshr->fsh.lifFirstFree = free_header_this.lifNext;
  296. }
  297. else {
  298. // prev->next = this->next;
  299. if (LSeekFid(fid, lifPrev, SEEK_SET) != lifPrev)
  300. goto error_return;
  301. if (LcbReadFid(fid, &free_header_prev, (int) sizeof(FREE_HEADER))
  302. !=
  303. (int) sizeof(FREE_HEADER))
  304. goto error_return;
  305. free_header_prev.lifNext = free_header_this.lifNext;
  306. if (LSeekFid(fid, lifPrev, SEEK_SET) != lifPrev)
  307. goto error_return;
  308. if (LcbWriteFid(fid, &free_header_prev, (int) sizeof(FREE_HEADER))
  309. !=
  310. (int) sizeof(FREE_HEADER))
  311. goto error_return;
  312. }
  313. // add padding at beginning:
  314. lifThis += lcbPadding;
  315. break;
  316. }
  317. else {
  318. lifPrev = lifThis;
  319. lifThis = free_header_this.lifNext;
  320. }
  321. }
  322. }
  323. qfshr->fsh.bFlags |= FS_DIRTY;
  324. qrwfo->lifBase = lifThis;
  325. SetFSErrorRc(RC_Success);
  326. return lcb;
  327. error_return:
  328. if (RcGetIOError() == RC_Success)
  329. SetFSErrorRc(RC_Invalid);
  330. else
  331. SetFSErrorRc(RcGetIOError());
  332. return (int) -1;
  333. }
  334. /***************************************************************************\
  335. *
  336. - Function: LcbCdRomPadding( lif, lcbOffset )
  337. -
  338. * Purpose: Returns the number of bytes that must be added to
  339. * lif to align the file on a CD block boundary.
  340. * This is also the amount of the free block that
  341. * should stay a free block.
  342. * This allows block structured data to be retrieved
  343. * more quickly from a CDROM drive.
  344. *
  345. * ASSUMES
  346. * args IN: lif - offset in bytes of the beginning of the
  347. * free block (relative to top of FS)
  348. * lcbOffset - align the file this many bytes from the
  349. * beginning of the file
  350. *
  351. * PROMISES
  352. * returns: the number of bytes that must be added to lif in
  353. * order to align the file
  354. *
  355. * Notes: Currently doesn't ensure that the padding is big enough
  356. * to hold a FREE_HEADER so it can be added to the free list.
  357. * That's what the "#if 0"'ed code does.
  358. * +++
  359. *
  360. * Notes: Should CDROM_ALIGN be a parameter?
  361. *
  362. \***************************************************************************/
  363. static int FASTCALL LcbCdRomPadding( int lif, int lcbOffset )
  364. {
  365. return CDROM_ALIGN - (lif + sizeof(FH) + lcbOffset) % CDROM_ALIGN;
  366. #if 0
  367. /* Guarantee the padding block can be added to the free list. */
  368. /* #if'ed out because we don't add it to the free list today. */
  369. int lT = lif + sizeof( FREE_HEADER ) + sizeof( FH ) + lcbOffset;
  370. return sizeof( FREE_HEADER ) + CDROM_ALIGN - lT % CDROM_ALIGN;
  371. #endif // 0
  372. }
  373. /***************************************************************************\
  374. *
  375. * Function: RcCopyToTempFile( qrwfo )
  376. *
  377. * Purpose: Copy a FS file into a temp file. This is done when the
  378. * file needs to be modified.
  379. *
  380. * ASSUMES
  381. *
  382. * args IN: qrwfo - specs the open file
  383. *
  384. * PROMISES
  385. *
  386. * returns: RC_Success; RC_Failure
  387. *
  388. \***************************************************************************/
  389. RC_TYPE STDCALL RcCopyToTempFile(QRWFO qrwfo)
  390. {
  391. QFSHR qfshr = (QFSHR) qrwfo->hfs;
  392. ConfirmOrDie(!(qfshr->fsh.bFlags & FS_OPEN_READ_ONLY));
  393. if (!FPlungeQfshr(qfshr))
  394. return rcFSError;
  395. qrwfo->bFlags |= FS_DIRTY;
  396. qrwfo->pTmpFile = new CTmpFile();
  397. // copy from FS file into temp file
  398. if (LSeekFid(qfshr->fid, qrwfo->lifBase, SEEK_SET) != qrwfo->lifBase)
  399. return SetFSErrorRc(RcGetIOError());
  400. ASSERT(qrwfo->pTmpFile);
  401. if (qrwfo->pTmpFile->copyfromfile(qfshr->fid, qrwfo->lcbFile + sizeof(FH))
  402. != RC_Success) {
  403. delete qrwfo->pTmpFile;
  404. qrwfo->pTmpFile = NULL;
  405. }
  406. return rcFSError;
  407. }
  408. /***************************************************************************
  409. FUNCTION: LcbWriteHf
  410. PURPOSE: Writes buffer to a temporary file
  411. PARAMETERS:
  412. hf
  413. qb
  414. lcb
  415. RETURNS:
  416. Returns ONLY if no errors occured, and what we wrote is the same
  417. amount as what was requested
  418. COMMENTS:
  419. MODIFICATION DATES:
  420. 01-Mar-1994 [ralphw]
  421. ***************************************************************************/
  422. void STDCALL LcbWriteHf(HF hf, void* pvData, int lcb)
  423. {
  424. int lcbTotalWrote;
  425. ASSERT(hf != NULL);
  426. QRWFO qrwfo = (QRWFO) hf;
  427. ASSERT(!(qrwfo->bFlags & FS_OPEN_READ_ONLY));
  428. if (!(qrwfo->bFlags & FS_DIRTY)) {
  429. ASSERT(!qrwfo->pTmpFile);
  430. // make sure we have a temp file version
  431. // FS permission is checked in RcCopyToTempFile()
  432. if (RcCopyToTempFile(qrwfo) != RC_Success) {
  433. FatalError:
  434. OutErrorRc(rcFSError);
  435. HardExit(); // doesn't return
  436. }
  437. }
  438. // position file pointer in temp file
  439. if (qrwfo->pTmpFile->seek(sizeof(FH) + qrwfo->lifCurrent,
  440. SEEK_SET) != (int) (sizeof(FH) + qrwfo->lifCurrent)) {
  441. ForceFSError();
  442. goto FatalError;
  443. }
  444. lcbTotalWrote = qrwfo->pTmpFile->write(pvData, lcb);
  445. if (lcbTotalWrote != lcb) {
  446. errHpj.ep = epNoFile;
  447. if (!qrwfo->pTmpFile->pszFileName)
  448. OOM();
  449. // This shouldn't return
  450. VReportError(HCERR_CANNOT_WRITE, &errHpj, qrwfo->pTmpFile->pszFileName);
  451. }
  452. // update file pointer and file size
  453. if (lcbTotalWrote > 0) {
  454. qrwfo->lifCurrent += lcbTotalWrote;
  455. if (qrwfo->lifCurrent > qrwfo->lcbFile)
  456. qrwfo->lcbFile = qrwfo->lifCurrent;
  457. }
  458. }
  459. /***************************************************************************
  460. FUNCTION: LcbWriteIntAsShort
  461. PURPOSE: Write a 32-bit integer as a 16-bit value
  462. PARAMETERS:
  463. hf
  464. val
  465. RETURNS:
  466. COMMENTS:
  467. MODIFICATION DATES:
  468. 19-Aug-1994 [ralphw]
  469. ***************************************************************************/
  470. void STDCALL LcbWriteIntAsShort(HF hf, int val)
  471. {
  472. INT16 iWriteVal = (INT16) val;
  473. LcbWriteHf(hf, &iWriteVal, sizeof(INT16));
  474. }
  475. /***************************************************************************\
  476. *
  477. * Function: FCloseOrFlushDirtyQrwfo( qrwfo, fClose, lcbOffset )
  478. *
  479. * Purpose: flush a dirty open file in a file system
  480. *
  481. * Method: If the file is dirty, copy the scratch file back to the
  482. * FS file. If this is the first time the file has been closed,
  483. * we enter the name into the FS directory. If this file is
  484. * the FS directory, store the location in a special place
  485. * instead. Write the FS directory and header to disk.
  486. * Do other various hairy stuff.
  487. *
  488. * ASSUMES
  489. *
  490. * args IN: qrwfo -
  491. * fClose - TRUE to close file; FALSE to just flush
  492. * lcbOffset - offset for CDROM alignment
  493. *
  494. * PROMISES
  495. *
  496. * returns: TRUE on success; FALSE for error
  497. *
  498. * failure: If we fail on a flush, the handle is still valid
  499. * but hosed? yes. This is so further file ops will fail but
  500. * not assert.
  501. *
  502. \***************************************************************************/
  503. static BOOL STDCALL FCloseOrFlushDirtyQrwfo(QRWFO qrwfo, BOOL fClose,
  504. int lcbOffset)
  505. {
  506. QFSHR qfshr;
  507. FILE_REC fr;
  508. FH fh;
  509. RC_TYPE rc = RC_Success;
  510. BOOL fChangeFH = FALSE;
  511. qfshr = (QFSHR) qrwfo->hfs;
  512. ASSERT(!(qfshr->fsh.bFlags & FS_OPEN_READ_ONLY));
  513. if (qfshr->fid == HFILE_ERROR && !FPlungeQfshr(qfshr))
  514. return FALSE;
  515. ASSERT(qrwfo->pTmpFile);
  516. // read the file header
  517. if (qrwfo->pTmpFile->seek(0, SEEK_SET) != 0 ||
  518. qrwfo->pTmpFile->read(&fh, sizeof(FH)) != sizeof(FH)) {
  519. ForceFSError();
  520. return FALSE;
  521. }
  522. if (qrwfo->bFlags & FS_NO_BLOCK) {
  523. if ((fh.lcbBlock = LcbGetFree(qfshr, qrwfo, lcbOffset)) == (int) -1)
  524. return FALSE;
  525. fChangeFH = TRUE;
  526. // store file offset for new file
  527. if (qrwfo->bFlags & FS_IS_DIRECTORY)
  528. qfshr->fsh.lifDirectory = qrwfo->lifBase;
  529. else {
  530. fr.lifBase = qrwfo->lifBase;
  531. rc = RcInsertHbt(qfshr->qbthr, (KEY) qrwfo->rgchKey, &fr);
  532. if (rc == RC_Exists) {
  533. // oops there is one (someone else created the same file)
  534. // lookup directory entry and free old block
  535. if (RcLookupByKey(qfshr->qbthr, (KEY) qrwfo->rgchKey, NULL, &fr) !=
  536. RC_Success) {
  537. SetFSErrorRc(RcGetBtreeError());
  538. goto error_freeblock;
  539. }
  540. if (!FFreeBlock(qfshr, fr.lifBase))
  541. goto error_freeblock;
  542. // update directory record to show new block
  543. fr.lifBase = qrwfo->lifBase;
  544. if (RcUpdateHbt(qfshr->qbthr, (KEY) qrwfo->rgchKey, &fr) != RC_Success) {
  545. SetFSErrorRc(RcGetBtreeError());
  546. goto error_freeblock;
  547. }
  548. }
  549. else if (rc != RC_Success) {
  550. // some other btree error: handle it
  551. SetFSErrorRc(rc);
  552. goto error_freeblock;
  553. }
  554. }
  555. }
  556. else {
  557. // see if file still fits in old block
  558. if (qrwfo->lcbFile + (LONG) sizeof(FH) > fh.lcbBlock) {
  559. // file doesn't fit in old block: get a new one, free old one
  560. int lif = qrwfo->lifBase;
  561. if ((fh.lcbBlock = LcbGetFree(qfshr, qrwfo, lcbOffset)) ==
  562. (int) -1)
  563. return FALSE;
  564. if (!FFreeBlock(qfshr, lif))
  565. goto error_freeblock;
  566. fChangeFH = TRUE;
  567. // update directory record to show new block
  568. if (qrwfo->bFlags & FS_IS_DIRECTORY)
  569. qfshr->fsh.lifDirectory = qrwfo->lifBase;
  570. else {
  571. fr.lifBase = qrwfo->lifBase;
  572. rc = RcUpdateHbt(qfshr->qbthr, (KEY) qrwfo->rgchKey, &fr);
  573. if (rc != RC_Success) {
  574. SetFSErrorRc(rc);
  575. return FALSE;
  576. }
  577. }
  578. }
  579. }
  580. // put new header in temp file if block or file size changed
  581. if (fh.lcbFile != qrwfo->lcbFile) {
  582. fChangeFH = TRUE;
  583. fh.lcbFile = qrwfo->lcbFile;
  584. }
  585. if (fChangeFH) {
  586. if (qrwfo->pTmpFile->seek(0, SEEK_SET) != 0 ||
  587. qrwfo->pTmpFile->write(&fh, sizeof(FH)) != sizeof(FH)) {
  588. ForceFSError();
  589. goto error_deletekey;
  590. }
  591. }
  592. qrwfo->pTmpFile->seek(0, SEEK_SET);
  593. // copy tmp file back to file system file
  594. if (LSeekFid(qfshr->fid, qrwfo->lifBase, SEEK_SET) != qrwfo->lifBase) {
  595. ForceFSError();
  596. goto error_deletekey;
  597. }
  598. if (qrwfo->pTmpFile->copytofile(qfshr->fid, qrwfo->lcbFile + sizeof(FH))
  599. != RC_Success)
  600. goto error_deletekey;
  601. delete qrwfo->pTmpFile;
  602. qrwfo->pTmpFile = NULL;
  603. /*
  604. * Don't flush the FS if this file is the FS directory, because if it
  605. * is, we're already closing or flushing it!
  606. */
  607. if (!(qrwfo->bFlags & FS_IS_DIRECTORY))
  608. RcCloseOrFlushHfs(qrwfo->hfs, FALSE);
  609. return TRUE; // errors here are already cleaned up
  610. error_deletekey:
  611. if (!(qrwfo->bFlags & FS_IS_DIRECTORY) && fClose)
  612. RcDeleteHbt(qfshr->qbthr, (KEY) qrwfo->rgchKey);
  613. error_freeblock:
  614. if (fClose) {
  615. rc = rcFSError;
  616. FFreeBlock(qfshr, qrwfo->lifBase); // we don't want to lose an error
  617. SetFSErrorRc(rc);
  618. }
  619. delete qrwfo->pTmpFile;
  620. qrwfo->pTmpFile = NULL;
  621. return FALSE;
  622. }
  623. /***************************************************************************\
  624. *
  625. - Function: RcCloseOrFlushHfs( hfs, fClose )
  626. -
  627. * Purpose: Close or sync the header and directory of an open file system.
  628. *
  629. * ASSUMES
  630. * args IN: hfs - handle to an open file system
  631. * fClose - fTrue to close the file system;
  632. * FALSE to write through
  633. * PROMISES
  634. * returns: standard return code
  635. * globals OUT:rcFSError
  636. *
  637. * Note: If closing the FS, all FS files must have been closed or
  638. * changes made will be lost.
  639. *
  640. \***************************************************************************/
  641. RC_TYPE STDCALL RcCloseOrFlushHfs(HFS hfs, BOOL fClose)
  642. {
  643. ASSERT(hfs != NULL);
  644. QFSHR qfshr = (QFSHR) hfs;
  645. /*
  646. * We don't call FPlungeQfshr() here because if we need to open the
  647. * file, it will be opened in the btree call. In fixing a bug (help3.5
  648. * 164) I added this FPlungeQfshr() call, but I now think the bug was due
  649. * to inherent FS limitations in dealing with a FS open multiple times.
  650. */
  651. if (SetFSErrorRc(RcCloseOrFlushHbt(qfshr->qbthr, fClose))
  652. != RC_Success) {
  653. ASSERT(qfshr->fid != HFILE_ERROR); // see comment above
  654. // out of disk space, internal error, or out of file handles.
  655. if (RC_NoFileHandles != RcGetBtreeError()) {
  656. // attempt to invalidate FS by clobbering magic number
  657. LSeekFid(qfshr->fid, 0L, SEEK_SET);
  658. qfshr->fsh.wMagic = 0;
  659. LcbWriteFid(qfshr->fid, &qfshr->fsh, sizeof(FSH));
  660. }
  661. }
  662. else {
  663. if (qfshr->fsh.bFlags & FS_DIRTY) {
  664. ASSERT(qfshr->fid != HFILE_ERROR); // see comment above
  665. ASSERT(!(qfshr->fsh.bFlags & (FS_OPEN_READ_ONLY | FS_READ_ONLY)));
  666. // save the directory flag, clear before writing, and restore
  667. BOOL fIsDir = qfshr->fsh.bFlags & FS_IS_DIRECTORY;
  668. qfshr->fsh.bFlags &= ~(FS_DIRTY | FS_IS_DIRECTORY);
  669. // write out file system header
  670. if (LSeekFid(qfshr->fid, 0, SEEK_SET) != 0 ||
  671. LcbWriteFid(qfshr->fid, &qfshr->fsh, sizeof(FSH)) != sizeof(FSH))
  672. ForceFSError();
  673. qfshr->fsh.bFlags |= fIsDir;
  674. /*
  675. * REVIEW: should we keep track of open files and make sure they
  676. * are all closed, or close them here?
  677. */
  678. }
  679. }
  680. if (fClose) {
  681. if (qfshr->fid != HFILE_ERROR) {
  682. if (hfs == hfsOut)
  683. cbHlpFile = GetFileSize((HANDLE) qfshr->fid, NULL);
  684. RcCloseFid(qfshr->fid);
  685. if (rcFSError == RC_Success)
  686. rcFSError = RcGetIOError();
  687. }
  688. DisposeFm(qfshr->fm);
  689. lcFree(hfs);
  690. }
  691. return rcFSError;
  692. }
  693. /***************************************************************************\
  694. *
  695. - Function: HfOpenHfs( hfs, sz, bFlags )
  696. -
  697. * Purpose: open a file in a file system
  698. *
  699. * ASSUMES
  700. * args IN: hfs - handle to file system
  701. * sz - name (key) of file to open
  702. * bFlags - FS_OPEN_READ_ONLY, FS_IS_DIRECTORY, or combination
  703. *
  704. * PROMISES
  705. * returns: handle to open file or NULL on failure
  706. * +++
  707. *
  708. * Notes: strlen( NULL ) and strcpy( s, NULL ) don't work as I'd like.
  709. *
  710. \***************************************************************************/
  711. QRWFO STDCALL HfOpenHfs(QFSHR qfshr, PCSTR pszKey, DWORD bFlags)
  712. {
  713. FILE_REC fr;
  714. FH fh;
  715. if ((qfshr->fsh.bFlags & FS_OPEN_READ_ONLY) && !(bFlags & FS_OPEN_READ_ONLY)) {
  716. SetFSErrorRc(RC_NoPermission);
  717. return NULL;
  718. }
  719. if (!FPlungeQfshr(qfshr))
  720. return NULL;
  721. if (bFlags & FS_IS_DIRECTORY) {
  722. // check if directory is already open
  723. if (qfshr->fsh.bFlags & FS_IS_DIRECTORY) {
  724. SetFSErrorRc(RC_BadArg);
  725. return NULL;
  726. }
  727. qfshr->fsh.bFlags |= FS_IS_DIRECTORY;
  728. fr.lifBase = qfshr->fsh.lifDirectory;
  729. }
  730. else if (SetFSErrorRc(RcLookupByKey(qfshr->qbthr, (KEY) pszKey, NULL, &fr))
  731. != RC_Success)
  732. return NULL;
  733. // sanity check
  734. if (fr.lifBase < sizeof(FSH) || fr.lifBase > qfshr->fsh.lifEof) {
  735. SetFSErrorRc(RC_Invalid);
  736. return NULL;
  737. }
  738. // read the file header
  739. if (LSeekFid(qfshr->fid, fr.lifBase, SEEK_SET) != fr.lifBase
  740. ||
  741. LcbReadFid(qfshr->fid, &fh, sizeof(FH)) != sizeof(FH)) {
  742. ForceFSError();
  743. return NULL;
  744. }
  745. // sanity check
  746. if (fh.lcbFile < 0 || fh.lcbFile + (int) sizeof(FH) > (int) fh.lcbBlock ||
  747. fr.lifBase + fh.lcbBlock > qfshr->fsh.lifEof) {
  748. SetFSErrorRc(RC_Invalid);
  749. return NULL;
  750. }
  751. // check mode against fh.bPerms for legality
  752. if ((fh.bPerms & FS_READ_ONLY) && !(bFlags & FS_OPEN_READ_ONLY)) {
  753. SetFSErrorRc(RC_NoPermission);
  754. return NULL;
  755. }
  756. // build file struct
  757. QRWFO qrwfo = (QRWFO) lcCalloc(sizeof(RWFO) +
  758. (pszKey == NULL ? 0 : strlen(pszKey)));
  759. qrwfo->hfs = (HFS) qfshr;
  760. qrwfo->lifBase = fr.lifBase;
  761. qrwfo->lifCurrent = 0;
  762. qrwfo->lcbFile = fh.lcbFile;
  763. qrwfo->bFlags = bFlags & (~(FS_DIRTY | FS_NO_BLOCK));
  764. if (pszKey != NULL)
  765. strcpy(qrwfo->rgchKey, pszKey);
  766. SetFSErrorRc(RC_Success);
  767. return qrwfo;
  768. }
  769. /***************************************************************************\
  770. *
  771. - Function: RcCloseOrFlushHf( hf, fClose, lcbOffset )
  772. -
  773. * Purpose: close or flush an open file in a file system
  774. *
  775. * ASSUMES
  776. * args IN: hf - file handle
  777. * fClose - fTrue to close; FALSE to just flush
  778. * lcbOffset - offset for CDROM alignment (align at this
  779. * offset into the file) (only used if
  780. * FS_CDROM flag is set for the file)
  781. *
  782. * PROMISES
  783. * returns: RC_Success on successful closing
  784. * failure: If we fail on a flush, the handle is still valid
  785. * but hosed? yes. This is so further file ops will fail but
  786. * not assert.
  787. * +++
  788. *
  789. * Method: If the file is dirty, copy the scratch file back to the
  790. * FS file. If this is the first time the file has been closed,
  791. * we enter the name into the FS directory. If this file is
  792. * the FS directory, store the location in a special place
  793. * instead. Write the FS directory and header to disk.
  794. * Do other various hairy stuff.
  795. *
  796. \***************************************************************************/
  797. RC_TYPE STDCALL RcCloseOrFlushHf(HF hf, BOOL fClose, int lcbOffset)
  798. {
  799. QRWFO qrwfo;
  800. BOOL fError = FALSE;
  801. ASSERT(hf);
  802. qrwfo = (QRWFO) hf;
  803. if (qrwfo->bFlags & FS_DIRTY)
  804. fError = !FCloseOrFlushDirtyQrwfo(qrwfo, fClose, lcbOffset);
  805. else
  806. SetFSErrorRc(RC_Success);
  807. if (fClose || fError)
  808. lcFree(hf);
  809. else
  810. qrwfo->bFlags &= ~(FS_NO_BLOCK | FS_DIRTY);
  811. return rcFSError;
  812. }
  813. /***************************************************************************\
  814. *
  815. - Function: HfsOpenFm( fm, bFlags )
  816. -
  817. * Purpose: Open a file system
  818. *
  819. * ASSUMES
  820. * args IN: fm - descriptor of file system to open
  821. * bFlags - FS_OPEN_READ_ONLY or FS_OPEN_READ_WRITE
  822. *
  823. * PROMISES
  824. * returns: handle to file system if opened OK, else NULL
  825. *
  826. * Bugs: don't have mode now (a file system is opened r/w)
  827. *
  828. \***************************************************************************/
  829. HFS STDCALL HfsOpenFm(FM fm, BYTE bFlags)
  830. {
  831. QFSHR qfshr;
  832. int lcb;
  833. // make header
  834. qfshr = (QFSHR) lcCalloc(sizeof(FSHR));
  835. qfshr->fm = FmCopyFm(fm);
  836. qfshr->fid = HFILE_ERROR;
  837. if (qfshr->fm == fmNil) {
  838. SetFSErrorRc(RcGetIOError());
  839. goto error_return;
  840. }
  841. qfshr->fsh.bFlags = (BYTE) ((bFlags & FS_OPEN_READ_ONLY)
  842. ? FS_OPEN_READ_ONLY : FS_OPEN_READ_WRITE);
  843. if (!FPlungeQfshr(qfshr))
  844. goto error_return;
  845. lcb = LcbReadFid(qfshr->fid, &qfshr->fsh, sizeof(FSH));
  846. // restore the FS_OPEN_READ_ONLY bit
  847. if (bFlags & FS_OPEN_READ_ONLY)
  848. qfshr->fsh.bFlags |= FS_OPEN_READ_ONLY;
  849. if (lcb != sizeof(FSH) ||
  850. qfshr->fsh.wMagic != wFileSysMagic ||
  851. qfshr->fsh.lifDirectory < sizeof(FSH) ||
  852. qfshr->fsh.lifDirectory > qfshr->fsh.lifEof ||
  853. (qfshr->fsh.lifFirstFree < sizeof(FSH) &&
  854. qfshr->fsh.lifFirstFree != lifNil) ||
  855. qfshr->fsh.lifFirstFree > qfshr->fsh.lifEof) {
  856. if (RcGetIOError() == RC_Success)
  857. SetFSErrorRc(RC_Invalid);
  858. else
  859. SetFSErrorRc(RcGetIOError());
  860. goto error_return;
  861. }
  862. if (qfshr->fsh.bVersion != FILESYSVERSION) {
  863. SetFSErrorRc(RC_BadVersion);
  864. goto error_return;
  865. }
  866. // open btree directory
  867. qfshr->qbthr = HbtOpenBtreeSz(NULL,
  868. (HFS) qfshr,
  869. (BYTE) (qfshr->fsh.bFlags | FS_IS_DIRECTORY));
  870. if (!qfshr->qbthr) {
  871. SetFSErrorRc(RcGetBtreeError());
  872. goto error_return;
  873. }
  874. SetFSErrorRc(RC_Success);
  875. return (HFS) qfshr;
  876. error_return:
  877. if (qfshr->fid != HFILE_ERROR) {
  878. RcCloseFid(qfshr->fid);
  879. qfshr->fid = HFILE_ERROR;
  880. }
  881. if (FValidFm(qfshr->fm))
  882. DisposeFm(qfshr->fm);
  883. lcFree(qfshr);
  884. return NULL;
  885. }