cache.c 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. cache.c
  9. Abstract:
  10. This module implements a block level cache for the setup application.
  11. Author:
  12. Evan Green 9-Oct-2014
  13. Environment:
  14. User Mode
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <assert.h>
  20. #include <errno.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/types.h>
  25. #include <time.h>
  26. #include "setup.h"
  27. //
  28. // ---------------------------------------------------------------- Definitions
  29. //
  30. #define SETUP_CACHE_BLOCK_SIZE (64 * 1024)
  31. #define SETUP_CACHE_BLOCK_SHIFT 16
  32. #define SETUP_MAX_CACHE_SIZE (1024 * 1024 * 10)
  33. //
  34. // Define some max offset to ever expect to write to in order to debug stray
  35. // writes.
  36. //
  37. #define SETUP_CACHE_MAX_OFFSET (16ULL * _1TB)
  38. //
  39. // ------------------------------------------------------ Data Type Definitions
  40. //
  41. /*++
  42. Structure Description:
  43. This structure describes a handle to an I/O object in the setup app.
  44. Members:
  45. Handle - Stores the device handle.
  46. Cached - Stores a boolean indicating whether caching is enabled or not.
  47. NextOffset - Stores the file position where the next I/O occurs.
  48. NextOsOffset - Stores the file position according to the OS handle.
  49. Cache - Stores the tree of cached data.
  50. CacheSize - Stores the number of entries in the cache.
  51. MaxCacheSize - Stores the maximum size the cache can grow, in entries.
  52. CheckBlock - Stores a single block's worth of buffer space, used to verify
  53. writes.
  54. --*/
  55. typedef struct _SETUP_HANDLE {
  56. PVOID Handle;
  57. BOOL Cached;
  58. LONGLONG NextOffset;
  59. LONGLONG NextOsOffset;
  60. RED_BLACK_TREE Cache;
  61. LIST_ENTRY CacheLruList;
  62. UINTN CacheSize;
  63. UINTN MaxCacheSize;
  64. PVOID CheckBlock;
  65. } SETUP_HANDLE, *PSETUP_HANDLE;
  66. /*++
  67. Structure Description:
  68. This structure describes a cached block.
  69. Members:
  70. Offset - Stores the offset of the data.
  71. TreeNode - Stores the red black tree node structure.
  72. ListEntry - Stores the LRU list structure.
  73. Dirty - Stores a boolean indicating if this entry is dirty.
  74. Data - Stores a pointer to the data.
  75. --*/
  76. typedef struct _SETUP_CACHE_DATA {
  77. ULONGLONG Offset;
  78. RED_BLACK_TREE_NODE TreeNode;
  79. LIST_ENTRY ListEntry;
  80. BOOL Dirty;
  81. PVOID Data;
  82. } SETUP_CACHE_DATA, *PSETUP_CACHE_DATA;
  83. //
  84. // ----------------------------------------------- Internal Function Prototypes
  85. //
  86. VOID
  87. SetupDestroyCache (
  88. PSETUP_HANDLE Handle
  89. );
  90. VOID
  91. SetupAddCacheData (
  92. PSETUP_HANDLE Handle,
  93. ULONGLONG Offset,
  94. PVOID Buffer,
  95. BOOL Dirty
  96. );
  97. INT
  98. SetupCleanCacheData (
  99. PSETUP_HANDLE Handle,
  100. PSETUP_CACHE_DATA Data
  101. );
  102. PSETUP_CACHE_DATA
  103. SetupGetCacheData (
  104. PSETUP_HANDLE Handle,
  105. ULONGLONG Offset
  106. );
  107. COMPARISON_RESULT
  108. SetupCompareCacheTreeNodes (
  109. PRED_BLACK_TREE Tree,
  110. PRED_BLACK_TREE_NODE FirstNode,
  111. PRED_BLACK_TREE_NODE SecondNode
  112. );
  113. //
  114. // -------------------------------------------------------------------- Globals
  115. //
  116. //
  117. // Set this boolean to verify all writes.
  118. //
  119. BOOL SetupVerifyWrites = TRUE;
  120. //
  121. // ------------------------------------------------------------------ Functions
  122. //
  123. PVOID
  124. SetupOpenDestination (
  125. PSETUP_DESTINATION Destination,
  126. INT Flags,
  127. INT CreatePermissions
  128. )
  129. /*++
  130. Routine Description:
  131. This routine opens a handle to a given destination.
  132. Arguments:
  133. Destination - Supplies a pointer to the destination to open.
  134. Flags - Supplies open flags. See O_* definitions.
  135. CreatePermissions - Supplies optional create permissions.
  136. Return Value:
  137. Returns a pointer to an opaque context on success.
  138. NULL on failure.
  139. --*/
  140. {
  141. UINTN AllocationSize;
  142. PSETUP_HANDLE IoHandle;
  143. AllocationSize = sizeof(SETUP_HANDLE) + SETUP_CACHE_BLOCK_SIZE;
  144. IoHandle = malloc(AllocationSize);
  145. if (IoHandle == NULL) {
  146. errno = ENOMEM;
  147. return NULL;
  148. }
  149. memset(IoHandle, 0, sizeof(SETUP_HANDLE));
  150. IoHandle->CheckBlock = (PVOID)(IoHandle + 1);
  151. if ((Destination->Type == SetupDestinationDisk) ||
  152. (Destination->Type == SetupDestinationPartition) ||
  153. (Destination->Type == SetupDestinationImage)) {
  154. IoHandle->Cached = TRUE;
  155. RtlRedBlackTreeInitialize(&(IoHandle->Cache),
  156. 0,
  157. SetupCompareCacheTreeNodes);
  158. INITIALIZE_LIST_HEAD(&(IoHandle->CacheLruList));
  159. IoHandle->MaxCacheSize = SETUP_MAX_CACHE_SIZE / SETUP_CACHE_BLOCK_SIZE;
  160. }
  161. IoHandle->Handle = SetupOsOpenDestination(Destination,
  162. Flags,
  163. CreatePermissions);
  164. if (IoHandle->Handle == NULL) {
  165. free(IoHandle);
  166. IoHandle = NULL;
  167. }
  168. return IoHandle;
  169. }
  170. VOID
  171. SetupClose (
  172. PVOID Handle
  173. )
  174. /*++
  175. Routine Description:
  176. This routine closes a handle.
  177. Arguments:
  178. Handle - Supplies a pointer to the destination to open.
  179. Return Value:
  180. None.
  181. --*/
  182. {
  183. PSETUP_HANDLE IoHandle;
  184. IoHandle = Handle;
  185. SetupDestroyCache(IoHandle);
  186. if (IoHandle->Handle != NULL) {
  187. SetupOsClose(IoHandle->Handle);
  188. IoHandle->Handle = NULL;
  189. }
  190. free(IoHandle);
  191. return;
  192. }
  193. ssize_t
  194. SetupRead (
  195. PVOID Handle,
  196. void *Buffer,
  197. size_t ByteCount
  198. )
  199. /*++
  200. Routine Description:
  201. This routine reads from an open handle.
  202. Arguments:
  203. Handle - Supplies the handle.
  204. Buffer - Supplies a pointer where the read bytes will be returned.
  205. ByteCount - Supplies the number of bytes to read.
  206. Return Value:
  207. Returns the number of bytes read.
  208. -1 on failure.
  209. --*/
  210. {
  211. ssize_t BytesRead;
  212. size_t BytesThisRound;
  213. PSETUP_CACHE_DATA CacheData;
  214. size_t CacheDataOffset;
  215. LONGLONG CacheOffset;
  216. PSETUP_HANDLE IoHandle;
  217. PVOID ReadBuffer;
  218. size_t TotalBytesRead;
  219. IoHandle = Handle;
  220. if (IoHandle->Cached == FALSE) {
  221. return SetupOsRead(IoHandle->Handle, Buffer, ByteCount);
  222. }
  223. TotalBytesRead = 0;
  224. while (ByteCount != 0) {
  225. CacheOffset = ALIGN_RANGE_DOWN(IoHandle->NextOffset,
  226. SETUP_CACHE_BLOCK_SIZE);
  227. CacheDataOffset = IoHandle->NextOffset - CacheOffset;
  228. BytesThisRound = SETUP_CACHE_BLOCK_SIZE - CacheDataOffset;
  229. if (BytesThisRound > ByteCount) {
  230. BytesThisRound = ByteCount;
  231. }
  232. CacheData = SetupGetCacheData(IoHandle, CacheOffset);
  233. if (CacheData != NULL) {
  234. memcpy(Buffer, CacheData->Data + CacheDataOffset, BytesThisRound);
  235. } else {
  236. if (IoHandle->NextOsOffset != CacheOffset) {
  237. IoHandle->NextOsOffset = SetupOsSeek(IoHandle->Handle,
  238. CacheOffset);
  239. if (IoHandle->NextOsOffset != CacheOffset) {
  240. assert(FALSE);
  241. TotalBytesRead = -1;
  242. break;
  243. }
  244. }
  245. //
  246. // Read directly into the destination buffer, or allocate a new
  247. // buffer if the destination is too small.
  248. //
  249. ReadBuffer = Buffer;
  250. if (BytesThisRound != SETUP_CACHE_BLOCK_SIZE) {
  251. ReadBuffer = malloc(SETUP_CACHE_BLOCK_SIZE);
  252. if (ReadBuffer == NULL) {
  253. TotalBytesRead = -1;
  254. break;
  255. }
  256. }
  257. BytesRead = SetupOsRead(IoHandle->Handle,
  258. ReadBuffer,
  259. SETUP_CACHE_BLOCK_SIZE);
  260. //
  261. // Allow a partial read in case the disk is actually a file that
  262. // hasn't grown all the way out.
  263. //
  264. if (BytesRead < 0) {
  265. perror("Read error");
  266. TotalBytesRead = -1;
  267. break;
  268. } else if (BytesRead < SETUP_CACHE_BLOCK_SIZE) {
  269. memset(ReadBuffer + BytesRead,
  270. 0,
  271. SETUP_CACHE_BLOCK_SIZE - BytesRead);
  272. }
  273. IoHandle->NextOsOffset += BytesRead;
  274. SetupAddCacheData(IoHandle, CacheOffset, ReadBuffer, FALSE);
  275. if (ReadBuffer != Buffer) {
  276. memcpy(Buffer, ReadBuffer +CacheDataOffset, BytesThisRound);
  277. free(ReadBuffer);
  278. }
  279. }
  280. IoHandle->NextOffset += BytesThisRound;
  281. Buffer += BytesThisRound;
  282. ByteCount -= BytesThisRound;
  283. TotalBytesRead += BytesThisRound;
  284. }
  285. return TotalBytesRead;
  286. }
  287. ssize_t
  288. SetupWrite (
  289. PVOID Handle,
  290. void *Buffer,
  291. size_t ByteCount
  292. )
  293. /*++
  294. Routine Description:
  295. This routine writes data to an open handle.
  296. Arguments:
  297. Handle - Supplies the handle.
  298. Buffer - Supplies a pointer to the bytes to write.
  299. ByteCount - Supplies the number of bytes to read.
  300. Return Value:
  301. Returns the number of bytes written.
  302. -1 on failure.
  303. --*/
  304. {
  305. ssize_t BytesRead;
  306. size_t BytesThisRound;
  307. PSETUP_CACHE_DATA CacheData;
  308. size_t CacheDataOffset;
  309. LONGLONG CacheOffset;
  310. PSETUP_HANDLE IoHandle;
  311. PVOID ReadBuffer;
  312. size_t TotalBytesWritten;
  313. IoHandle = Handle;
  314. if (IoHandle->Cached == FALSE) {
  315. return SetupOsWrite(IoHandle->Handle, Buffer, ByteCount);
  316. }
  317. ReadBuffer = NULL;
  318. TotalBytesWritten = 0;
  319. while (ByteCount != 0) {
  320. CacheOffset = ALIGN_RANGE_DOWN(IoHandle->NextOffset,
  321. SETUP_CACHE_BLOCK_SIZE);
  322. CacheDataOffset = IoHandle->NextOffset - CacheOffset;
  323. BytesThisRound = SETUP_CACHE_BLOCK_SIZE - CacheDataOffset;
  324. if (BytesThisRound > ByteCount) {
  325. BytesThisRound = ByteCount;
  326. }
  327. CacheData = SetupGetCacheData(IoHandle, CacheOffset);
  328. if (CacheData != NULL) {
  329. memcpy(CacheData->Data + CacheDataOffset, Buffer, BytesThisRound);
  330. CacheData->Dirty = TRUE;
  331. } else {
  332. //
  333. // The block was not in the cache. If it's a complete block, just
  334. // slam it in.
  335. //
  336. if ((BytesThisRound == SETUP_CACHE_BLOCK_SIZE) &&
  337. (CacheDataOffset == 0)) {
  338. SetupAddCacheData(IoHandle, CacheOffset, Buffer, TRUE);
  339. //
  340. // Go read the data first, then do the partial write.
  341. //
  342. } else {
  343. if (IoHandle->NextOsOffset != CacheOffset) {
  344. IoHandle->NextOsOffset = SetupOsSeek(IoHandle->Handle,
  345. CacheOffset);
  346. if (IoHandle->NextOsOffset != CacheOffset) {
  347. assert(FALSE);
  348. TotalBytesWritten = -1;
  349. break;
  350. }
  351. }
  352. if (ReadBuffer == NULL) {
  353. ReadBuffer = malloc(SETUP_CACHE_BLOCK_SIZE);
  354. if (ReadBuffer == NULL) {
  355. TotalBytesWritten = -1;
  356. break;
  357. }
  358. }
  359. BytesRead = SetupOsRead(IoHandle->Handle,
  360. ReadBuffer,
  361. SETUP_CACHE_BLOCK_SIZE);
  362. //
  363. // Allow a partial read in case the disk is actually a file that
  364. // hasn't grown all the way out.
  365. //
  366. if (BytesRead < 0) {
  367. perror("Read error");
  368. TotalBytesWritten = -1;
  369. break;
  370. } else if (BytesRead < SETUP_CACHE_BLOCK_SIZE) {
  371. memset(ReadBuffer + BytesRead,
  372. 0,
  373. SETUP_CACHE_BLOCK_SIZE - BytesRead);
  374. }
  375. IoHandle->NextOsOffset += BytesRead;
  376. memcpy(ReadBuffer + CacheDataOffset, Buffer, BytesThisRound);
  377. SetupAddCacheData(IoHandle, CacheOffset, ReadBuffer, TRUE);
  378. }
  379. }
  380. IoHandle->NextOffset += BytesThisRound;
  381. Buffer += BytesThisRound;
  382. ByteCount -= BytesThisRound;
  383. TotalBytesWritten += BytesThisRound;
  384. }
  385. if (ReadBuffer != NULL) {
  386. free(ReadBuffer);
  387. }
  388. return TotalBytesWritten;
  389. }
  390. LONGLONG
  391. SetupSeek (
  392. PVOID Handle,
  393. LONGLONG Offset
  394. )
  395. /*++
  396. Routine Description:
  397. This routine seeks in the current file or device.
  398. Arguments:
  399. Handle - Supplies the handle.
  400. Offset - Supplies the new offset to set.
  401. Return Value:
  402. Returns the resulting file offset after the operation.
  403. -1 on failure, and errno will contain more information. The file offset
  404. will remain unchanged.
  405. --*/
  406. {
  407. PSETUP_HANDLE IoHandle;
  408. IoHandle = Handle;
  409. if (IoHandle->Cached == FALSE) {
  410. return SetupOsSeek(IoHandle->Handle, Offset);
  411. }
  412. IoHandle->NextOffset = Offset;
  413. return Offset;
  414. }
  415. INT
  416. SetupFstat (
  417. PVOID Handle,
  418. PULONGLONG FileSize,
  419. time_t *ModificationDate,
  420. mode_t *Mode
  421. )
  422. /*++
  423. Routine Description:
  424. This routine gets details for the given open file.
  425. Arguments:
  426. Handle - Supplies the handle.
  427. FileSize - Supplies an optional pointer where the file size will be
  428. returned on success.
  429. ModificationDate - Supplies an optional pointer where the file's
  430. modification date will be returned on success.
  431. Mode - Supplies an optional pointer where the file's mode information will
  432. be returned on success.
  433. Return Value:
  434. 0 on success.
  435. Non-zero on failure.
  436. --*/
  437. {
  438. PSETUP_HANDLE IoHandle;
  439. IoHandle = Handle;
  440. return SetupOsFstat(IoHandle->Handle, FileSize, ModificationDate, Mode);
  441. }
  442. INT
  443. SetupFtruncate (
  444. PVOID Handle,
  445. ULONGLONG NewSize
  446. )
  447. /*++
  448. Routine Description:
  449. This routine sets the file size of the given file.
  450. Arguments:
  451. Handle - Supplies the handle.
  452. NewSize - Supplies the new file size.
  453. Return Value:
  454. 0 on success.
  455. Non-zero on failure.
  456. --*/
  457. {
  458. PSETUP_HANDLE IoHandle;
  459. int Result;
  460. IoHandle = Handle;
  461. assert(IoHandle->Cached == FALSE);
  462. Result = SetupOsFtruncate(IoHandle->Handle, NewSize);
  463. return Result;
  464. }
  465. INT
  466. SetupEnumerateDirectory (
  467. PVOID VolumeHandle,
  468. PSTR DirectoryPath,
  469. PSTR *Enumeration
  470. )
  471. /*++
  472. Routine Description:
  473. This routine enumerates the contents of a given directory.
  474. Arguments:
  475. VolumeHandle - Supplies the open volume handle.
  476. DirectoryPath - Supplies a pointer to a string containing the path to the
  477. directory to enumerate.
  478. Enumeration - Supplies a pointer where a pointer to a sequence of
  479. strings will be returned containing the files in the directory. The
  480. sequence will be terminated by an empty string. The caller is
  481. responsible for freeing this memory when done.
  482. Return Value:
  483. 0 on success.
  484. Non-zero on failure.
  485. --*/
  486. {
  487. INT Result;
  488. Result = SetupOsEnumerateDirectory(VolumeHandle,
  489. DirectoryPath,
  490. Enumeration);
  491. return Result;
  492. }
  493. VOID
  494. SetupDetermineExecuteBit (
  495. PVOID Handle,
  496. PCSTR Path,
  497. mode_t *Mode
  498. )
  499. /*++
  500. Routine Description:
  501. This routine determines whether the open file is executable.
  502. Arguments:
  503. Handle - Supplies the open file handle.
  504. Path - Supplies the path the file was opened from (sometimes the file name
  505. is used as a hint).
  506. Mode - Supplies a pointer to the current mode bits. This routine may add
  507. the executable bit to user/group/other if it determines this file is
  508. executable.
  509. Return Value:
  510. None.
  511. --*/
  512. {
  513. PSETUP_HANDLE IoHandle;
  514. IoHandle = Handle;
  515. assert(IoHandle->Cached == FALSE);
  516. SetupOsDetermineExecuteBit(IoHandle->Handle, Path, Mode);
  517. return;
  518. }
  519. //
  520. // --------------------------------------------------------- Internal Functions
  521. //
  522. VOID
  523. SetupDestroyCache (
  524. PSETUP_HANDLE Handle
  525. )
  526. /*++
  527. Routine Description:
  528. This routine destroys the data cache on a handle.
  529. Arguments:
  530. Handle - Supplies the handle.
  531. Return Value:
  532. None.
  533. --*/
  534. {
  535. PLIST_ENTRY CurrentEntry;
  536. PSETUP_CACHE_DATA Data;
  537. if (Handle->Cached == FALSE) {
  538. return;
  539. }
  540. assert(Handle->CacheLruList.Next != NULL);
  541. //
  542. // Rudely go through the list. Don't bother removing them from the list
  543. // or the tree, but do clean them.
  544. //
  545. CurrentEntry = Handle->CacheLruList.Previous;
  546. while (CurrentEntry != &(Handle->CacheLruList)) {
  547. Data = LIST_VALUE(CurrentEntry, SETUP_CACHE_DATA, ListEntry);
  548. if (Data->Dirty != FALSE) {
  549. SetupCleanCacheData(Handle, Data);
  550. }
  551. CurrentEntry = CurrentEntry->Previous;
  552. Handle->CacheSize -= 1;
  553. free(Data);
  554. }
  555. assert(Handle->CacheSize == 0);
  556. INITIALIZE_LIST_HEAD(&(Handle->CacheLruList));
  557. memset(&(Handle->Cache), 0, sizeof(RED_BLACK_TREE));
  558. return;
  559. }
  560. VOID
  561. SetupAddCacheData (
  562. PSETUP_HANDLE Handle,
  563. ULONGLONG Offset,
  564. PVOID Buffer,
  565. BOOL Dirty
  566. )
  567. /*++
  568. Routine Description:
  569. This routine adds an entry to the cache. It's assumed the entry doesn't
  570. already exist.
  571. Arguments:
  572. Handle - Supplies the handle.
  573. Offset - Supplies the offset of the data.
  574. Buffer - Supplies a pointer to the buffer of the data. This data will be
  575. copied.
  576. Dirty - Supplies a boolean indicating whether or not the data is dirty
  577. already.
  578. Return Value:
  579. None. On allocation failure, the data won't be cached.
  580. --*/
  581. {
  582. PSETUP_CACHE_DATA Data;
  583. assert(ALIGN_RANGE_DOWN(Offset, SETUP_CACHE_BLOCK_SIZE) == Offset);
  584. //
  585. // If the cache is at it's max, evict and reclaim the least recently used
  586. // entry.
  587. //
  588. if (Handle->CacheSize >= Handle->MaxCacheSize) {
  589. Data = LIST_VALUE(Handle->CacheLruList.Previous,
  590. SETUP_CACHE_DATA,
  591. ListEntry);
  592. LIST_REMOVE(&(Data->ListEntry));
  593. RtlRedBlackTreeRemove(&(Handle->Cache), &(Data->TreeNode));
  594. Handle->CacheSize -= 1;
  595. if (Data->Dirty != FALSE) {
  596. SetupCleanCacheData(Handle, Data);
  597. }
  598. } else {
  599. Data = malloc(sizeof(SETUP_CACHE_DATA) + SETUP_CACHE_BLOCK_SIZE);
  600. if (Data == NULL) {
  601. assert(FALSE);
  602. return;
  603. }
  604. }
  605. assert(Offset < SETUP_CACHE_MAX_OFFSET);
  606. Data->Offset = Offset;
  607. Data->Dirty = Dirty;
  608. Data->Data = Data + 1;
  609. memcpy(Data->Data, Buffer, SETUP_CACHE_BLOCK_SIZE);
  610. RtlRedBlackTreeInsert(&(Handle->Cache), &(Data->TreeNode));
  611. INSERT_AFTER(&(Data->ListEntry), &(Handle->CacheLruList));
  612. Handle->CacheSize += 1;
  613. return;
  614. }
  615. INT
  616. SetupCleanCacheData (
  617. PSETUP_HANDLE Handle,
  618. PSETUP_CACHE_DATA Data
  619. )
  620. /*++
  621. Routine Description:
  622. This routine cleans dirty cache data, writing it out to the underlying
  623. handle.
  624. Arguments:
  625. Handle - Supplies the handle.
  626. Data - Supplies the dirty cache entry to write out.
  627. Return Value:
  628. 0 on success.
  629. Non-zero on failure.
  630. --*/
  631. {
  632. PUCHAR Bytes;
  633. ssize_t BytesWritten;
  634. UINTN Errors;
  635. UINTN FirstBad;
  636. UINTN Index;
  637. UINTN LastBad;
  638. PUCHAR ReadBytes;
  639. assert(Data->Dirty != FALSE);
  640. errno = 0;
  641. if (Data->Offset != Handle->NextOsOffset) {
  642. Handle->NextOsOffset = SetupOsSeek(Handle->Handle, Data->Offset);
  643. if (Handle->NextOsOffset != Data->Offset) {
  644. fprintf(stderr, "Error: Failed to seek.\n");
  645. return -1;
  646. }
  647. }
  648. BytesWritten = SetupOsWrite(Handle->Handle,
  649. Data->Data,
  650. SETUP_CACHE_BLOCK_SIZE);
  651. if (BytesWritten != SETUP_CACHE_BLOCK_SIZE) {
  652. if (errno != 0) {
  653. fprintf(stderr,
  654. "Error: Write failed at offset %llx: %d bytes "
  655. "written: %s.\n",
  656. Data->Offset,
  657. (int)BytesWritten,
  658. strerror(errno));
  659. return -1;
  660. }
  661. }
  662. if (SetupVerifyWrites != FALSE) {
  663. Bytes = Data->Data;
  664. ReadBytes = Handle->CheckBlock;
  665. SetupOsSeek(Handle->Handle, Data->Offset);
  666. SetupOsRead(Handle->Handle, ReadBytes, SETUP_CACHE_BLOCK_SIZE);
  667. if (memcmp(ReadBytes, Bytes, SETUP_CACHE_BLOCK_SIZE) != 0) {
  668. FirstBad = SETUP_CACHE_BLOCK_SIZE;
  669. LastBad = 0;
  670. Errors = 0;
  671. for (Index = 0; Index < SETUP_CACHE_BLOCK_SIZE; Index += 1) {
  672. if (Bytes[Index] != ReadBytes[Index]) {
  673. Errors += 1;
  674. if (Errors < 10) {
  675. fprintf(stderr,
  676. " Offset %lx: Got %02x, expected %02x\n",
  677. Index,
  678. ReadBytes[Index],
  679. Bytes[Index]);
  680. }
  681. if (Index < FirstBad) {
  682. FirstBad = Index;
  683. }
  684. if (Index > LastBad) {
  685. LastBad = Index;
  686. }
  687. }
  688. }
  689. fprintf(stderr,
  690. "%ld errors (offsets %lx - %lx) at offset %llx\n",
  691. Errors,
  692. FirstBad,
  693. LastBad,
  694. Data->Offset);
  695. }
  696. }
  697. Handle->NextOsOffset += SETUP_CACHE_BLOCK_SIZE;
  698. Data->Dirty = FALSE;
  699. return 0;
  700. }
  701. PSETUP_CACHE_DATA
  702. SetupGetCacheData (
  703. PSETUP_HANDLE Handle,
  704. ULONGLONG Offset
  705. )
  706. /*++
  707. Routine Description:
  708. This routine queries the data cache for an entry. If found, it also puts
  709. the cache entry at the head of the list.
  710. Arguments:
  711. Handle - Supplies the handle.
  712. Offset - Supplies the desired offset.
  713. Return Value:
  714. Returns a pointer to the cache entry on success.
  715. NULL if no cache entry exists.
  716. --*/
  717. {
  718. PSETUP_CACHE_DATA Data;
  719. PRED_BLACK_TREE_NODE FoundNode;
  720. SETUP_CACHE_DATA Search;
  721. assert(Offset < SETUP_CACHE_MAX_OFFSET);
  722. Search.Offset = Offset;
  723. FoundNode = RtlRedBlackTreeSearch(&(Handle->Cache), &(Search.TreeNode));
  724. if (FoundNode != NULL) {
  725. Data = RED_BLACK_TREE_VALUE(FoundNode, SETUP_CACHE_DATA, TreeNode);
  726. LIST_REMOVE(&(Data->ListEntry));
  727. INSERT_AFTER(&(Data->ListEntry), &(Handle->CacheLruList));
  728. return Data;
  729. }
  730. return NULL;
  731. }
  732. COMPARISON_RESULT
  733. SetupCompareCacheTreeNodes (
  734. PRED_BLACK_TREE Tree,
  735. PRED_BLACK_TREE_NODE FirstNode,
  736. PRED_BLACK_TREE_NODE SecondNode
  737. )
  738. /*++
  739. Routine Description:
  740. This routine compares two Red-Black tree nodes.
  741. Arguments:
  742. Tree - Supplies a pointer to the Red-Black tree that owns both nodes.
  743. FirstNode - Supplies a pointer to the left side of the comparison.
  744. SecondNode - Supplies a pointer to the second side of the comparison.
  745. Return Value:
  746. Same if the two nodes have the same value.
  747. Ascending if the first node is less than the second node.
  748. Descending if the second node is less than the first node.
  749. --*/
  750. {
  751. PSETUP_CACHE_DATA FirstEntry;
  752. PSETUP_CACHE_DATA SecondEntry;
  753. FirstEntry = RED_BLACK_TREE_VALUE(FirstNode, SETUP_CACHE_DATA, TreeNode);
  754. SecondEntry = RED_BLACK_TREE_VALUE(SecondNode, SETUP_CACHE_DATA, TreeNode);
  755. assert(FirstEntry->Offset < SETUP_CACHE_MAX_OFFSET);
  756. assert(SecondEntry->Offset < SETUP_CACHE_MAX_OFFSET);
  757. if (FirstEntry->Offset < SecondEntry->Offset) {
  758. return ComparisonResultAscending;
  759. } else if (FirstEntry->Offset > SecondEntry->Offset) {
  760. return ComparisonResultDescending;
  761. }
  762. return ComparisonResultSame;
  763. }