Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11907 lines
289 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. brfcase.c
  5. Abstract:
  6. This module implements the conversion of paths inside a Windows Briefcase Database
  7. as a result of files/directories relocation after an upgrade.
  8. Author:
  9. Ovidiu Temereanca (ovidiut) 24-Jun-1999
  10. Environment:
  11. GUI mode Setup.
  12. Revision History:
  13. 24-Jun-1999 ovidiut Creation and initial implementation.
  14. --*/
  15. #include "pch.h"
  16. #include "brfcasep.h"
  17. //
  18. // globals
  19. //
  20. POOLHANDLE g_BrfcasePool = NULL;
  21. /* Constants
  22. ************/
  23. /* database file attributes */
  24. #define DB_FILE_ATTR (FILE_ATTRIBUTE_HIDDEN)
  25. /* database cache lengths */
  26. #define DEFAULT_DATABASE_CACHE_LEN (32)
  27. #define MAX_DATABASE_CACHE_LEN (32 * 1024)
  28. /* string table allocation constants */
  29. #define NUM_NAME_HASH_BUCKETS (67)
  30. /* Types
  31. ********/
  32. /* briefcase database description */
  33. typedef struct _brfcasedb
  34. {
  35. /*
  36. * handle to path of folder of open database (stored in briefcase path list)
  37. */
  38. HPATH hpathDBFolder;
  39. /* name of database file */
  40. LPTSTR pszDBName;
  41. /* handle to open cached database file */
  42. HCACHEDFILE hcfDB;
  43. /*
  44. * handle to path of folder that database was last saved in (stored in
  45. * briefcase's path list), only valid during OpenBriefcase() and
  46. * SaveBriefcase()
  47. */
  48. HPATH hpathLastSavedDBFolder;
  49. }
  50. BRFCASEDB;
  51. DECLARE_STANDARD_TYPES(BRFCASEDB);
  52. /*
  53. * briefcase flags
  54. *
  55. * N.b., the private BR_ flags must not collide with the public OB_ flags!
  56. */
  57. typedef enum _brfcaseflags
  58. {
  59. /* The briefcase database has been opened. */
  60. BR_FL_DATABASE_OPENED = 0x00010000,
  61. /* flag combinations */
  62. ALL_BR_FLAGS = (BR_FL_DATABASE_OPENED
  63. ),
  64. ALL_BRFCASE_FLAGS = (ALL_OB_FLAGS |
  65. ALL_BR_FLAGS)
  66. }
  67. BRFCASEFLAGS;
  68. /* briefcase structure */
  69. typedef struct _brfcase
  70. {
  71. /* flags */
  72. DWORD dwFlags;
  73. /* handle to name string table */
  74. HSTRINGTABLE hstNames;
  75. /* handle to list of paths */
  76. HPATHLIST hpathlist;
  77. /* handle to array of pointers to twin families */
  78. HPTRARRAY hpaTwinFamilies;
  79. /* handle to array of pointers to folder pairs */
  80. HPTRARRAY hpaFolderPairs;
  81. /*
  82. * handle to parent window, only valid if OB_FL_ALLOW_UI is set in dwFlags
  83. * field
  84. */
  85. HWND hwndOwner;
  86. /* database description */
  87. BRFCASEDB bcdb;
  88. }
  89. BRFCASE;
  90. DECLARE_STANDARD_TYPES(BRFCASE);
  91. /* database briefcase structure */
  92. typedef struct _dbbrfcase
  93. {
  94. /* old handle to folder that database was saved in */
  95. HPATH hpathLastSavedDBFolder;
  96. }
  97. DBBRFCASE;
  98. DECLARE_STANDARD_TYPES(DBBRFCASE);
  99. /* database cache size */
  100. DWORD MdwcbMaxDatabaseCacheLen = MAX_DATABASE_CACHE_LEN;
  101. TWINRESULT OpenBriefcaseDatabase(PBRFCASE, LPCTSTR);
  102. TWINRESULT CloseBriefcaseDatabase(PBRFCASEDB);
  103. BOOL CreateBriefcase(PBRFCASE *, DWORD, HWND);
  104. TWINRESULT DestroyBriefcase(PBRFCASE);
  105. TWINRESULT MyWriteDatabase(PBRFCASE);
  106. TWINRESULT MyReadDatabase(PBRFCASE, DWORD);
  107. void CopyFileStampFromFindData(PCWIN32_FIND_DATA pcwfdSrc,
  108. PFILESTAMP pfsDest)
  109. {
  110. ASSERT(IS_VALID_READ_PTR(pcwfdSrc, CWIN32_FIND_DATA));
  111. pfsDest->dwcbHighLength = pcwfdSrc->nFileSizeHigh;
  112. pfsDest->dwcbLowLength = pcwfdSrc->nFileSizeLow;
  113. /* Convert to local time and save that too */
  114. if ( !FileTimeToLocalFileTime(&pcwfdSrc->ftLastWriteTime, &pfsDest->ftModLocal) )
  115. {
  116. /* Just copy the time if FileTimeToLocalFileTime failed */
  117. pfsDest->ftModLocal = pcwfdSrc->ftLastWriteTime;
  118. }
  119. pfsDest->ftMod = pcwfdSrc->ftLastWriteTime;
  120. pfsDest->fscond = FS_COND_EXISTS;
  121. }
  122. void MyGetFileStamp(LPCTSTR pcszFile, PFILESTAMP pfs)
  123. {
  124. WIN32_FIND_DATA wfd;
  125. HANDLE hff;
  126. ASSERT(IS_VALID_STRING_PTR(pcszFile, CSTR));
  127. ZeroMemory(pfs, sizeof(*pfs));
  128. hff = FindFirstFile(pcszFile, &wfd);
  129. if (hff != INVALID_HANDLE_VALUE)
  130. {
  131. if (! IS_ATTR_DIR(wfd.dwFileAttributes))
  132. CopyFileStampFromFindData(&wfd, pfs);
  133. else
  134. pfs->fscond = FS_COND_EXISTS;
  135. EVAL(FindClose(hff));
  136. }
  137. else
  138. pfs->fscond = FS_COND_DOES_NOT_EXIST;
  139. }
  140. TWINRESULT OpenBriefcaseDatabase(PBRFCASE pbr, LPCTSTR pcszPath)
  141. {
  142. TWINRESULT tr;
  143. TCHAR rgchCanonicalPath[MAX_SEPARATED_PATH_LEN];
  144. DWORD dwOutFlags;
  145. TCHAR rgchNetResource[MAX_PATH_LEN];
  146. LPTSTR pszRootPathSuffix;
  147. ASSERT(IS_VALID_STRUCT_PTR(pbr, CBRFCASE));
  148. ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
  149. if (GetCanonicalPathInfo(pcszPath, rgchCanonicalPath, &dwOutFlags,
  150. rgchNetResource, &pszRootPathSuffix))
  151. {
  152. LPTSTR pszDBName;
  153. pszDBName = (LPTSTR)ExtractFileName(pszRootPathSuffix);
  154. ASSERT(IS_SLASH(*(pszDBName - 1)));
  155. if (StringCopy2(pszDBName, &(pbr->bcdb.pszDBName)))
  156. {
  157. if (pszDBName == pszRootPathSuffix)
  158. {
  159. /* Database in root. */
  160. *pszDBName = TEXT('\0');
  161. ASSERT(IsRootPath(rgchCanonicalPath));
  162. }
  163. else
  164. {
  165. ASSERT(pszDBName > pszRootPathSuffix);
  166. *(pszDBName - 1) = TEXT('\0');
  167. }
  168. tr = TranslatePATHRESULTToTWINRESULT(
  169. AddPath(pbr->hpathlist, rgchCanonicalPath,
  170. &(pbr->bcdb.hpathDBFolder)));
  171. if (tr == TR_SUCCESS)
  172. {
  173. if (IsPathVolumeAvailable(pbr->bcdb.hpathDBFolder))
  174. {
  175. TCHAR rgchDBPath[MAX_PATH_LEN];
  176. CACHEDFILE cfDB;
  177. GetPathString(pbr->bcdb.hpathDBFolder, rgchDBPath);
  178. CatPath(rgchDBPath, pbr->bcdb.pszDBName);
  179. /* Assume sequential reads and writes. */
  180. /* Share read access, but not write access. */
  181. cfDB.pcszPath = rgchDBPath;
  182. cfDB.dwOpenMode = (GENERIC_READ | GENERIC_WRITE);
  183. cfDB.dwSharingMode = FILE_SHARE_READ;
  184. cfDB.psa = NULL;
  185. cfDB.dwCreateMode = OPEN_ALWAYS;
  186. cfDB.dwAttrsAndFlags = (DB_FILE_ATTR | FILE_FLAG_SEQUENTIAL_SCAN);
  187. cfDB.hTemplateFile = NULL;
  188. cfDB.dwcbDefaultCacheSize = DEFAULT_DATABASE_CACHE_LEN;
  189. tr = TranslateFCRESULTToTWINRESULT(
  190. CreateCachedFile(&cfDB, &(pbr->bcdb.hcfDB)));
  191. if (tr == TR_SUCCESS)
  192. {
  193. pbr->bcdb.hpathLastSavedDBFolder = NULL;
  194. ASSERT(IS_FLAG_CLEAR(pbr->dwFlags, BR_FL_DATABASE_OPENED));
  195. SET_FLAG(pbr->dwFlags, BR_FL_DATABASE_OPENED);
  196. }
  197. else
  198. {
  199. DeletePath(pbr->bcdb.hpathDBFolder);
  200. OPENBRIEFCASEDATABASE_BAIL:
  201. FreeMemory(pbr->bcdb.pszDBName);
  202. }
  203. }
  204. else
  205. {
  206. tr = TR_UNAVAILABLE_VOLUME;
  207. goto OPENBRIEFCASEDATABASE_BAIL;
  208. }
  209. }
  210. }
  211. else
  212. tr = TR_OUT_OF_MEMORY;
  213. }
  214. else
  215. tr = TWINRESULTFromLastError(TR_INVALID_PARAMETER);
  216. return(tr);
  217. }
  218. TWINRESULT CloseBriefcaseDatabase(PBRFCASEDB pbcdb)
  219. {
  220. TWINRESULT tr;
  221. TCHAR rgchDBPath[MAX_PATH_LEN];
  222. FILESTAMP fsDB;
  223. tr = CloseCachedFile(pbcdb->hcfDB) ? TR_SUCCESS : TR_BRIEFCASE_WRITE_FAILED;
  224. if (tr == TR_SUCCESS)
  225. TRACE_OUT((TEXT("CloseBriefcaseDatabase(): Closed cached briefcase database file %s\\%s."),
  226. DebugGetPathString(pbcdb->hpathDBFolder),
  227. pbcdb->pszDBName));
  228. else
  229. WARNING_OUT((TEXT("CloseBriefcaseDatabase(): Failed to close cached briefcase database file %s\\%s."),
  230. DebugGetPathString(pbcdb->hpathDBFolder),
  231. pbcdb->pszDBName));
  232. /* Try not to leave a 0-length database laying around. */
  233. GetPathString(pbcdb->hpathDBFolder, rgchDBPath);
  234. CatPath(rgchDBPath, pbcdb->pszDBName);
  235. MyGetFileStamp(rgchDBPath, &fsDB);
  236. if (fsDB.fscond == FS_COND_EXISTS &&
  237. (! fsDB.dwcbLowLength && ! fsDB.dwcbHighLength))
  238. {
  239. if (DeleteFile(rgchDBPath))
  240. WARNING_OUT((TEXT("CloseBriefcaseDatabase(): Deleted 0 length database %s\\%s."),
  241. DebugGetPathString(pbcdb->hpathDBFolder),
  242. pbcdb->pszDBName));
  243. }
  244. FreeMemory(pbcdb->pszDBName);
  245. DeletePath(pbcdb->hpathDBFolder);
  246. return(tr);
  247. }
  248. BOOL CreateBriefcase(PBRFCASE *ppbr, DWORD dwInFlags,
  249. HWND hwndOwner)
  250. {
  251. BOOL bResult = FALSE;
  252. ASSERT(IS_VALID_WRITE_PTR(ppbr, PBRFCASE));
  253. ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_BRFCASE_FLAGS));
  254. ASSERT(IS_FLAG_CLEAR(dwInFlags, OB_FL_ALLOW_UI) ||
  255. IS_VALID_HANDLE(hwndOwner, WND));
  256. if (AllocateMemory(sizeof(**ppbr), ppbr))
  257. {
  258. DWORD dwCPLFlags;
  259. dwCPLFlags = (RLI_IFL_CONNECT |
  260. RLI_IFL_UPDATE |
  261. RLI_IFL_LOCAL_SEARCH);
  262. if (IS_FLAG_SET(dwInFlags, OB_FL_ALLOW_UI))
  263. SET_FLAG(dwCPLFlags, RLI_IFL_ALLOW_UI);
  264. if (CreatePathList(dwCPLFlags, hwndOwner, &((*ppbr)->hpathlist)))
  265. {
  266. NEWSTRINGTABLE nszt;
  267. nszt.hbc = NUM_NAME_HASH_BUCKETS;
  268. if (CreateStringTable(&nszt, &((*ppbr)->hstNames)))
  269. {
  270. if (CreateTwinFamilyPtrArray(&((*ppbr)->hpaTwinFamilies)))
  271. {
  272. if (CreateFolderPairPtrArray(&((*ppbr)->hpaFolderPairs)))
  273. {
  274. if (TRUE)
  275. {
  276. (*ppbr)->dwFlags = dwInFlags;
  277. (*ppbr)->hwndOwner = hwndOwner;
  278. bResult = TRUE;
  279. }
  280. else
  281. {
  282. CREATEBRIEFCASE_BAIL1:
  283. DestroyTwinFamilyPtrArray((*ppbr)->hpaTwinFamilies);
  284. CREATEBRIEFCASE_BAIL2:
  285. DestroyStringTable((*ppbr)->hstNames);
  286. CREATEBRIEFCASE_BAIL3:
  287. DestroyPathList((*ppbr)->hpathlist);
  288. CREATEBRIEFCASE_BAIL4:
  289. FreeMemory((*ppbr));
  290. }
  291. }
  292. else
  293. goto CREATEBRIEFCASE_BAIL1;
  294. }
  295. else
  296. goto CREATEBRIEFCASE_BAIL2;
  297. }
  298. else
  299. goto CREATEBRIEFCASE_BAIL3;
  300. }
  301. else
  302. goto CREATEBRIEFCASE_BAIL4;
  303. }
  304. ASSERT(! bResult ||
  305. IS_VALID_STRUCT_PTR(*ppbr, CBRFCASE));
  306. return(bResult);
  307. }
  308. TWINRESULT DestroyBriefcase(PBRFCASE pbr)
  309. {
  310. TWINRESULT tr;
  311. ASSERT(IS_VALID_STRUCT_PTR(pbr, CBRFCASE));
  312. if (IS_FLAG_SET(pbr->dwFlags, BR_FL_DATABASE_OPENED))
  313. tr = CloseBriefcaseDatabase(&(pbr->bcdb));
  314. else
  315. tr = TR_SUCCESS;
  316. //
  317. // don't free any memory here; this is not done properly if some strings were
  318. // replaced with longer ones
  319. //
  320. #if 0
  321. DestroyFolderPairPtrArray(pbr->hpaFolderPairs);
  322. DestroyTwinFamilyPtrArray(pbr->hpaTwinFamilies);
  323. ASSERT(! GetStringCount(pbr->hstNames));
  324. DestroyStringTable(pbr->hstNames);
  325. ASSERT(! GetPathCount(pbr->hpathlist));
  326. DestroyPathList(pbr->hpathlist);
  327. FreeMemory(pbr);
  328. #endif
  329. return(tr);
  330. }
  331. TWINRESULT MyWriteDatabase(PBRFCASE pbr)
  332. {
  333. TWINRESULT tr;
  334. ASSERT(IS_VALID_STRUCT_PTR(pbr, CBRFCASE));
  335. ASSERT(IS_FLAG_SET(pbr->dwFlags, BR_FL_DATABASE_OPENED));
  336. {
  337. /* Grow the database cache in preparation for writing. */
  338. ASSERT(MdwcbMaxDatabaseCacheLen > 0);
  339. if (SetCachedFileCacheSize(pbr->bcdb.hcfDB, MdwcbMaxDatabaseCacheLen)
  340. != FCR_SUCCESS)
  341. WARNING_OUT((TEXT("MyWriteDatabase(): Unable to grow database cache to %lu bytes. Using default database write cache of %lu bytes."),
  342. MdwcbMaxDatabaseCacheLen,
  343. (DWORD)DEFAULT_DATABASE_CACHE_LEN));
  344. /* Write the database. */
  345. tr = WriteTwinDatabase(pbr->bcdb.hcfDB, (HBRFCASE)pbr);
  346. if (tr == TR_SUCCESS)
  347. {
  348. if (CommitCachedFile(pbr->bcdb.hcfDB))
  349. {
  350. /* Shrink the database cache back down to its default size. */
  351. EVAL(SetCachedFileCacheSize(pbr->bcdb.hcfDB,
  352. DEFAULT_DATABASE_CACHE_LEN)
  353. == FCR_SUCCESS);
  354. TRACE_OUT((TEXT("MyWriteDatabase(): Wrote database %s\\%s."),
  355. DebugGetPathString(pbr->bcdb.hpathDBFolder),
  356. pbr->bcdb.pszDBName));
  357. }
  358. else
  359. tr = TR_BRIEFCASE_WRITE_FAILED;
  360. }
  361. }
  362. return(tr);
  363. }
  364. TWINRESULT MyReadDatabase(PBRFCASE pbr, DWORD dwInFlags)
  365. {
  366. TWINRESULT tr;
  367. ASSERT(IS_VALID_STRUCT_PTR(pbr, CBRFCASE));
  368. ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_OB_FLAGS));
  369. {
  370. DWORD dwcbDatabaseSize;
  371. /* Is there an exising database to read? */
  372. dwcbDatabaseSize = GetCachedFileSize(pbr->bcdb.hcfDB);
  373. if (dwcbDatabaseSize > 0)
  374. {
  375. DWORD dwcbMaxCacheSize;
  376. /* Yes. Grow the database cache in preparation for reading. */
  377. /*
  378. * Use file length instead of MdwcbMaxDatabaseCacheLen if file length
  379. * is smaller.
  380. */
  381. ASSERT(MdwcbMaxDatabaseCacheLen > 0);
  382. if (dwcbDatabaseSize < MdwcbMaxDatabaseCacheLen)
  383. {
  384. dwcbMaxCacheSize = dwcbDatabaseSize;
  385. WARNING_OUT((TEXT("MyReadDatabase(): Using file size %lu bytes as read cache size for database %s\\%s."),
  386. dwcbDatabaseSize,
  387. DebugGetPathString(pbr->bcdb.hpathDBFolder),
  388. pbr->bcdb.pszDBName));
  389. }
  390. else
  391. dwcbMaxCacheSize = MdwcbMaxDatabaseCacheLen;
  392. if (TranslateFCRESULTToTWINRESULT(SetCachedFileCacheSize(
  393. pbr->bcdb.hcfDB,
  394. dwcbMaxCacheSize))
  395. != TR_SUCCESS)
  396. WARNING_OUT((TEXT("MyReadDatabase(): Unable to grow database cache to %lu bytes. Using default database read cache of %lu bytes."),
  397. dwcbMaxCacheSize,
  398. (DWORD)DEFAULT_DATABASE_CACHE_LEN));
  399. tr = ReadTwinDatabase((HBRFCASE)pbr, pbr->bcdb.hcfDB);
  400. if (tr == TR_SUCCESS)
  401. {
  402. ASSERT(! pbr->bcdb.hpathLastSavedDBFolder ||
  403. IS_VALID_HANDLE(pbr->bcdb.hpathLastSavedDBFolder, PATH));
  404. if (pbr->bcdb.hpathLastSavedDBFolder)
  405. {
  406. DeletePath(pbr->bcdb.hpathLastSavedDBFolder);
  407. pbr->bcdb.hpathLastSavedDBFolder = NULL;
  408. }
  409. if (tr == TR_SUCCESS)
  410. TRACE_OUT((TEXT("MyReadDatabase(): Read database %s\\%s."),
  411. DebugGetPathString(pbr->bcdb.hpathDBFolder),
  412. pbr->bcdb.pszDBName));
  413. }
  414. /* Shrink the database cache back down to its default size. */
  415. EVAL(TranslateFCRESULTToTWINRESULT(SetCachedFileCacheSize(
  416. pbr->bcdb.hcfDB,
  417. DEFAULT_DATABASE_CACHE_LEN))
  418. == TR_SUCCESS);
  419. }
  420. else
  421. {
  422. tr = TR_SUCCESS;
  423. WARNING_OUT((TEXT("MyReadDatabase(): Database %s\\%s not found."),
  424. DebugGetPathString(pbr->bcdb.hpathDBFolder),
  425. pbr->bcdb.pszDBName));
  426. }
  427. }
  428. return(tr);
  429. }
  430. HSTRINGTABLE GetBriefcaseNameStringTable(HBRFCASE hbr)
  431. {
  432. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  433. return(((PCBRFCASE)hbr)->hstNames);
  434. }
  435. HPTRARRAY GetBriefcaseTwinFamilyPtrArray(HBRFCASE hbr)
  436. {
  437. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  438. return(((PCBRFCASE)hbr)->hpaTwinFamilies);
  439. }
  440. HPTRARRAY GetBriefcaseFolderPairPtrArray(HBRFCASE hbr)
  441. {
  442. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  443. return(((PCBRFCASE)hbr)->hpaFolderPairs);
  444. }
  445. HPATHLIST GetBriefcasePathList(HBRFCASE hbr)
  446. {
  447. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  448. return(((PCBRFCASE)hbr)->hpathlist);
  449. }
  450. TWINRESULT WriteBriefcaseInfo(HCACHEDFILE hcf, HBRFCASE hbr)
  451. {
  452. TWINRESULT tr;
  453. DBBRFCASE dbbr;
  454. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  455. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  456. /* Set up briefcase database structure. */
  457. ASSERT(IS_VALID_HANDLE(((PCBRFCASE)hbr)->bcdb.hpathLastSavedDBFolder, PATH));
  458. dbbr.hpathLastSavedDBFolder = ((PCBRFCASE)hbr)->bcdb.hpathLastSavedDBFolder;
  459. /* Save briefcase database structure. */
  460. if (WriteToCachedFile(hcf, (PCVOID)&dbbr, sizeof(dbbr), NULL))
  461. {
  462. tr = TR_SUCCESS;
  463. TRACE_OUT((TEXT("WriteBriefcaseInfo(): Wrote last saved database folder %s."),
  464. DebugGetPathString(dbbr.hpathLastSavedDBFolder)));
  465. }
  466. else
  467. tr = TR_BRIEFCASE_WRITE_FAILED;
  468. return(tr);
  469. }
  470. TWINRESULT ReadBriefcaseInfo(HCACHEDFILE hcf, HBRFCASE hbr,
  471. HHANDLETRANS hhtFolderTrans)
  472. {
  473. TWINRESULT tr;
  474. DBBRFCASE dbbr;
  475. DWORD dwcbRead;
  476. HPATH hpath;
  477. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  478. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  479. ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
  480. /* Read briefcase database structure. */
  481. if ((ReadFromCachedFile(hcf, &dbbr, sizeof(dbbr), &dwcbRead) &&
  482. dwcbRead == sizeof(dbbr)) &&
  483. TranslateHandle(hhtFolderTrans, (HGENERIC)(dbbr.hpathLastSavedDBFolder),
  484. (PHGENERIC)&hpath))
  485. {
  486. HPATH hpathLastSavedDBFolder;
  487. /*
  488. * Bump last saved database folder path's lock count in the briefcase's
  489. * path list.
  490. */
  491. if (CopyPath(hpath, ((PCBRFCASE)hbr)->hpathlist, &hpathLastSavedDBFolder))
  492. {
  493. ((PBRFCASE)hbr)->bcdb.hpathLastSavedDBFolder = hpathLastSavedDBFolder;
  494. tr = TR_SUCCESS;
  495. TRACE_OUT((TEXT("ReadBriefcaseInfo(): Read last saved database folder %s."),
  496. DebugGetPathString(((PBRFCASE)hbr)->bcdb.hpathLastSavedDBFolder)));
  497. }
  498. else
  499. tr = TR_OUT_OF_MEMORY;
  500. }
  501. else
  502. tr = TR_CORRUPT_BRIEFCASE;
  503. return(tr);
  504. }
  505. /******************************************************************************
  506. @api TWINRESULT | OpenBriefcase | Opens an existing briefcase database, or
  507. creates a new briefcase.
  508. @parm PCSTR | pcszPath | A pointer to a path string indicating the briefcase
  509. database to be opened or created. This parameter is ignored unless the
  510. OB_FL_OPEN_DATABASE flag is set in dwFlags.
  511. @parm DWORD | dwInFlags | A bit mask of flags. This parameter may be any
  512. combination of the following values:
  513. OB_FL_OPEN_DATABASE - Open the briefcase database specified by pcszPath.
  514. OB_FL_TRANSLATE_DB_FOLDER - Translate the folder where the briefcase
  515. database was last saved to the folder where the briefcase database was
  516. opened.
  517. OB_FL_ALLOW_UI - Allow interaction with the user during briefcase
  518. operations.
  519. @parm HWND | hwndOwner | A handle to the parent window to be used when
  520. requesting user interaction. This parameter is ignored if the OB_FL_ALLOW_UI
  521. flag is clear.
  522. @parm PHBRFCASE | phbr | A pointer to an HBRFCASE to be filled in with a
  523. handle to the open briefcase. *phbr is only valid if TR_SUCCESS is returned.
  524. @rdesc If the briefcase was opened or created successfully, TR_SUCCESS is
  525. returned, and *phbr contains a handle to the open briefcase. Otherwise, the
  526. briefcase was not opened or created successfully, the return value indicates
  527. the error that occurred, and *phbr is undefined.
  528. @comm If the OB_FL_OPEN_DATABASE flag is set in dwFlags, the database specified
  529. by pcszPath is associated with the briefcase. If the database specified does
  530. not exist, the database is created.<nl>
  531. If the OB_FL_OPEN_DATABASE flag is clear in dwFlags, no persistent database is
  532. associated with the briefcase. SaveBriefcase() will fail if called on a
  533. briefcase with no associated database.<nl>
  534. Once the caller is finished with the briefcase handle returned by
  535. OpenBriefcase(), CloseBriefcase() should be called to release the briefcase.
  536. SaveBriefcase() may be called before CloseBriefcase() to save the current
  537. contents of the briefcase.
  538. ******************************************************************************/
  539. TWINRESULT WINAPI OpenBriefcase(LPCTSTR pcszPath, DWORD dwInFlags,
  540. HWND hwndOwner, PHBRFCASE phbr)
  541. {
  542. TWINRESULT tr;
  543. /* Verify parameters. */
  544. if (FLAGS_ARE_VALID(dwInFlags, ALL_OB_FLAGS))
  545. {
  546. PBRFCASE pbr;
  547. if (CreateBriefcase(&pbr, dwInFlags, hwndOwner))
  548. {
  549. if (IS_FLAG_SET(dwInFlags, OB_FL_OPEN_DATABASE))
  550. {
  551. tr = OpenBriefcaseDatabase(pbr, pcszPath);
  552. if (tr == TR_SUCCESS)
  553. {
  554. tr = MyReadDatabase(pbr, dwInFlags);
  555. if (tr == TR_SUCCESS)
  556. {
  557. *phbr = (HBRFCASE)pbr;
  558. }
  559. else
  560. {
  561. OPENBRIEFCASE_BAIL:
  562. EVAL(DestroyBriefcase(pbr) == TR_SUCCESS);
  563. }
  564. }
  565. else
  566. goto OPENBRIEFCASE_BAIL;
  567. }
  568. else
  569. {
  570. *phbr = (HBRFCASE)pbr;
  571. tr = TR_SUCCESS;
  572. TRACE_OUT((TEXT("OpenBriefcase(): Opened briefcase %#lx with no associated database, by request."),
  573. *phbr));
  574. }
  575. }
  576. else
  577. tr = TR_OUT_OF_MEMORY;
  578. }
  579. else
  580. tr = TR_INVALID_PARAMETER;
  581. return(tr);
  582. }
  583. /******************************************************************************
  584. @api TWINRESULT | SaveBriefcase | Saves the contents of an open briefcase to a
  585. briefcase database.
  586. @parm HBRFCASE | hbr | A handle to the briefcase to be saved. This handle may
  587. be obtained by calling OpenBriefcase() with a briefcase database path and with
  588. the OB_FL_OPEN_DATABASE flag set. SaveBriefcase() will return
  589. TR_INVALID_PARAMETER if called on a briefcase with no associated briefcase
  590. database.
  591. @rdesc If the contents of the briefcase was saved to the briefcase database
  592. successfully, TR_SUCCESS is returned. Otherwise, the contents of the briefcase
  593. was not saved to the briefcase database successfully, and the return value
  594. indicates the error that occurred.
  595. ******************************************************************************/
  596. TWINRESULT WINAPI SaveBriefcase(HBRFCASE hbr)
  597. {
  598. TWINRESULT tr;
  599. /* Verify parameters. */
  600. if (IS_FLAG_SET(((PBRFCASE)hbr)->dwFlags, BR_FL_DATABASE_OPENED))
  601. {
  602. ((PBRFCASE)hbr)->bcdb.hpathLastSavedDBFolder = ((PCBRFCASE)hbr)->bcdb.hpathDBFolder;
  603. tr = MyWriteDatabase((PBRFCASE)hbr);
  604. ((PBRFCASE)hbr)->bcdb.hpathLastSavedDBFolder = NULL;
  605. }
  606. else
  607. tr = TR_INVALID_PARAMETER;
  608. return(tr);
  609. }
  610. /******************************************************************************
  611. @api TWINRESULT | CloseBriefcase | Closes an open briefcase.
  612. @parm HBRFCASE | hbr | A handle to the briefcase to be closed. This handle may
  613. be obtained by calling OpenBriefcase().
  614. @rdesc If the briefcase was closed successfully, TR_SUCCESS is returned.
  615. Otherwise, the briefcase was not closed successfully, and the return value
  616. indicates the error that occurred.
  617. ******************************************************************************/
  618. TWINRESULT WINAPI CloseBriefcase(HBRFCASE hbr)
  619. {
  620. TWINRESULT tr;
  621. /* Verify parameters. */
  622. if (IS_VALID_HANDLE(hbr, BRFCASE))
  623. {
  624. tr = DestroyBriefcase((PBRFCASE)hbr);
  625. }
  626. else
  627. tr = TR_INVALID_PARAMETER;
  628. return(tr);
  629. }
  630. void CatPath(LPTSTR pszPath, LPCTSTR pcszSubPath)
  631. {
  632. LPTSTR pcsz;
  633. LPTSTR pcszLast;
  634. ASSERT(IS_VALID_STRING_PTR(pszPath, STR));
  635. ASSERT(IS_VALID_STRING_PTR(pcszSubPath, CSTR));
  636. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPath, STR, MAX_PATH_LEN - lstrlen(pszPath)));
  637. /* Find last character in path string. */
  638. for (pcsz = pcszLast = pszPath; *pcsz; pcsz = CharNext(pcsz))
  639. pcszLast = pcsz;
  640. if (IS_SLASH(*pcszLast) && IS_SLASH(*pcszSubPath))
  641. pcszSubPath++;
  642. else if (! IS_SLASH(*pcszLast) && ! IS_SLASH(*pcszSubPath))
  643. {
  644. if (*pcszLast && *pcszLast != COLON && *pcszSubPath)
  645. *pcsz++ = TEXT('\\');
  646. }
  647. MyLStrCpyN(pcsz, pcszSubPath, MAX_PATH_LEN - (int)(pcsz - pszPath));
  648. ASSERT(IS_VALID_STRING_PTR(pszPath, STR));
  649. }
  650. COMPARISONRESULT MapIntToComparisonResult(int nResult)
  651. {
  652. COMPARISONRESULT cr;
  653. /* Any integer is valid input. */
  654. if (nResult < 0)
  655. cr = CR_FIRST_SMALLER;
  656. else if (nResult > 0)
  657. cr = CR_FIRST_LARGER;
  658. else
  659. cr = CR_EQUAL;
  660. return(cr);
  661. }
  662. /*
  663. ** MyLStrCpyN()
  664. **
  665. ** Like lstrcpy(), but the copy is limited to ucb bytes. The destination
  666. ** string is always null-terminated.
  667. **
  668. ** Arguments: pszDest - pointer to destination buffer
  669. ** pcszSrc - pointer to source string
  670. ** ncb - maximum number of bytes to copy, including null
  671. ** terminator
  672. **
  673. ** Returns: void
  674. **
  675. ** Side Effects: none
  676. **
  677. ** N.b., this function behaves quite differently than strncpy()! It does not
  678. ** pad out the destination buffer with null characters, and it always null
  679. ** terminates the destination string.
  680. */
  681. void MyLStrCpyN(LPTSTR pszDest, LPCTSTR pcszSrc, int ncch)
  682. {
  683. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszDest, STR, ncch * sizeof(TCHAR)));
  684. ASSERT(IS_VALID_STRING_PTR(pcszSrc, CSTR));
  685. ASSERT(ncch > 0);
  686. while (ncch > 1)
  687. {
  688. ncch--;
  689. *pszDest = *pcszSrc;
  690. if (*pcszSrc)
  691. {
  692. pszDest++;
  693. pcszSrc++;
  694. }
  695. else
  696. break;
  697. }
  698. if (ncch == 1)
  699. *pszDest = TEXT('\0');
  700. ASSERT(IS_VALID_STRING_PTR(pszDest, STR));
  701. ASSERT(lstrlen(pszDest) < ncch);
  702. ASSERT(lstrlen(pszDest) <= lstrlen(pcszSrc));
  703. }
  704. BOOL StringCopy2(LPCTSTR pcszSrc, LPTSTR *ppszCopy)
  705. {
  706. BOOL bResult;
  707. ASSERT(IS_VALID_STRING_PTR(pcszSrc, CSTR));
  708. ASSERT(IS_VALID_WRITE_PTR(ppszCopy, LPTSTR));
  709. /* (+ 1) for null terminator. */
  710. bResult = AllocateMemory((lstrlen(pcszSrc) + 1) * sizeof(TCHAR), ppszCopy);
  711. if (bResult)
  712. lstrcpy(*ppszCopy, pcszSrc);
  713. ASSERT(! bResult ||
  714. IS_VALID_STRING_PTR(*ppszCopy, STR));
  715. return(bResult);
  716. }
  717. COMPARISONRESULT ComparePathStrings(LPCTSTR pcszFirst, LPCTSTR pcszSecond)
  718. {
  719. ASSERT(IS_VALID_STRING_PTR(pcszFirst, CSTR));
  720. ASSERT(IS_VALID_STRING_PTR(pcszSecond, CSTR));
  721. return(MapIntToComparisonResult(lstrcmpi(pcszFirst, pcszSecond)));
  722. }
  723. #ifdef DEBUG
  724. BOOL IsRootPath(LPCTSTR pcszFullPath)
  725. {
  726. TCHAR rgchCanonicalPath[MAX_PATH_LEN];
  727. DWORD dwOutFlags;
  728. TCHAR rgchNetResource[MAX_PATH_LEN];
  729. LPTSTR pszRootPathSuffix;
  730. ASSERT(IsFullPath(pcszFullPath));
  731. return(GetCanonicalPathInfo(pcszFullPath, rgchCanonicalPath, &dwOutFlags,
  732. rgchNetResource, &pszRootPathSuffix) &&
  733. ! *pszRootPathSuffix);
  734. }
  735. BOOL IsTrailingSlashCanonicalized(LPCTSTR pcszFullPath)
  736. {
  737. BOOL bResult;
  738. BOOL bSlashLast;
  739. LPCTSTR pcszLastPathChar;
  740. ASSERT(IsFullPath(pcszFullPath));
  741. /* Make sure that the path only ends in a slash for root paths. */
  742. pcszLastPathChar = CharPrev(pcszFullPath, pcszFullPath + lstrlen(pcszFullPath));
  743. ASSERT(pcszLastPathChar >= pcszFullPath);
  744. bSlashLast = IS_SLASH(*pcszLastPathChar);
  745. /* Is this a root path? */
  746. if (IsRootPath(pcszFullPath))
  747. bResult = bSlashLast;
  748. else
  749. bResult = ! bSlashLast;
  750. return(bResult);
  751. }
  752. BOOL IsFullPath(LPCTSTR pcszPath)
  753. {
  754. BOOL bResult = FALSE;
  755. TCHAR rgchFullPath[MAX_PATH_LEN];
  756. if (IS_VALID_STRING_PTR(pcszPath, CSTR) &&
  757. EVAL(lstrlen(pcszPath) < MAX_PATH_LEN))
  758. {
  759. DWORD dwPathLen;
  760. LPTSTR pszFileName;
  761. dwPathLen = GetFullPathName(pcszPath, ARRAYSIZE(rgchFullPath), rgchFullPath,
  762. &pszFileName);
  763. if (EVAL(dwPathLen > 0) &&
  764. EVAL(dwPathLen < ARRAYSIZE(rgchFullPath)))
  765. bResult = EVAL(ComparePathStrings(pcszPath, rgchFullPath) == CR_EQUAL);
  766. }
  767. return(bResult);
  768. }
  769. BOOL IsCanonicalPath(LPCTSTR pcszPath)
  770. {
  771. return(EVAL(IsFullPath(pcszPath)) &&
  772. EVAL(IsTrailingSlashCanonicalized(pcszPath)));
  773. }
  774. #endif /* DEBUG */
  775. /* Constants
  776. ************/
  777. /* database header magic id string */
  778. #define MAGIC_HEADER "DDSH\x02\x05\x01\x14"
  779. /* length of MAGIC_HEADER (no null terminator) */
  780. #define MAGIC_HEADER_LEN (8)
  781. /* Types
  782. ********/
  783. typedef struct _dbheader
  784. {
  785. BYTE rgbyteMagic[MAGIC_HEADER_LEN];
  786. DWORD dwcbHeaderLen;
  787. DWORD dwMajorVer;
  788. DWORD dwMinorVer;
  789. }
  790. DBHEADER;
  791. DECLARE_STANDARD_TYPES(DBHEADER);
  792. TWINRESULT WriteDBHeader(HCACHEDFILE, PDBHEADER);
  793. TWINRESULT ReadDBHeader(HCACHEDFILE, PDBHEADER);
  794. TWINRESULT CheckDBHeader(PCDBHEADER);
  795. TWINRESULT WriteTwinInfo(HCACHEDFILE, HBRFCASE);
  796. TWINRESULT ReadTwinInfo(HCACHEDFILE, HBRFCASE, PCDBVERSION);
  797. TWINRESULT WriteDBHeader(HCACHEDFILE hcf, PDBHEADER pdbh)
  798. {
  799. TWINRESULT tr;
  800. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  801. ASSERT(IS_VALID_STRUCT_PTR(pdbh, CDBHEADER));
  802. if (WriteToCachedFile(hcf, (PCVOID)pdbh, sizeof(*pdbh), NULL))
  803. tr = TR_SUCCESS;
  804. else
  805. tr = TR_BRIEFCASE_WRITE_FAILED;
  806. return(tr);
  807. }
  808. TWINRESULT ReadDBHeader(HCACHEDFILE hcf, PDBHEADER pdbh)
  809. {
  810. TWINRESULT tr;
  811. DWORD dwcbRead;
  812. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  813. ASSERT(IS_VALID_WRITE_PTR(pdbh, DBHEADER));
  814. if (ReadFromCachedFile(hcf, pdbh, sizeof(*pdbh), &dwcbRead) &&
  815. dwcbRead == sizeof(*pdbh))
  816. tr = CheckDBHeader(pdbh);
  817. else
  818. tr = TR_CORRUPT_BRIEFCASE;
  819. return(tr);
  820. }
  821. TWINRESULT CheckDBHeader(PCDBHEADER pcdbh)
  822. {
  823. TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  824. ASSERT(IS_VALID_READ_PTR(pcdbh, CDBHEADER));
  825. if (MyMemComp(pcdbh->rgbyteMagic, MAGIC_HEADER, MAGIC_HEADER_LEN) == CR_EQUAL)
  826. {
  827. /* Treat older databases as corrupt. Support M8 databases. */
  828. if (pcdbh->dwMajorVer == HEADER_MAJOR_VER &&
  829. (pcdbh->dwMinorVer == HEADER_MINOR_VER || pcdbh->dwMinorVer == HEADER_M8_MINOR_VER))
  830. {
  831. if (pcdbh->dwcbHeaderLen == sizeof(*pcdbh))
  832. tr = TR_SUCCESS;
  833. }
  834. else if (pcdbh->dwMajorVer > HEADER_MAJOR_VER ||
  835. (pcdbh->dwMajorVer == HEADER_MAJOR_VER &&
  836. pcdbh->dwMinorVer > HEADER_MINOR_VER))
  837. {
  838. tr = TR_NEWER_BRIEFCASE;
  839. WARNING_OUT((TEXT("CheckDBHeader(): Newer database version %lu.%lu."),
  840. pcdbh->dwMajorVer,
  841. pcdbh->dwMinorVer));
  842. }
  843. else
  844. {
  845. tr = TR_CORRUPT_BRIEFCASE;
  846. WARNING_OUT((TEXT("CheckDBHeader(): Treating old database version %lu.%lu as corrupt. Current database version is %lu.%lu."),
  847. pcdbh->dwMajorVer,
  848. pcdbh->dwMinorVer,
  849. (DWORD)HEADER_MAJOR_VER,
  850. (DWORD)HEADER_MINOR_VER));
  851. }
  852. }
  853. return(tr);
  854. }
  855. TWINRESULT WriteTwinInfo(HCACHEDFILE hcf, HBRFCASE hbr)
  856. {
  857. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  858. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  859. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  860. tr = WritePathList(hcf, GetBriefcasePathList(hbr));
  861. if (tr == TR_SUCCESS)
  862. {
  863. tr = WriteBriefcaseInfo(hcf, hbr);
  864. if (tr == TR_SUCCESS)
  865. {
  866. tr = WriteStringTable(hcf, GetBriefcaseNameStringTable(hbr));
  867. if (tr == TR_SUCCESS)
  868. {
  869. tr = WriteTwinFamilies(hcf, GetBriefcaseTwinFamilyPtrArray(hbr));
  870. if (tr == TR_SUCCESS)
  871. tr = WriteFolderPairList(hcf, GetBriefcaseFolderPairPtrArray(hbr));
  872. }
  873. }
  874. }
  875. return(tr);
  876. }
  877. TWINRESULT ReadTwinInfo(HCACHEDFILE hcf, HBRFCASE hbr,
  878. PCDBVERSION pcdbver)
  879. {
  880. TWINRESULT tr;
  881. HHANDLETRANS hhtPathTrans;
  882. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  883. ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION));
  884. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  885. tr = ReadPathList(hcf, GetBriefcasePathList(hbr), &hhtPathTrans);
  886. if (tr == TR_SUCCESS)
  887. {
  888. tr = ReadBriefcaseInfo(hcf, hbr, hhtPathTrans);
  889. if (tr == TR_SUCCESS)
  890. {
  891. HHANDLETRANS hhtNameTrans;
  892. tr = ReadStringTable(hcf, GetBriefcaseNameStringTable(hbr), &hhtNameTrans);
  893. if (tr == TR_SUCCESS)
  894. {
  895. tr = ReadTwinFamilies(hcf, hbr, pcdbver, hhtPathTrans, hhtNameTrans);
  896. if (tr == TR_SUCCESS)
  897. tr = ReadFolderPairList(hcf, hbr, hhtPathTrans, hhtNameTrans);
  898. DestroyHandleTranslator(hhtNameTrans);
  899. }
  900. }
  901. DestroyHandleTranslator(hhtPathTrans);
  902. }
  903. return(tr);
  904. }
  905. TWINRESULT WriteTwinDatabase(HCACHEDFILE hcf, HBRFCASE hbr)
  906. {
  907. TWINRESULT tr;
  908. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  909. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  910. if (! SeekInCachedFile(hcf, 0, FILE_BEGIN))
  911. {
  912. DBHEADER dbh;
  913. /* Set up database header. */
  914. CopyMemory(dbh.rgbyteMagic, MAGIC_HEADER, MAGIC_HEADER_LEN);
  915. dbh.dwcbHeaderLen = sizeof(dbh);
  916. dbh.dwMajorVer = HEADER_MAJOR_VER;
  917. dbh.dwMinorVer = HEADER_MINOR_VER;
  918. tr = WriteDBHeader(hcf, &dbh);
  919. if (tr == TR_SUCCESS)
  920. {
  921. TRACE_OUT((TEXT("WriteTwinDatabase(): Wrote database header version %lu.%lu."),
  922. dbh.dwMajorVer,
  923. dbh.dwMinorVer));
  924. tr = WriteTwinInfo(hcf, hbr);
  925. if (tr == TR_SUCCESS && ! SetEndOfCachedFile(hcf))
  926. tr = TR_BRIEFCASE_WRITE_FAILED;
  927. }
  928. }
  929. else
  930. tr = TR_BRIEFCASE_WRITE_FAILED;
  931. return(tr);
  932. }
  933. TWINRESULT ReadTwinDatabase(HBRFCASE hbr, HCACHEDFILE hcf)
  934. {
  935. TWINRESULT tr;
  936. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  937. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  938. if (! SeekInCachedFile(hcf, 0, FILE_BEGIN))
  939. {
  940. DBHEADER dbh;
  941. tr = ReadDBHeader(hcf, &dbh);
  942. if (tr == TR_SUCCESS)
  943. {
  944. TRACE_OUT((TEXT("ReadTwinDatabase(): Read database header version %lu.%lu."),
  945. dbh.dwMajorVer,
  946. dbh.dwMinorVer));
  947. tr = ReadTwinInfo(hcf, hbr, (PCDBVERSION)&dbh.dwMajorVer);
  948. if (tr == TR_SUCCESS)
  949. ASSERT(GetCachedFilePointerPosition(hcf) == GetCachedFileSize(hcf));
  950. }
  951. }
  952. else
  953. tr = TR_BRIEFCASE_READ_FAILED;
  954. return(tr);
  955. }
  956. TWINRESULT WriteDBSegmentHeader(HCACHEDFILE hcf,
  957. LONG lcbDBSegmentHeaderOffset,
  958. PCVOID pcvSegmentHeader,
  959. UINT ucbSegmentHeaderLen)
  960. {
  961. TWINRESULT tr;
  962. DWORD dwcbStartOffset;
  963. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  964. ASSERT(lcbDBSegmentHeaderOffset >= 0);
  965. ASSERT(ucbSegmentHeaderLen > 0);
  966. ASSERT(IS_VALID_READ_BUFFER_PTR(pcvSegmentHeader, BYTE, ucbSegmentHeaderLen));
  967. dwcbStartOffset = GetCachedFilePointerPosition(hcf);
  968. if (dwcbStartOffset != INVALID_SEEK_POSITION &&
  969. SeekInCachedFile(hcf, lcbDBSegmentHeaderOffset, SEEK_SET) != INVALID_SEEK_POSITION &&
  970. WriteToCachedFile(hcf, pcvSegmentHeader, ucbSegmentHeaderLen, NULL) &&
  971. SeekInCachedFile(hcf, dwcbStartOffset, SEEK_SET) != INVALID_SEEK_POSITION)
  972. tr = TR_SUCCESS;
  973. else
  974. tr = TR_BRIEFCASE_WRITE_FAILED;
  975. return(tr);
  976. }
  977. TWINRESULT TranslateFCRESULTToTWINRESULT(FCRESULT fcr)
  978. {
  979. TWINRESULT tr;
  980. switch (fcr)
  981. {
  982. case FCR_SUCCESS:
  983. tr = TR_SUCCESS;
  984. break;
  985. case FCR_OUT_OF_MEMORY:
  986. tr = TR_OUT_OF_MEMORY;
  987. break;
  988. case FCR_OPEN_FAILED:
  989. tr = TR_BRIEFCASE_OPEN_FAILED;
  990. break;
  991. case FCR_CREATE_FAILED:
  992. tr = TR_BRIEFCASE_OPEN_FAILED;
  993. break;
  994. case FCR_WRITE_FAILED:
  995. tr = TR_BRIEFCASE_WRITE_FAILED;
  996. break;
  997. default:
  998. ASSERT(fcr == FCR_FILE_LOCKED);
  999. tr = TR_BRIEFCASE_LOCKED;
  1000. break;
  1001. }
  1002. return(tr);
  1003. }
  1004. /* Constants
  1005. ************/
  1006. /* last resort default minimum cache size */
  1007. #define DEFAULT_MIN_CACHE_SIZE (32)
  1008. /* Types
  1009. ********/
  1010. /* cached file description structure */
  1011. typedef struct _icachedfile
  1012. {
  1013. /* current position of file pointer in file */
  1014. DWORD dwcbCurFilePosition;
  1015. /* file handle of cached file */
  1016. HANDLE hfile;
  1017. /* file open mode */
  1018. DWORD dwOpenMode;
  1019. /* size of cache in bytes */
  1020. DWORD dwcbCacheSize;
  1021. /* pointer to base of cache */
  1022. PBYTE pbyteCache;
  1023. /* size of default cache in bytes */
  1024. DWORD dwcbDefaultCacheSize;
  1025. /* default cache */
  1026. PBYTE pbyteDefaultCache;
  1027. /* length of file (including data written to cache) */
  1028. DWORD dwcbFileLen;
  1029. /* offset of start of cache in file */
  1030. DWORD dwcbFileOffsetOfCache;
  1031. /* number of valid bytes in cache, starting at beginning of cache */
  1032. DWORD dwcbValid;
  1033. /* number of uncommitted bytes in cache, starting at beginning of cache */
  1034. DWORD dwcbUncommitted;
  1035. /* path of cached file */
  1036. LPTSTR pszPath;
  1037. }
  1038. ICACHEDFILE;
  1039. DECLARE_STANDARD_TYPES(ICACHEDFILE);
  1040. FCRESULT SetUpCachedFile(PCCACHEDFILE, PHCACHEDFILE);
  1041. void BreakDownCachedFile(PICACHEDFILE);
  1042. void ResetCacheToEmpty(PICACHEDFILE);
  1043. DWORD ReadFromCache(PICACHEDFILE, PVOID, DWORD);
  1044. DWORD GetValidReadData(PICACHEDFILE, PBYTE *);
  1045. BOOL FillCache(PICACHEDFILE, PDWORD);
  1046. DWORD WriteToCache(PICACHEDFILE, PCVOID, DWORD);
  1047. DWORD GetAvailableWriteSpace(PICACHEDFILE, PBYTE *);
  1048. BOOL CommitCache(PICACHEDFILE);
  1049. FCRESULT SetUpCachedFile(PCCACHEDFILE pccf, PHCACHEDFILE phcf)
  1050. {
  1051. FCRESULT fcr;
  1052. HANDLE hfNew;
  1053. ASSERT(IS_VALID_STRUCT_PTR(pccf, CCACHEDFILE));
  1054. ASSERT(IS_VALID_WRITE_PTR(phcf, HCACHEDFILE));
  1055. /* Open the file with the requested open and sharing flags. */
  1056. hfNew = CreateFile(pccf->pcszPath, pccf->dwOpenMode, pccf->dwSharingMode,
  1057. pccf->psa, pccf->dwCreateMode, pccf->dwAttrsAndFlags,
  1058. pccf->hTemplateFile);
  1059. if (hfNew != INVALID_HANDLE_VALUE)
  1060. {
  1061. PICACHEDFILE picf;
  1062. fcr = FCR_OUT_OF_MEMORY;
  1063. /* Try to allocate a new cached file structure. */
  1064. if (AllocateMemory(sizeof(*picf), &picf))
  1065. {
  1066. DWORD dwcbDefaultCacheSize;
  1067. /* Allocate the default cache for the cached file. */
  1068. if (pccf->dwcbDefaultCacheSize > 0)
  1069. dwcbDefaultCacheSize = pccf->dwcbDefaultCacheSize;
  1070. else
  1071. {
  1072. dwcbDefaultCacheSize = DEFAULT_MIN_CACHE_SIZE;
  1073. WARNING_OUT((TEXT("SetUpCachedFile(): Using minimum cache size of %lu instead of %lu."),
  1074. dwcbDefaultCacheSize,
  1075. pccf->dwcbDefaultCacheSize));
  1076. }
  1077. if (AllocateMemory(dwcbDefaultCacheSize, &(picf->pbyteDefaultCache)))
  1078. {
  1079. if (StringCopy2(pccf->pcszPath, &(picf->pszPath)))
  1080. {
  1081. DWORD dwcbFileLenHigh;
  1082. picf->dwcbFileLen = GetFileSize(hfNew, &dwcbFileLenHigh);
  1083. if (picf->dwcbFileLen != INVALID_FILE_SIZE && ! dwcbFileLenHigh)
  1084. {
  1085. /* Success! Fill in cached file structure fields. */
  1086. picf->hfile = hfNew;
  1087. picf->dwcbCurFilePosition = 0;
  1088. picf->dwcbCacheSize = dwcbDefaultCacheSize;
  1089. picf->pbyteCache = picf->pbyteDefaultCache;
  1090. picf->dwcbDefaultCacheSize = dwcbDefaultCacheSize;
  1091. picf->dwOpenMode = pccf->dwOpenMode;
  1092. ResetCacheToEmpty(picf);
  1093. *phcf = (HCACHEDFILE)picf;
  1094. fcr = FCR_SUCCESS;
  1095. ASSERT(IS_VALID_HANDLE(*phcf, CACHEDFILE));
  1096. TRACE_OUT((TEXT("SetUpCachedFile(): Created %lu byte default cache for file %s."),
  1097. picf->dwcbCacheSize,
  1098. picf->pszPath));
  1099. }
  1100. else
  1101. {
  1102. fcr = FCR_OPEN_FAILED;
  1103. SETUPCACHEDFILE_BAIL1:
  1104. FreeMemory(picf->pbyteDefaultCache);
  1105. SETUPCACHEDFILE_BAIL2:
  1106. FreeMemory(picf);
  1107. SETUPCACHEDFILE_BAIL3:
  1108. /*
  1109. * Failing to close the file properly is not a failure
  1110. * condition here.
  1111. */
  1112. CloseHandle(hfNew);
  1113. }
  1114. }
  1115. else
  1116. goto SETUPCACHEDFILE_BAIL1;
  1117. }
  1118. else
  1119. goto SETUPCACHEDFILE_BAIL2;
  1120. }
  1121. else
  1122. goto SETUPCACHEDFILE_BAIL3;
  1123. }
  1124. else
  1125. {
  1126. switch (GetLastError())
  1127. {
  1128. /* Returned when file opened by local machine. */
  1129. case ERROR_SHARING_VIOLATION:
  1130. fcr = FCR_FILE_LOCKED;
  1131. break;
  1132. default:
  1133. fcr = FCR_OPEN_FAILED;
  1134. break;
  1135. }
  1136. }
  1137. return(fcr);
  1138. }
  1139. void BreakDownCachedFile(PICACHEDFILE picf)
  1140. {
  1141. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1142. /* Are we using the default cache? */
  1143. if (picf->pbyteCache != picf->pbyteDefaultCache)
  1144. /* No. Free the cache. */
  1145. FreeMemory(picf->pbyteCache);
  1146. /* Free the default cache. */
  1147. FreeMemory(picf->pbyteDefaultCache);
  1148. TRACE_OUT((TEXT("BreakDownCachedFile(): Destroyed cache for file %s."),
  1149. picf->pszPath));
  1150. FreeMemory(picf->pszPath);
  1151. FreeMemory(picf);
  1152. }
  1153. void ResetCacheToEmpty(PICACHEDFILE picf)
  1154. {
  1155. /*
  1156. * Don't fully validate *picf here since we may be called by
  1157. * SetUpCachedFile() before *picf has been set up.
  1158. */
  1159. ASSERT(IS_VALID_WRITE_PTR(picf, ICACHEDFILE));
  1160. picf->dwcbFileOffsetOfCache = picf->dwcbCurFilePosition;
  1161. picf->dwcbValid = 0;
  1162. picf->dwcbUncommitted = 0;
  1163. }
  1164. DWORD ReadFromCache(PICACHEDFILE picf, PVOID hpbyteBuffer, DWORD dwcb)
  1165. {
  1166. DWORD dwcbRead;
  1167. PBYTE pbyteStart;
  1168. DWORD dwcbValid;
  1169. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1170. ASSERT(IS_VALID_WRITE_BUFFER_PTR(hpbyteBuffer, BYTE, (UINT)dwcb));
  1171. ASSERT(IS_FLAG_SET(picf->dwOpenMode, GENERIC_READ));
  1172. ASSERT(dwcb > 0);
  1173. /* Is there any valid data that can be read from the cache? */
  1174. dwcbValid = GetValidReadData(picf, &pbyteStart);
  1175. if (dwcbValid > 0)
  1176. {
  1177. /* Yes. Copy it into the buffer. */
  1178. dwcbRead = min(dwcbValid, dwcb);
  1179. CopyMemory(hpbyteBuffer, pbyteStart, dwcbRead);
  1180. picf->dwcbCurFilePosition += dwcbRead;
  1181. }
  1182. else
  1183. dwcbRead = 0;
  1184. return(dwcbRead);
  1185. }
  1186. DWORD GetValidReadData(PICACHEDFILE picf, PBYTE *ppbyteStart)
  1187. {
  1188. DWORD dwcbValid;
  1189. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1190. ASSERT(IS_VALID_WRITE_PTR(ppbyteStart, PBYTE *));
  1191. ASSERT(IS_FLAG_SET(picf->dwOpenMode, GENERIC_READ));
  1192. /* Is there any valid read data in the cache? */
  1193. /* The current file position must be inside the valid data in the cache. */
  1194. /* Watch out for overflow. */
  1195. ASSERT(picf->dwcbFileOffsetOfCache <= DWORD_MAX - picf->dwcbValid);
  1196. if (picf->dwcbCurFilePosition >= picf->dwcbFileOffsetOfCache &&
  1197. picf->dwcbCurFilePosition < picf->dwcbFileOffsetOfCache + picf->dwcbValid)
  1198. {
  1199. DWORD dwcbStartBias;
  1200. /* Yes. */
  1201. dwcbStartBias = picf->dwcbCurFilePosition - picf->dwcbFileOffsetOfCache;
  1202. *ppbyteStart = picf->pbyteCache + dwcbStartBias;
  1203. /* The second clause above protects against underflow here. */
  1204. dwcbValid = picf->dwcbValid - dwcbStartBias;
  1205. }
  1206. else
  1207. /* No. */
  1208. dwcbValid = 0;
  1209. return(dwcbValid);
  1210. }
  1211. BOOL FillCache(PICACHEDFILE picf, PDWORD pdwcbNewData)
  1212. {
  1213. BOOL bResult = FALSE;
  1214. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1215. ASSERT(IS_VALID_WRITE_PTR(pdwcbNewData, DWORD));
  1216. ASSERT(IS_FLAG_SET(picf->dwOpenMode, GENERIC_READ));
  1217. if (CommitCache(picf))
  1218. {
  1219. DWORD dwcbOffset;
  1220. ResetCacheToEmpty(picf);
  1221. /* Seek to start position. */
  1222. dwcbOffset = SetFilePointer(picf->hfile, picf->dwcbCurFilePosition, NULL, FILE_BEGIN);
  1223. if (dwcbOffset != INVALID_SEEK_POSITION)
  1224. {
  1225. DWORD dwcbRead;
  1226. ASSERT(dwcbOffset == picf->dwcbCurFilePosition);
  1227. /* Fill cache from file. */
  1228. if (ReadFile(picf->hfile, picf->pbyteCache, picf->dwcbCacheSize, &dwcbRead, NULL))
  1229. {
  1230. picf->dwcbValid = dwcbRead;
  1231. *pdwcbNewData = dwcbRead;
  1232. bResult = TRUE;
  1233. TRACE_OUT((TEXT("FillCache(): Read %lu bytes into cache starting at offset %lu in file %s."),
  1234. dwcbRead,
  1235. dwcbOffset,
  1236. picf->pszPath));
  1237. }
  1238. }
  1239. }
  1240. return(bResult);
  1241. }
  1242. DWORD WriteToCache(PICACHEDFILE picf, PCVOID hpbyteBuffer, DWORD dwcb)
  1243. {
  1244. DWORD dwcbAvailable;
  1245. PBYTE pbyteStart;
  1246. DWORD dwcbWritten;
  1247. DWORD dwcbNewUncommitted;
  1248. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1249. ASSERT(IS_VALID_READ_BUFFER_PTR(hpbyteBuffer, BYTE, (UINT)dwcb));
  1250. ASSERT(IS_FLAG_SET(picf->dwOpenMode, GENERIC_WRITE));
  1251. ASSERT(dwcb > 0);
  1252. /* Is there any room left to write data into the cache? */
  1253. dwcbAvailable = GetAvailableWriteSpace(picf, &pbyteStart);
  1254. /* Yes. Determine how much to copy into cache. */
  1255. dwcbWritten = min(dwcbAvailable, dwcb);
  1256. /* Can we write anything into the cache? */
  1257. if (dwcbWritten > 0)
  1258. {
  1259. /* Yes. Write it. */
  1260. CopyMemory(pbyteStart, hpbyteBuffer, dwcbWritten);
  1261. /* Watch out for overflow. */
  1262. ASSERT(picf->dwcbCurFilePosition <= DWORD_MAX - dwcbWritten);
  1263. picf->dwcbCurFilePosition += dwcbWritten;
  1264. /* Watch out for underflow. */
  1265. ASSERT(picf->dwcbCurFilePosition >= picf->dwcbFileOffsetOfCache);
  1266. dwcbNewUncommitted = picf->dwcbCurFilePosition - picf->dwcbFileOffsetOfCache;
  1267. if (picf->dwcbUncommitted < dwcbNewUncommitted)
  1268. picf->dwcbUncommitted = dwcbNewUncommitted;
  1269. if (picf->dwcbValid < dwcbNewUncommitted)
  1270. {
  1271. DWORD dwcbNewFileLen;
  1272. picf->dwcbValid = dwcbNewUncommitted;
  1273. /* Watch out for overflow. */
  1274. ASSERT(picf->dwcbFileOffsetOfCache <= DWORD_MAX - dwcbNewUncommitted);
  1275. dwcbNewFileLen = picf->dwcbFileOffsetOfCache + dwcbNewUncommitted;
  1276. if (picf->dwcbFileLen < dwcbNewFileLen)
  1277. picf->dwcbFileLen = dwcbNewFileLen;
  1278. }
  1279. }
  1280. return(dwcbWritten);
  1281. }
  1282. DWORD GetAvailableWriteSpace(PICACHEDFILE picf, PBYTE *ppbyteStart)
  1283. {
  1284. DWORD dwcbAvailable;
  1285. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1286. ASSERT(IS_VALID_WRITE_PTR(ppbyteStart, PBYTE *));
  1287. ASSERT(IS_FLAG_SET(picf->dwOpenMode, GENERIC_WRITE));
  1288. /* Is there room to write data in the cache? */
  1289. /*
  1290. * The current file position must be inside or just after the end of the
  1291. * valid data in the cache, or at the front of the cache when there is no
  1292. * valid data in the cache.
  1293. */
  1294. /* Watch out for overflow. */
  1295. ASSERT(picf->dwcbFileOffsetOfCache <= DWORD_MAX - picf->dwcbValid);
  1296. if (picf->dwcbCurFilePosition >= picf->dwcbFileOffsetOfCache &&
  1297. picf->dwcbCurFilePosition <= picf->dwcbFileOffsetOfCache + picf->dwcbValid)
  1298. {
  1299. DWORD dwcbStartBias;
  1300. /* Yes. */
  1301. dwcbStartBias = picf->dwcbCurFilePosition - picf->dwcbFileOffsetOfCache;
  1302. *ppbyteStart = picf->pbyteCache + dwcbStartBias;
  1303. /* Watch out for underflow. */
  1304. ASSERT(picf->dwcbCacheSize >= dwcbStartBias);
  1305. dwcbAvailable = picf->dwcbCacheSize - dwcbStartBias;
  1306. }
  1307. else
  1308. /* No. */
  1309. dwcbAvailable = 0;
  1310. return(dwcbAvailable);
  1311. }
  1312. BOOL CommitCache(PICACHEDFILE picf)
  1313. {
  1314. BOOL bResult;
  1315. ASSERT(IS_VALID_STRUCT_PTR(picf, CICACHEDFILE));
  1316. /* Any data to commit? */
  1317. if (IS_FLAG_SET(picf->dwOpenMode, GENERIC_WRITE) &&
  1318. picf->dwcbUncommitted > 0)
  1319. {
  1320. DWORD dwcbOffset;
  1321. /* Yes. Seek to start position of cache in file. */
  1322. bResult = FALSE;
  1323. dwcbOffset = SetFilePointer(picf->hfile, picf->dwcbFileOffsetOfCache, NULL, FILE_BEGIN);
  1324. if (dwcbOffset != INVALID_SEEK_POSITION)
  1325. {
  1326. DWORD dwcbWritten;
  1327. ASSERT(dwcbOffset == picf->dwcbFileOffsetOfCache);
  1328. /* Write to file from cache. */
  1329. if (WriteFile(picf->hfile, picf->pbyteCache, picf->dwcbUncommitted, &dwcbWritten, NULL) &&
  1330. dwcbWritten == picf->dwcbUncommitted)
  1331. {
  1332. TRACE_OUT((TEXT("CommitCache(): Committed %lu uncommitted bytes starting at offset %lu in file %s."),
  1333. dwcbWritten,
  1334. dwcbOffset,
  1335. picf->pszPath));
  1336. bResult = TRUE;
  1337. }
  1338. }
  1339. }
  1340. else
  1341. bResult = TRUE;
  1342. return(bResult);
  1343. }
  1344. FCRESULT CreateCachedFile(PCCACHEDFILE pccf, PHCACHEDFILE phcf)
  1345. {
  1346. ASSERT(IS_VALID_STRUCT_PTR(pccf, CCACHEDFILE));
  1347. ASSERT(IS_VALID_WRITE_PTR(phcf, HCACHEDFILE));
  1348. return(SetUpCachedFile(pccf, phcf));
  1349. }
  1350. FCRESULT SetCachedFileCacheSize(HCACHEDFILE hcf, DWORD dwcbNewCacheSize)
  1351. {
  1352. FCRESULT fcr;
  1353. /* dwcbNewCacheSize may be any value here. */
  1354. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1355. /* Use default cache size instead of 0. */
  1356. if (! dwcbNewCacheSize)
  1357. {
  1358. ASSERT(((PICACHEDFILE)hcf)->dwcbDefaultCacheSize > 0);
  1359. dwcbNewCacheSize = ((PICACHEDFILE)hcf)->dwcbDefaultCacheSize;
  1360. }
  1361. /* Is the cache size changing? */
  1362. if (dwcbNewCacheSize == ((PICACHEDFILE)hcf)->dwcbCacheSize)
  1363. /* No. Whine about it. */
  1364. WARNING_OUT((TEXT("SetCachedFileCacheSize(): Cache size is already %lu bytes."),
  1365. dwcbNewCacheSize));
  1366. /* Commit the cache so we can change its size. */
  1367. if (CommitCache((PICACHEDFILE)hcf))
  1368. {
  1369. PBYTE pbyteNewCache;
  1370. /* Throw away cached data. */
  1371. ResetCacheToEmpty((PICACHEDFILE)hcf);
  1372. /* Do we need to allocate a new cache? */
  1373. if (dwcbNewCacheSize <= ((PICACHEDFILE)hcf)->dwcbDefaultCacheSize)
  1374. {
  1375. /* No. */
  1376. pbyteNewCache = ((PICACHEDFILE)hcf)->pbyteDefaultCache;
  1377. fcr = FCR_SUCCESS;
  1378. TRACE_OUT((TEXT("SetCachedFileCacheSize(): Using %lu bytes of %lu bytes allocated to default cache."),
  1379. dwcbNewCacheSize,
  1380. ((PICACHEDFILE)hcf)->dwcbDefaultCacheSize));
  1381. }
  1382. else
  1383. {
  1384. /* Yes. */
  1385. if (AllocateMemory(dwcbNewCacheSize, &pbyteNewCache))
  1386. {
  1387. fcr = FCR_SUCCESS;
  1388. TRACE_OUT((TEXT("SetCachedFileCacheSize(): Allocated %lu bytes for new cache."),
  1389. dwcbNewCacheSize));
  1390. }
  1391. else
  1392. fcr = FCR_OUT_OF_MEMORY;
  1393. }
  1394. if (fcr == FCR_SUCCESS)
  1395. {
  1396. /* Do we need to free the old cache? */
  1397. if (((PICACHEDFILE)hcf)->pbyteCache != ((PICACHEDFILE)hcf)->pbyteDefaultCache)
  1398. {
  1399. /* Yes. */
  1400. ASSERT(((PICACHEDFILE)hcf)->dwcbCacheSize > ((PICACHEDFILE)hcf)->dwcbDefaultCacheSize);
  1401. FreeMemory(((PICACHEDFILE)hcf)->pbyteCache);
  1402. }
  1403. /* Use new cache. */
  1404. ((PICACHEDFILE)hcf)->pbyteCache = pbyteNewCache;
  1405. ((PICACHEDFILE)hcf)->dwcbCacheSize = dwcbNewCacheSize;
  1406. }
  1407. }
  1408. else
  1409. fcr = FCR_WRITE_FAILED;
  1410. return(fcr);
  1411. }
  1412. DWORD SeekInCachedFile(HCACHEDFILE hcf, DWORD dwcbSeek, DWORD uOrigin)
  1413. {
  1414. DWORD dwcbResult;
  1415. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1416. ASSERT(uOrigin == FILE_BEGIN || uOrigin == FILE_CURRENT || uOrigin == FILE_END);
  1417. {
  1418. BOOL bValidTarget = TRUE;
  1419. DWORD dwcbWorkingOffset = 0;
  1420. /* Determine seek base. */
  1421. switch (uOrigin)
  1422. {
  1423. case SEEK_CUR:
  1424. dwcbWorkingOffset = ((PICACHEDFILE)hcf)->dwcbCurFilePosition;
  1425. break;
  1426. case SEEK_SET:
  1427. break;
  1428. case SEEK_END:
  1429. dwcbWorkingOffset = ((PICACHEDFILE)hcf)->dwcbFileLen;
  1430. break;
  1431. default:
  1432. bValidTarget = FALSE;
  1433. break;
  1434. }
  1435. if (bValidTarget)
  1436. {
  1437. /* Add bias. */
  1438. /* Watch out for overflow. */
  1439. ASSERT(dwcbWorkingOffset <= DWORD_MAX - dwcbSeek);
  1440. dwcbWorkingOffset += dwcbSeek;
  1441. ((PICACHEDFILE)hcf)->dwcbCurFilePosition = dwcbWorkingOffset;
  1442. dwcbResult = dwcbWorkingOffset;
  1443. }
  1444. else
  1445. dwcbResult = INVALID_SEEK_POSITION;
  1446. }
  1447. return(dwcbResult);
  1448. }
  1449. BOOL SetEndOfCachedFile(HCACHEDFILE hcf)
  1450. {
  1451. BOOL bResult;
  1452. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1453. bResult = CommitCache((PICACHEDFILE)hcf);
  1454. if (bResult)
  1455. {
  1456. bResult = (SetFilePointer(((PICACHEDFILE)hcf)->hfile,
  1457. ((PICACHEDFILE)hcf)->dwcbCurFilePosition, NULL,
  1458. FILE_BEGIN) ==
  1459. ((PICACHEDFILE)hcf)->dwcbCurFilePosition);
  1460. if (bResult)
  1461. {
  1462. bResult = SetEndOfFile(((PICACHEDFILE)hcf)->hfile);
  1463. if (bResult)
  1464. {
  1465. ResetCacheToEmpty((PICACHEDFILE)hcf);
  1466. ((PICACHEDFILE)hcf)->dwcbFileLen = ((PICACHEDFILE)hcf)->dwcbCurFilePosition;
  1467. #ifdef DEBUG
  1468. {
  1469. DWORD dwcbFileSizeHigh;
  1470. DWORD dwcbFileSizeLow;
  1471. dwcbFileSizeLow = GetFileSize(((PICACHEDFILE)hcf)->hfile, &dwcbFileSizeHigh);
  1472. ASSERT(! dwcbFileSizeHigh);
  1473. ASSERT(((PICACHEDFILE)hcf)->dwcbFileLen == dwcbFileSizeLow);
  1474. ASSERT(((PICACHEDFILE)hcf)->dwcbCurFilePosition == dwcbFileSizeLow);
  1475. }
  1476. #endif
  1477. }
  1478. }
  1479. }
  1480. return(bResult);
  1481. }
  1482. DWORD GetCachedFilePointerPosition(HCACHEDFILE hcf)
  1483. {
  1484. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1485. return(((PICACHEDFILE)hcf)->dwcbCurFilePosition);
  1486. }
  1487. DWORD GetCachedFileSize(HCACHEDFILE hcf)
  1488. {
  1489. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1490. return(((PICACHEDFILE)hcf)->dwcbFileLen);
  1491. }
  1492. BOOL ReadFromCachedFile(HCACHEDFILE hcf, PVOID hpbyteBuffer, DWORD dwcb,
  1493. PDWORD pdwcbRead)
  1494. {
  1495. BOOL bResult;
  1496. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1497. ASSERT(IS_VALID_WRITE_BUFFER_PTR(hpbyteBuffer, BYTE, (UINT)dwcb));
  1498. ASSERT(! pdwcbRead || IS_VALID_WRITE_PTR(pdwcbRead, DWORD));
  1499. *pdwcbRead = 0;
  1500. /*
  1501. * Make sure that the cached file has been set up for read access before
  1502. * allowing a read.
  1503. */
  1504. if (IS_FLAG_SET(((PICACHEDFILE)hcf)->dwOpenMode, GENERIC_READ))
  1505. {
  1506. DWORD dwcbToRead = dwcb;
  1507. /* Read requested data. */
  1508. bResult = TRUE;
  1509. while (dwcbToRead > 0)
  1510. {
  1511. DWORD dwcbRead;
  1512. dwcbRead = ReadFromCache((PICACHEDFILE)hcf, hpbyteBuffer, dwcbToRead);
  1513. /* Watch out for underflow. */
  1514. ASSERT(dwcbRead <= dwcbToRead);
  1515. dwcbToRead -= dwcbRead;
  1516. if (dwcbToRead > 0)
  1517. {
  1518. DWORD dwcbNewData;
  1519. if (FillCache((PICACHEDFILE)hcf, &dwcbNewData))
  1520. {
  1521. hpbyteBuffer = (PBYTE)hpbyteBuffer + dwcbRead;
  1522. if (! dwcbNewData)
  1523. break;
  1524. }
  1525. else
  1526. {
  1527. bResult = FALSE;
  1528. break;
  1529. }
  1530. }
  1531. }
  1532. /* Watch out for underflow. */
  1533. ASSERT(dwcb >= dwcbToRead);
  1534. if (bResult && pdwcbRead)
  1535. *pdwcbRead = dwcb - dwcbToRead;
  1536. }
  1537. else
  1538. bResult = FALSE;
  1539. ASSERT(! pdwcbRead ||
  1540. ((bResult && *pdwcbRead <= dwcb) ||
  1541. (! bResult && ! *pdwcbRead)));
  1542. return(bResult);
  1543. }
  1544. BOOL WriteToCachedFile(HCACHEDFILE hcf, PCVOID hpbyteBuffer, DWORD dwcb,
  1545. PDWORD pdwcbWritten)
  1546. {
  1547. BOOL bResult;
  1548. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1549. ASSERT(IS_VALID_READ_BUFFER_PTR(hpbyteBuffer, BYTE, (UINT)dwcb));
  1550. ASSERT(dwcb > 0);
  1551. /*
  1552. * Make sure that the cached file has been set up for write access before
  1553. * allowing a write.
  1554. */
  1555. if (IS_FLAG_SET(((PICACHEDFILE)hcf)->dwOpenMode, GENERIC_WRITE))
  1556. {
  1557. DWORD dwcbToWrite = dwcb;
  1558. /* Write requested data. */
  1559. bResult = TRUE;
  1560. while (dwcbToWrite > 0)
  1561. {
  1562. DWORD dwcbWritten;
  1563. dwcbWritten = WriteToCache((PICACHEDFILE)hcf, hpbyteBuffer, dwcbToWrite);
  1564. /* Watch out for underflow. */
  1565. ASSERT(dwcbWritten <= dwcbToWrite);
  1566. dwcbToWrite -= dwcbWritten;
  1567. if (dwcbToWrite > 0)
  1568. {
  1569. if (CommitCache((PICACHEDFILE)hcf))
  1570. {
  1571. ResetCacheToEmpty((PICACHEDFILE)hcf);
  1572. hpbyteBuffer = (PCBYTE)hpbyteBuffer + dwcbWritten;
  1573. }
  1574. else
  1575. {
  1576. bResult = FALSE;
  1577. break;
  1578. }
  1579. }
  1580. }
  1581. ASSERT(dwcb >= dwcbToWrite);
  1582. if (pdwcbWritten)
  1583. {
  1584. if (bResult)
  1585. {
  1586. ASSERT(! dwcbToWrite);
  1587. *pdwcbWritten = dwcb;
  1588. }
  1589. else
  1590. *pdwcbWritten = 0;
  1591. }
  1592. }
  1593. else
  1594. bResult = FALSE;
  1595. ASSERT(! pdwcbWritten ||
  1596. ((bResult && *pdwcbWritten == dwcb) ||
  1597. (! bResult && ! *pdwcbWritten)));
  1598. return(bResult);
  1599. }
  1600. BOOL CommitCachedFile(HCACHEDFILE hcf)
  1601. {
  1602. BOOL bResult;
  1603. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1604. /*
  1605. * Make sure that the cached file has been set up for write access before
  1606. * allowing a commit.
  1607. */
  1608. if (IS_FLAG_SET(((PICACHEDFILE)hcf)->dwOpenMode, GENERIC_WRITE))
  1609. bResult = CommitCache((PICACHEDFILE)hcf);
  1610. else
  1611. bResult = FALSE;
  1612. return(bResult);
  1613. }
  1614. HANDLE GetFileHandle(HCACHEDFILE hcf)
  1615. {
  1616. HANDLE hfResult;
  1617. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1618. hfResult = ((PCICACHEDFILE)hcf)->hfile;
  1619. return(hfResult);
  1620. }
  1621. BOOL CloseCachedFile(HCACHEDFILE hcf)
  1622. {
  1623. BOOL bResult;
  1624. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  1625. {
  1626. BOOL bCommit;
  1627. BOOL bClose;
  1628. bCommit = CommitCache((PICACHEDFILE)hcf);
  1629. bClose = CloseHandle(((PCICACHEDFILE)hcf)->hfile);
  1630. BreakDownCachedFile((PICACHEDFILE)hcf);
  1631. bResult = bCommit && bClose;
  1632. }
  1633. return(bResult);
  1634. }
  1635. /* Constants
  1636. ************/
  1637. /* pointer array allocation constants */
  1638. #define NUM_START_FOLDER_TWIN_PTRS (16)
  1639. #define NUM_FOLDER_TWIN_PTRS_TO_ADD (16)
  1640. /* Types
  1641. ********/
  1642. /* internal new folder twin description */
  1643. typedef struct _inewfoldertwin
  1644. {
  1645. HPATH hpathFirst;
  1646. HPATH hpathSecond;
  1647. HSTRING hsName;
  1648. DWORD dwAttributes;
  1649. HBRFCASE hbr;
  1650. DWORD dwFlags;
  1651. }
  1652. INEWFOLDERTWIN;
  1653. DECLARE_STANDARD_TYPES(INEWFOLDERTWIN);
  1654. /* database folder twin list header */
  1655. typedef struct _dbfoldertwinlistheader
  1656. {
  1657. LONG lcFolderPairs;
  1658. }
  1659. DBFOLDERTWINLISTHEADER;
  1660. DECLARE_STANDARD_TYPES(DBFOLDERTWINLISTHEADER);
  1661. /* database folder twin structure */
  1662. typedef struct _dbfoldertwin
  1663. {
  1664. /* shared stub flags */
  1665. DWORD dwStubFlags;
  1666. /* old handle to first folder path */
  1667. HPATH hpath1;
  1668. /* old handle to second folder path */
  1669. HPATH hpath2;
  1670. /* old handle to name string */
  1671. HSTRING hsName;
  1672. /* attributes to match */
  1673. DWORD dwAttributes;
  1674. }
  1675. DBFOLDERTWIN;
  1676. DECLARE_STANDARD_TYPES(DBFOLDERTWIN);
  1677. TWINRESULT TwinFolders(PCINEWFOLDERTWIN, PFOLDERPAIR *);
  1678. BOOL CreateFolderPair(PCINEWFOLDERTWIN, PFOLDERPAIR *);
  1679. BOOL CreateHalfOfFolderPair(HPATH, HBRFCASE, PFOLDERPAIR *);
  1680. void DestroyHalfOfFolderPair(PFOLDERPAIR);
  1681. BOOL CreateSharedFolderPairData(PCINEWFOLDERTWIN, PFOLDERPAIRDATA *);
  1682. void DestroySharedFolderPairData(PFOLDERPAIRDATA);
  1683. COMPARISONRESULT FolderPairSortCmp(PCVOID, PCVOID);
  1684. COMPARISONRESULT FolderPairSearchCmp(PCVOID, PCVOID);
  1685. BOOL RemoveSourceFolderTwin(POBJECTTWIN, PVOID);
  1686. void UnlinkHalfOfFolderPair(PFOLDERPAIR);
  1687. BOOL FolderTwinIntersectsFolder(PCFOLDERPAIR, HPATH);
  1688. TWINRESULT CreateListOfFolderTwins(HBRFCASE, ARRAYINDEX, HPATH, PFOLDERTWIN *, PARRAYINDEX);
  1689. void DestroyListOfFolderTwins(PFOLDERTWIN);
  1690. TWINRESULT AddFolderTwinToList(PFOLDERPAIR, PFOLDERTWIN, PFOLDERTWIN *);
  1691. TWINRESULT WriteFolderPair(HCACHEDFILE, PFOLDERPAIR);
  1692. TWINRESULT ReadFolderPair(HCACHEDFILE, HBRFCASE, HHANDLETRANS, HHANDLETRANS);
  1693. /* Macros
  1694. *********/
  1695. /* name component macros used by NameComponentsIntersect() */
  1696. #define COMPONENT_CHARS_MATCH(ch1, ch2) (CharLower((PTSTR)(DWORD)ch1) == CharLower((PTSTR)(DWORD)ch2) || (ch1) == QMARK || (ch2) == QMARK)
  1697. #define IS_COMPONENT_TERMINATOR(ch) (! (ch) || (ch) == PERIOD || (ch) == ASTERISK)
  1698. BOOL NameComponentsIntersect(LPCTSTR pcszComponent1,
  1699. LPCTSTR pcszComponent2)
  1700. {
  1701. BOOL bIntersect;
  1702. ASSERT(IS_VALID_STRING_PTR(pcszComponent1, CSTR));
  1703. ASSERT(IS_VALID_STRING_PTR(pcszComponent2, CSTR));
  1704. while (! IS_COMPONENT_TERMINATOR(*pcszComponent1) && ! IS_COMPONENT_TERMINATOR(*pcszComponent2) &&
  1705. COMPONENT_CHARS_MATCH(*pcszComponent1, *pcszComponent2))
  1706. {
  1707. pcszComponent1 = CharNext(pcszComponent1);
  1708. pcszComponent2 = CharNext(pcszComponent2);
  1709. }
  1710. if (*pcszComponent1 == ASTERISK ||
  1711. *pcszComponent2 == ASTERISK ||
  1712. *pcszComponent1 == *pcszComponent2)
  1713. bIntersect = TRUE;
  1714. else
  1715. {
  1716. LPCTSTR pcszTrailer;
  1717. if (! *pcszComponent1 || *pcszComponent1 == PERIOD)
  1718. pcszTrailer = pcszComponent2;
  1719. else
  1720. pcszTrailer = pcszComponent1;
  1721. while (*pcszTrailer == QMARK)
  1722. pcszTrailer++;
  1723. if (IS_COMPONENT_TERMINATOR(*pcszTrailer))
  1724. bIntersect = TRUE;
  1725. else
  1726. bIntersect = FALSE;
  1727. }
  1728. return(bIntersect);
  1729. }
  1730. BOOL NamesIntersect(LPCTSTR pcszName1, LPCTSTR pcszName2)
  1731. {
  1732. BOOL bIntersect = FALSE;
  1733. ASSERT(IS_VALID_STRING_PTR(pcszName1, CSTR));
  1734. ASSERT(IS_VALID_STRING_PTR(pcszName2, CSTR));
  1735. if (NameComponentsIntersect(pcszName1, pcszName2))
  1736. {
  1737. LPCTSTR pcszExt1;
  1738. LPCTSTR pcszExt2;
  1739. /* Get extensions, skipping leading periods. */
  1740. pcszExt1 = ExtractExtension(pcszName1);
  1741. if (*pcszExt1 == PERIOD)
  1742. pcszExt1 = CharNext(pcszExt1);
  1743. pcszExt2 = ExtractExtension(pcszName2);
  1744. if (*pcszExt2 == PERIOD)
  1745. pcszExt2 = CharNext(pcszExt2);
  1746. bIntersect = NameComponentsIntersect(pcszExt1, pcszExt2);
  1747. }
  1748. return(bIntersect);
  1749. }
  1750. void ClearFlagInArrayOfStubs(HPTRARRAY hpa, DWORD dwClearFlags)
  1751. {
  1752. ARRAYINDEX aicPtrs;
  1753. ARRAYINDEX ai;
  1754. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  1755. ASSERT(FLAGS_ARE_VALID(dwClearFlags, ALL_STUB_FLAGS));
  1756. aicPtrs = GetPtrCount(hpa);
  1757. for (ai = 0; ai < aicPtrs; ai++)
  1758. ClearStubFlag(GetPtr(hpa, ai), dwClearFlags);
  1759. }
  1760. /*
  1761. ** CreateFolderPair()
  1762. **
  1763. ** Creates a new folder pair, and adds them to a briefcase's list of folder
  1764. ** pairs.
  1765. **
  1766. ** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair to
  1767. ** create
  1768. ** ppfp - pointer to PFOLDERPAIR to be filled in with pointer to
  1769. ** half of new folder pair representing
  1770. ** pcnft->pcszFolder1
  1771. **
  1772. ** Returns:
  1773. **
  1774. ** Side Effects: Adds the new folder pair to the global array of folder pairs.
  1775. **
  1776. ** N.b., this function does not first check to see if the folder pair already
  1777. ** exists in the global list of folder pairs.
  1778. */
  1779. BOOL CreateFolderPair(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp)
  1780. {
  1781. BOOL bResult = FALSE;
  1782. PFOLDERPAIRDATA pfpd;
  1783. ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN));
  1784. ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
  1785. /* Try to create the shared folder data structure. */
  1786. if (CreateSharedFolderPairData(pcinft, &pfpd))
  1787. {
  1788. PFOLDERPAIR pfpNew1;
  1789. BOOL bPtr1Loose = TRUE;
  1790. if (CreateHalfOfFolderPair(pcinft->hpathFirst, pcinft->hbr, &pfpNew1))
  1791. {
  1792. PFOLDERPAIR pfpNew2;
  1793. if (CreateHalfOfFolderPair(pcinft->hpathSecond, pcinft->hbr,
  1794. &pfpNew2))
  1795. {
  1796. HPTRARRAY hpaFolderPairs;
  1797. ARRAYINDEX ai1;
  1798. /* Combine the two folder pair halves. */
  1799. pfpNew1->pfpd = pfpd;
  1800. pfpNew1->pfpOther = pfpNew2;
  1801. pfpNew2->pfpd = pfpd;
  1802. pfpNew2->pfpOther = pfpNew1;
  1803. /* Set flags. */
  1804. if (IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE))
  1805. {
  1806. SetStubFlag(&(pfpNew1->stub), STUB_FL_SUBTREE);
  1807. SetStubFlag(&(pfpNew2->stub), STUB_FL_SUBTREE);
  1808. }
  1809. /*
  1810. * Try to add the two folder pairs to the global list of folder
  1811. * pairs.
  1812. */
  1813. hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcinft->hbr);
  1814. if (AddPtr(hpaFolderPairs, FolderPairSortCmp, pfpNew1, &ai1))
  1815. {
  1816. ARRAYINDEX ai2;
  1817. bPtr1Loose = FALSE;
  1818. if (AddPtr(hpaFolderPairs, FolderPairSortCmp, pfpNew2, &ai2))
  1819. {
  1820. ASSERT(IS_VALID_STRUCT_PTR(pfpNew1, CFOLDERPAIR));
  1821. ASSERT(IS_VALID_STRUCT_PTR(pfpNew2, CFOLDERPAIR));
  1822. if (ApplyNewFolderTwinsToTwinFamilies(pfpNew1))
  1823. {
  1824. *ppfp = pfpNew1;
  1825. bResult = TRUE;
  1826. }
  1827. else
  1828. {
  1829. DeletePtr(hpaFolderPairs, ai2);
  1830. CREATEFOLDERPAIR_BAIL1:
  1831. DeletePtr(hpaFolderPairs, ai1);
  1832. CREATEFOLDERPAIR_BAIL2:
  1833. /*
  1834. * Don't try to remove pfpNew2 from the global list of
  1835. * folder pairs here since it was never added
  1836. * successfully.
  1837. */
  1838. DestroyHalfOfFolderPair(pfpNew2);
  1839. CREATEFOLDERPAIR_BAIL3:
  1840. /*
  1841. * Don't try to remove pfpNew1 from the global list of
  1842. * folder pairs here since it was never added
  1843. * successfully.
  1844. */
  1845. DestroyHalfOfFolderPair(pfpNew1);
  1846. CREATEFOLDERPAIR_BAIL4:
  1847. DestroySharedFolderPairData(pfpd);
  1848. }
  1849. }
  1850. else
  1851. goto CREATEFOLDERPAIR_BAIL1;
  1852. }
  1853. else
  1854. goto CREATEFOLDERPAIR_BAIL2;
  1855. }
  1856. else
  1857. goto CREATEFOLDERPAIR_BAIL3;
  1858. }
  1859. else
  1860. goto CREATEFOLDERPAIR_BAIL4;
  1861. }
  1862. return(bResult);
  1863. }
  1864. BOOL CreateHalfOfFolderPair(HPATH hpathFolder, HBRFCASE hbr,
  1865. PFOLDERPAIR *ppfp)
  1866. {
  1867. BOOL bResult = FALSE;
  1868. PFOLDERPAIR pfpNew;
  1869. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  1870. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  1871. ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
  1872. /* Try to create a new FOLDERPAIR structure. */
  1873. if (AllocateMemory(sizeof(*pfpNew), &pfpNew))
  1874. {
  1875. /* Try to add the folder string to the folder string table. */
  1876. if (CopyPath(hpathFolder, GetBriefcasePathList(hbr), &(pfpNew->hpath)))
  1877. {
  1878. /* Fill in the fields of the new FOLDERPAIR structure. */
  1879. InitStub(&(pfpNew->stub), ST_FOLDERPAIR);
  1880. *ppfp = pfpNew;
  1881. bResult = TRUE;
  1882. }
  1883. else
  1884. FreeMemory(pfpNew);
  1885. }
  1886. return(bResult);
  1887. }
  1888. void DestroyHalfOfFolderPair(PFOLDERPAIR pfp)
  1889. {
  1890. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  1891. TRACE_OUT((TEXT("DestroyHalfOfFolderPair(): Destroying folder twin %s."),
  1892. DebugGetPathString(pfp->hpath)));
  1893. /* Has the other half of this folder pair already been destroyed? */
  1894. if (IsStubFlagClear(&(pfp->stub), STUB_FL_BEING_DELETED))
  1895. /* No. Indicate that this half has already been deleted. */
  1896. SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_BEING_DELETED);
  1897. /* Destroy FOLDERPAIR fields. */
  1898. DeletePath(pfp->hpath);
  1899. FreeMemory(pfp);
  1900. }
  1901. BOOL CreateSharedFolderPairData(PCINEWFOLDERTWIN pcinft,
  1902. PFOLDERPAIRDATA *ppfpd)
  1903. {
  1904. PFOLDERPAIRDATA pfpd;
  1905. ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN));
  1906. ASSERT(IS_VALID_WRITE_PTR(ppfpd, PFOLDERPAIRDATA));
  1907. /* Try to allocate a new shared folder pair data data structure. */
  1908. *ppfpd = NULL;
  1909. if (AllocateMemory(sizeof(*pfpd), &pfpd))
  1910. {
  1911. /* Fill in the FOLDERPAIRDATA structure fields. */
  1912. LockString(pcinft->hsName);
  1913. pfpd->hsName = pcinft->hsName;
  1914. pfpd->dwAttributes = pcinft->dwAttributes;
  1915. pfpd->hbr = pcinft->hbr;
  1916. ASSERT(! IS_ATTR_DIR(pfpd->dwAttributes));
  1917. CLEAR_FLAG(pfpd->dwAttributes, FILE_ATTRIBUTE_DIRECTORY);
  1918. *ppfpd = pfpd;
  1919. ASSERT(IS_VALID_STRUCT_PTR(*ppfpd, CFOLDERPAIRDATA));
  1920. }
  1921. return(*ppfpd != NULL);
  1922. }
  1923. void DestroySharedFolderPairData(PFOLDERPAIRDATA pfpd)
  1924. {
  1925. ASSERT(IS_VALID_STRUCT_PTR(pfpd, CFOLDERPAIRDATA));
  1926. /* Destroy FOLDERPAIRDATA fields. */
  1927. DeleteString(pfpd->hsName);
  1928. FreeMemory(pfpd);
  1929. }
  1930. /*
  1931. ** FolderPairSortCmp()
  1932. **
  1933. ** Pointer comparison function used to sort the global array of folder pairs.
  1934. **
  1935. ** Arguments: pcfp1 - pointer to FOLDERPAIR describing first folder pair
  1936. ** pcfp2 - pointer to FOLDERPAIR describing second folder pair
  1937. **
  1938. ** Returns:
  1939. **
  1940. ** Side Effects: none
  1941. **
  1942. ** Folder pairs are sorted by:
  1943. ** 1) path
  1944. ** 2) pointer value
  1945. */
  1946. COMPARISONRESULT FolderPairSortCmp(PCVOID pcfp1, PCVOID pcfp2)
  1947. {
  1948. COMPARISONRESULT cr;
  1949. ASSERT(IS_VALID_STRUCT_PTR(pcfp1, CFOLDERPAIR));
  1950. ASSERT(IS_VALID_STRUCT_PTR(pcfp2, CFOLDERPAIR));
  1951. cr = ComparePaths(((PCFOLDERPAIR)pcfp1)->hpath,
  1952. ((PCFOLDERPAIR)pcfp2)->hpath);
  1953. if (cr == CR_EQUAL)
  1954. cr = ComparePointers(pcfp1, pcfp2);
  1955. return(cr);
  1956. }
  1957. /*
  1958. ** FolderPairSearchCmp()
  1959. **
  1960. ** Pointer comparison function used to search the global array of folder pairs
  1961. ** for the first folder pair for a given folder.
  1962. **
  1963. ** Arguments: hpath - folder pair to search for
  1964. ** pcfp - pointer to FOLDERPAIR to examine
  1965. **
  1966. ** Returns:
  1967. **
  1968. ** Side Effects: none
  1969. **
  1970. ** Folder pairs are searched by:
  1971. ** 1) path
  1972. */
  1973. COMPARISONRESULT FolderPairSearchCmp(PCVOID hpath, PCVOID pcfp)
  1974. {
  1975. ASSERT(IS_VALID_HANDLE((HPATH)hpath, PATH));
  1976. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  1977. return(ComparePaths((HPATH)hpath, ((PCFOLDERPAIR)pcfp)->hpath));
  1978. }
  1979. BOOL RemoveSourceFolderTwin(POBJECTTWIN pot, PVOID pv)
  1980. {
  1981. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  1982. ASSERT(! pv);
  1983. if (EVAL(pot->ulcSrcFolderTwins > 0))
  1984. pot->ulcSrcFolderTwins--;
  1985. /*
  1986. * If there are no more source folder twins for this object twin, and this
  1987. * object twin is not a separate "orphan" object twin, wipe it out.
  1988. */
  1989. if (! pot->ulcSrcFolderTwins &&
  1990. IsStubFlagClear(&(pot->stub), STUB_FL_FROM_OBJECT_TWIN))
  1991. EVAL(DestroyStub(&(pot->stub)) == TR_SUCCESS);
  1992. return(TRUE);
  1993. }
  1994. /*
  1995. ** UnlinkHalfOfFolderPair()
  1996. **
  1997. ** Unlinks one half of a pair of folder twins.
  1998. **
  1999. ** Arguments: pfp - pointer to folder pair half to unlink
  2000. **
  2001. ** Returns: void
  2002. **
  2003. ** Side Effects: Removes a source folder twin from each of the object twin's
  2004. ** in the folder pair's list of generated object twins. May
  2005. ** cause object twins and twin families to be destroyed.
  2006. */
  2007. void UnlinkHalfOfFolderPair(PFOLDERPAIR pfp)
  2008. {
  2009. HPTRARRAY hpaFolderPairs;
  2010. ARRAYINDEX aiUnlink;
  2011. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2012. TRACE_OUT((TEXT("UnlinkHalfOfFolderPair(): Unlinking folder twin %s."),
  2013. DebugGetPathString(pfp->hpath)));
  2014. /* Search for the folder pair to be unlinked. */
  2015. hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pfp->pfpd->hbr);
  2016. if (EVAL(SearchSortedArray(hpaFolderPairs, &FolderPairSortCmp, pfp,
  2017. &aiUnlink)))
  2018. {
  2019. /* Unlink folder pair. */
  2020. ASSERT(GetPtr(hpaFolderPairs, aiUnlink) == pfp);
  2021. DeletePtr(hpaFolderPairs, aiUnlink);
  2022. /*
  2023. * Don't mark folder pair stub unlinked here. Let caller do that after
  2024. * both folder pair halves have been unlinked.
  2025. */
  2026. /* Remove a source folder twin from all generated object twins. */
  2027. EVAL(EnumGeneratedObjectTwins(pfp, &RemoveSourceFolderTwin, NULL));
  2028. }
  2029. }
  2030. BOOL FolderTwinIntersectsFolder(PCFOLDERPAIR pcfp, HPATH hpathFolder)
  2031. {
  2032. BOOL bResult;
  2033. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  2034. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  2035. if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE))
  2036. bResult = IsPathPrefix(hpathFolder, pcfp->hpath);
  2037. else
  2038. bResult = (ComparePaths(hpathFolder, pcfp->hpath) == CR_EQUAL);
  2039. return(bResult);
  2040. }
  2041. /*
  2042. ** CreateListOfFolderTwins()
  2043. **
  2044. ** Creates a list of folder twins from a block of folder pairs.
  2045. **
  2046. ** Arguments: aiFirst - index of first folder pair in the array of folder
  2047. ** pairs
  2048. ** hpathFolder - folder that list of folder twins is to be
  2049. ** created for
  2050. ** ppftHead - pointer to PFOLDERTWIN to be filled in with
  2051. ** pointer to first folder twin in new list
  2052. ** paic - pointer to ARRAYINDEX to be filled in with number of
  2053. ** folder twins in new list
  2054. **
  2055. ** Returns: TWINRESULT
  2056. **
  2057. ** Side Effects: none
  2058. */
  2059. TWINRESULT CreateListOfFolderTwins(HBRFCASE hbr, ARRAYINDEX aiFirst,
  2060. HPATH hpathFolder,
  2061. PFOLDERTWIN *ppftHead,
  2062. PARRAYINDEX paic)
  2063. {
  2064. TWINRESULT tr;
  2065. PFOLDERPAIR pfp;
  2066. HPATH hpath;
  2067. ARRAYINDEX aicPtrs;
  2068. ARRAYINDEX ai;
  2069. PFOLDERTWIN pftHead;
  2070. HPTRARRAY hpaFolderTwins;
  2071. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  2072. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  2073. ASSERT(IS_VALID_WRITE_PTR(ppftHead, PFOLDERTWIN));
  2074. ASSERT(IS_VALID_WRITE_PTR(paic, ARRAYINDEX));
  2075. /*
  2076. * Get the handle to the common folder that the list of folder twins is
  2077. * being prepared for.
  2078. */
  2079. hpaFolderTwins = GetBriefcaseFolderPairPtrArray(hbr);
  2080. pfp = GetPtr(hpaFolderTwins, aiFirst);
  2081. hpath = pfp->hpath;
  2082. /*
  2083. * Add the other half of each matching folder pair to the folder twin list
  2084. * as a folder twin.
  2085. */
  2086. aicPtrs = GetPtrCount(hpaFolderTwins);
  2087. ASSERT(aicPtrs > 0);
  2088. ASSERT(! (aicPtrs % 2));
  2089. ASSERT(aiFirst >= 0);
  2090. ASSERT(aiFirst < aicPtrs);
  2091. /* Start with an empty list of folder twins. */
  2092. pftHead = NULL;
  2093. /*
  2094. * A pointer to the first folder pair is already in pfp, but we'll look it
  2095. * up again.
  2096. */
  2097. TRACE_OUT((TEXT("CreateListOfFolderTwins(): Creating list of folder twins of folder %s."),
  2098. DebugGetPathString(hpath)));
  2099. tr = TR_SUCCESS;
  2100. for (ai = aiFirst; ai < aicPtrs && tr == TR_SUCCESS; ai++)
  2101. {
  2102. pfp = GetPtr(hpaFolderTwins, ai);
  2103. if (ComparePaths(pfp->hpath, hpathFolder) == CR_EQUAL)
  2104. tr = AddFolderTwinToList(pfp, pftHead, &pftHead);
  2105. else
  2106. break;
  2107. }
  2108. TRACE_OUT((TEXT("CreateListOfFolderTwins(): Finished creating list of folder twins of folder %s."),
  2109. DebugGetPathString(hpath)));
  2110. if (tr == TR_SUCCESS)
  2111. {
  2112. /* Success! Fill in the result parameters. */
  2113. *ppftHead = pftHead;
  2114. *paic = ai - aiFirst;
  2115. }
  2116. else
  2117. /* Free any folder twins that have been added to the list. */
  2118. DestroyListOfFolderTwins(pftHead);
  2119. return(tr);
  2120. }
  2121. /*
  2122. ** DestroyListOfFolderTwins()
  2123. **
  2124. ** Wipes out the folder twins in a folder twin list.
  2125. **
  2126. ** Arguments: pftHead - pointer to first folder twin in list
  2127. **
  2128. ** Returns: TWINRESULT
  2129. **
  2130. ** Side Effects: none
  2131. */
  2132. void DestroyListOfFolderTwins(PFOLDERTWIN pftHead)
  2133. {
  2134. while (pftHead)
  2135. {
  2136. PFOLDERTWIN pftOldHead;
  2137. ASSERT(IS_VALID_STRUCT_PTR(pftHead, CFOLDERTWIN));
  2138. UnlockStub(&(((PFOLDERPAIR)(pftHead->hftSrc))->stub));
  2139. UnlockStub(&(((PFOLDERPAIR)(pftHead->hftOther))->stub));
  2140. pftOldHead = pftHead;
  2141. pftHead = (PFOLDERTWIN)(pftHead->pcftNext);
  2142. FreeMemory((LPTSTR)(pftOldHead->pcszSrcFolder));
  2143. FreeMemory((LPTSTR)(pftOldHead->pcszOtherFolder));
  2144. FreeMemory(pftOldHead);
  2145. }
  2146. }
  2147. /*
  2148. ** AddFolderTwinToList()
  2149. **
  2150. ** Adds a folder twin to a list of folder twins.
  2151. **
  2152. ** Arguments: pfpSrc - pointer to source folder pair to be added
  2153. ** pftHead - pointer to head of folder twin list, may be NULL
  2154. ** ppft - pointer to PFOLDERTWIN to be filled in with pointer
  2155. ** to new folder twin, ppft may be &pftHead
  2156. **
  2157. ** Returns: TWINRESULT
  2158. **
  2159. ** Side Effects: none
  2160. */
  2161. TWINRESULT AddFolderTwinToList(PFOLDERPAIR pfpSrc,
  2162. PFOLDERTWIN pftHead,
  2163. PFOLDERTWIN *ppft)
  2164. {
  2165. TWINRESULT tr = TR_OUT_OF_MEMORY;
  2166. PFOLDERTWIN pftNew;
  2167. ASSERT(IS_VALID_STRUCT_PTR(pfpSrc, CFOLDERPAIR));
  2168. ASSERT(! pftHead || IS_VALID_STRUCT_PTR(pftHead, CFOLDERTWIN));
  2169. ASSERT(IS_VALID_WRITE_PTR(ppft, PFOLDERTWIN));
  2170. /* Try to create a new FOLDERTWIN structure. */
  2171. if (AllocateMemory(sizeof(*pftNew), &pftNew))
  2172. {
  2173. LPTSTR pszFirstFolder;
  2174. if (AllocatePathString(pfpSrc->hpath, &pszFirstFolder))
  2175. {
  2176. LPTSTR pszSecondFolder;
  2177. if (AllocatePathString(pfpSrc->pfpOther->hpath, &pszSecondFolder))
  2178. {
  2179. /* Fill in FOLDERTWIN structure fields. */
  2180. pftNew->pcftNext = pftHead;
  2181. pftNew->hftSrc = (HFOLDERTWIN)pfpSrc;
  2182. pftNew->hvidSrc = (HVOLUMEID)(pfpSrc->hpath);
  2183. pftNew->pcszSrcFolder = pszFirstFolder;
  2184. pftNew->hftOther = (HFOLDERTWIN)(pfpSrc->pfpOther);
  2185. pftNew->hvidOther = (HVOLUMEID)(pfpSrc->pfpOther->hpath);
  2186. pftNew->pcszOtherFolder = pszSecondFolder;
  2187. pftNew->pcszName = GetBfcString(pfpSrc->pfpd->hsName);
  2188. pftNew->dwFlags = 0;
  2189. if (IsStubFlagSet(&(pfpSrc->stub), STUB_FL_SUBTREE))
  2190. pftNew->dwFlags = FT_FL_SUBTREE;
  2191. LockStub(&(pfpSrc->stub));
  2192. LockStub(&(pfpSrc->pfpOther->stub));
  2193. *ppft = pftNew;
  2194. tr = TR_SUCCESS;
  2195. TRACE_OUT((TEXT("AddFolderTwinToList(): Added folder twin %s of folder %s matching objects %s."),
  2196. pftNew->pcszSrcFolder,
  2197. pftNew->pcszOtherFolder,
  2198. pftNew->pcszName));
  2199. }
  2200. else
  2201. {
  2202. FreeMemory(pszFirstFolder);
  2203. ADDFOLDERTWINTOLIST_BAIL:
  2204. FreeMemory(pftNew);
  2205. }
  2206. }
  2207. else
  2208. goto ADDFOLDERTWINTOLIST_BAIL;
  2209. }
  2210. ASSERT(tr != TR_SUCCESS ||
  2211. IS_VALID_STRUCT_PTR(*ppft, CFOLDERTWIN));
  2212. return(tr);
  2213. }
  2214. TWINRESULT WriteFolderPair(HCACHEDFILE hcf, PFOLDERPAIR pfp)
  2215. {
  2216. TWINRESULT tr;
  2217. DBFOLDERTWIN dbft;
  2218. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  2219. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2220. /* Set up folder pair database structure. */
  2221. dbft.dwStubFlags = (pfp->stub.dwFlags & DB_STUB_FLAGS_MASK);
  2222. dbft.hpath1 = pfp->hpath;
  2223. dbft.hpath2 = pfp->pfpOther->hpath;
  2224. dbft.hsName = pfp->pfpd->hsName;
  2225. dbft.dwAttributes = pfp->pfpd->dwAttributes;
  2226. /* Save folder pair database structure. */
  2227. if (WriteToCachedFile(hcf, (PCVOID)&dbft, sizeof(dbft), NULL))
  2228. tr = TR_SUCCESS;
  2229. else
  2230. tr = TR_BRIEFCASE_WRITE_FAILED;
  2231. return(tr);
  2232. }
  2233. TWINRESULT ReadFolderPair(HCACHEDFILE hcf, HBRFCASE hbr,
  2234. HHANDLETRANS hhtFolderTrans,
  2235. HHANDLETRANS hhtNameTrans)
  2236. {
  2237. TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  2238. DBFOLDERTWIN dbft;
  2239. DWORD dwcbRead;
  2240. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  2241. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  2242. ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
  2243. ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
  2244. if (ReadFromCachedFile(hcf, &dbft, sizeof(dbft), &dwcbRead) &&
  2245. dwcbRead == sizeof(dbft))
  2246. {
  2247. INEWFOLDERTWIN inft;
  2248. if (TranslateHandle(hhtFolderTrans, (HGENERIC)(dbft.hpath1), (PHGENERIC)&(inft.hpathFirst)))
  2249. {
  2250. if (TranslateHandle(hhtFolderTrans, (HGENERIC)(dbft.hpath2), (PHGENERIC)&(inft.hpathSecond)))
  2251. {
  2252. if (TranslateHandle(hhtNameTrans, (HGENERIC)(dbft.hsName), (PHGENERIC)&(inft.hsName)))
  2253. {
  2254. PFOLDERPAIR pfp;
  2255. inft.dwAttributes = dbft.dwAttributes;
  2256. inft.hbr = hbr;
  2257. if (IS_FLAG_SET(dbft.dwStubFlags, STUB_FL_SUBTREE))
  2258. inft.dwFlags = NFT_FL_SUBTREE;
  2259. else
  2260. inft.dwFlags = 0;
  2261. if (CreateFolderPair(&inft, &pfp))
  2262. tr = TR_SUCCESS;
  2263. else
  2264. tr = TR_OUT_OF_MEMORY;
  2265. }
  2266. }
  2267. }
  2268. }
  2269. return(tr);
  2270. }
  2271. BOOL CreateFolderPairPtrArray(PHPTRARRAY phpa)
  2272. {
  2273. NEWPTRARRAY npa;
  2274. ASSERT(IS_VALID_WRITE_PTR(phpa, HPTRARRAY));
  2275. /* Try to create a folder pair pointer array. */
  2276. npa.aicInitialPtrs = NUM_START_FOLDER_TWIN_PTRS;
  2277. npa.aicAllocGranularity = NUM_FOLDER_TWIN_PTRS_TO_ADD;
  2278. npa.dwFlags = NPA_FL_SORTED_ADD;
  2279. return(CreatePtrArray(&npa, phpa));
  2280. }
  2281. void DestroyFolderPairPtrArray(HPTRARRAY hpa)
  2282. {
  2283. ARRAYINDEX aicPtrs;
  2284. ARRAYINDEX ai;
  2285. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  2286. /* Free all folder pairs in pointer array. */
  2287. aicPtrs = GetPtrCount(hpa);
  2288. ASSERT(! (aicPtrs % 2));
  2289. for (ai = 0; ai < aicPtrs; ai++)
  2290. {
  2291. PFOLDERPAIR pfp;
  2292. PFOLDERPAIR pfpOther;
  2293. PFOLDERPAIRDATA pfpd;
  2294. BOOL bDeleteFolderPairData;
  2295. pfp = GetPtr(hpa, ai);
  2296. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2297. /* Copy fields needed after folder pair half's demise. */
  2298. pfpOther = pfp->pfpOther;
  2299. pfpd = pfp->pfpd;
  2300. bDeleteFolderPairData = IsStubFlagSet(&(pfp->stub), STUB_FL_BEING_DELETED);
  2301. DestroyHalfOfFolderPair(pfp);
  2302. /* Has the other half of this folder pair already been destroyed? */
  2303. if (bDeleteFolderPairData)
  2304. /* Yes. Destroy the pair's shared data. */
  2305. DestroySharedFolderPairData(pfpd);
  2306. }
  2307. /* Now wipe out the pointer array. */
  2308. DestroyPtrArray(hpa);
  2309. }
  2310. void LockFolderPair(PFOLDERPAIR pfp)
  2311. {
  2312. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2313. ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED));
  2314. ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
  2315. ASSERT(pfp->stub.ulcLock < ULONG_MAX);
  2316. pfp->stub.ulcLock++;
  2317. ASSERT(pfp->pfpOther->stub.ulcLock < ULONG_MAX);
  2318. pfp->pfpOther->stub.ulcLock++;
  2319. }
  2320. void UnlockFolderPair(PFOLDERPAIR pfp)
  2321. {
  2322. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2323. if (EVAL(pfp->stub.ulcLock > 0))
  2324. pfp->stub.ulcLock--;
  2325. if (EVAL(pfp->pfpOther->stub.ulcLock > 0))
  2326. pfp->pfpOther->stub.ulcLock--;
  2327. if (! pfp->stub.ulcLock &&
  2328. IsStubFlagSet(&(pfp->stub), STUB_FL_UNLINKED))
  2329. {
  2330. ASSERT(! pfp->pfpOther->stub.ulcLock);
  2331. ASSERT(IsStubFlagSet(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
  2332. DestroyFolderPair(pfp);
  2333. }
  2334. }
  2335. /*
  2336. ** UnlinkFolderPair()
  2337. **
  2338. ** Unlinks a folder pair.
  2339. **
  2340. ** Arguments: pfp - pointer to folder pair to be unlinked
  2341. **
  2342. ** Returns: TWINRESULT
  2343. **
  2344. ** Side Effects: none
  2345. */
  2346. TWINRESULT UnlinkFolderPair(PFOLDERPAIR pfp)
  2347. {
  2348. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2349. ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED));
  2350. ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
  2351. /* Unlink both halves of the folder pair. */
  2352. UnlinkHalfOfFolderPair(pfp);
  2353. UnlinkHalfOfFolderPair(pfp->pfpOther);
  2354. SetStubFlag(&(pfp->stub), STUB_FL_UNLINKED);
  2355. SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_UNLINKED);
  2356. return(TR_SUCCESS);
  2357. }
  2358. /*
  2359. ** DestroyFolderPair()
  2360. **
  2361. ** Destroys a folder pair.
  2362. **
  2363. ** Arguments: pfp - pointer to folder pair to destroy
  2364. **
  2365. ** Returns: void
  2366. **
  2367. ** Side Effects: none
  2368. */
  2369. void DestroyFolderPair(PFOLDERPAIR pfp)
  2370. {
  2371. PFOLDERPAIRDATA pfpd;
  2372. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2373. /* Destroy both FOLDERPAIR halves, and shared data. */
  2374. pfpd = pfp->pfpd;
  2375. DestroyHalfOfFolderPair(pfp->pfpOther);
  2376. DestroyHalfOfFolderPair(pfp);
  2377. DestroySharedFolderPairData(pfpd);
  2378. }
  2379. /*
  2380. ** ApplyNewObjectTwinsToFolderTwins()
  2381. **
  2382. **
  2383. **
  2384. ** Arguments:
  2385. **
  2386. ** Returns:
  2387. **
  2388. ** Side Effects: Adds new spin-off object twins to hlistNewObjectTwins as they
  2389. ** are created.
  2390. **
  2391. ** N.b., new object twins may have been added to hlistNewObjectTwins even if
  2392. ** FALSE is returned. Clean-up of these new object twins in case of failure is
  2393. ** the caller's responsibility.
  2394. **
  2395. */
  2396. BOOL ApplyNewObjectTwinsToFolderTwins(HLIST hlistNewObjectTwins)
  2397. {
  2398. BOOL bResult = TRUE;
  2399. BOOL bContinue;
  2400. HNODE hnode;
  2401. ASSERT(IS_VALID_HANDLE(hlistNewObjectTwins, LIST));
  2402. /*
  2403. * Don't use WalkList() here because we want to insert new nodes in
  2404. * hlistNewObjectTwins after the current node.
  2405. */
  2406. for (bContinue = GetFirstNode(hlistNewObjectTwins, &hnode);
  2407. bContinue && bResult;
  2408. bContinue = GetNextNode(hnode, &hnode))
  2409. {
  2410. POBJECTTWIN pot;
  2411. HPATHLIST hpl;
  2412. HPTRARRAY hpaFolderPairs;
  2413. ARRAYINDEX aicPtrs;
  2414. ARRAYINDEX ai;
  2415. pot = GetNodeData(hnode);
  2416. ASSERT(! pot->ulcSrcFolderTwins);
  2417. TRACE_OUT((TEXT("ApplyNewObjectTwinsToFolderTwins(): Applying new object twin %s\\%s."),
  2418. DebugGetPathString(pot->hpath),
  2419. GetBfcString(pot->ptfParent->hsName)));
  2420. /*
  2421. * Assume that hpl, hpaFolderPairs, and aicPtrs don't change during this
  2422. * loop. Calculate them outside the loop.
  2423. */
  2424. hpl = GetBriefcasePathList(pot->ptfParent->hbr);
  2425. hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pot->ptfParent->hbr);
  2426. aicPtrs = GetPtrCount(hpaFolderPairs);
  2427. ASSERT(! (aicPtrs % 2));
  2428. for (ai = 0; ai < aicPtrs; ai++)
  2429. {
  2430. PFOLDERPAIR pfp;
  2431. pfp = GetPtr(hpaFolderPairs, ai);
  2432. if (FolderTwinGeneratesObjectTwin(pfp, pot->hpath,
  2433. GetBfcString(pot->ptfParent->hsName)))
  2434. {
  2435. HPATH hpathMatchingFolder;
  2436. HNODE hnodeUnused;
  2437. ASSERT(pot->ulcSrcFolderTwins < ULONG_MAX);
  2438. pot->ulcSrcFolderTwins++;
  2439. /*
  2440. * Append the generated object twin's subpath to the matching
  2441. * folder twin's base path for subtree twins.
  2442. */
  2443. if (BuildPathForMatchingObjectTwin(pfp, pot, hpl,
  2444. &hpathMatchingFolder))
  2445. {
  2446. /*
  2447. * We don't want to collapse any twin families if the matching
  2448. * object twin is found in a different twin family. This will
  2449. * be done by ApplyNewFolderTwinsToTwinFamilies() for spin-off
  2450. * object twins generated by new folder twins.
  2451. *
  2452. * Spin-off object twins created by new object twins never
  2453. * require collapsing twin families. For a spin-off object twin
  2454. * generated by a new object twin to collapse twin families,
  2455. * there would have to have been separate twin families
  2456. * connected by a folder twin. But if those twin families were
  2457. * already connected by a folder twin, they would not be
  2458. * separate because they would already have been collapsed by
  2459. * ApplyNewFolderTwinsToTwinFamilies() when the connecting
  2460. * folder twin was added.
  2461. */
  2462. if (! FindObjectTwin(pot->ptfParent->hbr, hpathMatchingFolder,
  2463. GetBfcString(pot->ptfParent->hsName),
  2464. &hnodeUnused))
  2465. {
  2466. POBJECTTWIN potNew;
  2467. /*
  2468. * CreateObjectTwin() ASSERT()s that an object twin for
  2469. * hpathMatchingFolder is not found, so we don't need to do
  2470. * that here.
  2471. */
  2472. if (CreateObjectTwin(pot->ptfParent, hpathMatchingFolder,
  2473. &potNew))
  2474. {
  2475. /*
  2476. * Add the new object twin to hlistNewObjectTwins after
  2477. * the new object twin currently being processed to make
  2478. * certain that it gets processed in the outside loop
  2479. * through hlistNewObjectTwins.
  2480. */
  2481. if (! InsertNodeAfter(hnode, NULL, potNew, &hnodeUnused))
  2482. {
  2483. DestroyStub(&(potNew->stub));
  2484. bResult = FALSE;
  2485. break;
  2486. }
  2487. }
  2488. }
  2489. DeletePath(hpathMatchingFolder);
  2490. }
  2491. else
  2492. {
  2493. bResult = FALSE;
  2494. break;
  2495. }
  2496. }
  2497. }
  2498. }
  2499. return(bResult);
  2500. }
  2501. /*
  2502. ** BuildPathForMatchingObjectTwin()
  2503. **
  2504. **
  2505. **
  2506. ** Arguments:
  2507. **
  2508. ** Returns:
  2509. **
  2510. ** Side Effects: Path is added to object twin's briefcase's path list.
  2511. */
  2512. BOOL BuildPathForMatchingObjectTwin(PCFOLDERPAIR pcfp,
  2513. PCOBJECTTWIN pcot,
  2514. HPATHLIST hpl, PHPATH phpath)
  2515. {
  2516. BOOL bResult;
  2517. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  2518. ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
  2519. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  2520. ASSERT(IS_VALID_WRITE_PTR(phpath, HPATH));
  2521. ASSERT(FolderTwinGeneratesObjectTwin(pcfp, pcot->hpath, GetBfcString(pcot->ptfParent->hsName)));
  2522. /* Is the generating folder twin a subtree twin? */
  2523. if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE))
  2524. {
  2525. TCHAR rgchPathSuffix[MAX_PATH_LEN];
  2526. LPCTSTR pcszSubPath;
  2527. /*
  2528. * Yes. Append the object twin's subpath to the subtree twin's base
  2529. * path.
  2530. */
  2531. pcszSubPath = FindChildPathSuffix(pcfp->hpath, pcot->hpath,
  2532. rgchPathSuffix);
  2533. bResult = AddChildPath(hpl, pcfp->pfpOther->hpath, pcszSubPath, phpath);
  2534. }
  2535. else
  2536. /* No. Just use the matching folder twin's folder. */
  2537. bResult = CopyPath(pcfp->pfpOther->hpath, hpl, phpath);
  2538. return(bResult);
  2539. }
  2540. /*
  2541. ** EnumGeneratedObjectTwins()
  2542. **
  2543. **
  2544. **
  2545. ** Arguments:
  2546. **
  2547. ** Returns: FALSE if callback aborted. TRUE if not.
  2548. **
  2549. ** Side Effects: none
  2550. */
  2551. BOOL EnumGeneratedObjectTwins(PCFOLDERPAIR pcfp,
  2552. ENUMGENERATEDOBJECTTWINSPROC egotp,
  2553. PVOID pvRefData)
  2554. {
  2555. BOOL bResult = TRUE;
  2556. HPTRARRAY hpaTwinFamilies;
  2557. ARRAYINDEX aicPtrs;
  2558. ARRAYINDEX ai;
  2559. /* pvRefData may be any value. */
  2560. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  2561. ASSERT(IS_VALID_CODE_PTR(egotp, ENUMGENERATEDOBJECTTWINPROC));
  2562. /*
  2563. * Walk the array of twin families, looking for twin families whose names
  2564. * intersect the given folder twin's name specification.
  2565. */
  2566. hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(pcfp->pfpd->hbr);
  2567. aicPtrs = GetPtrCount(hpaTwinFamilies);
  2568. ai = 0;
  2569. while (ai < aicPtrs)
  2570. {
  2571. PTWINFAMILY ptf;
  2572. LPCTSTR pcszName;
  2573. ptf = GetPtr(hpaTwinFamilies, ai);
  2574. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  2575. ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED));
  2576. /*
  2577. * Does the twin family's name match the folder twin's name
  2578. * specification?
  2579. */
  2580. pcszName = GetBfcString(ptf->hsName);
  2581. if (IsFolderObjectTwinName(pcszName) ||
  2582. NamesIntersect(pcszName, GetBfcString(pcfp->pfpd->hsName)))
  2583. {
  2584. BOOL bContinue;
  2585. HNODE hnodePrev;
  2586. /* Yes. Look for a matching folder. */
  2587. /* Lock the twin family so it isn't deleted out from under us. */
  2588. LockStub(&(ptf->stub));
  2589. /*
  2590. * Walk each twin family's list of object twins looking for object
  2591. * twins in the given folder twin's subtree.
  2592. */
  2593. bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnodePrev);
  2594. while (bContinue)
  2595. {
  2596. HNODE hnodeNext;
  2597. POBJECTTWIN pot;
  2598. bContinue = GetNextNode(hnodePrev, &hnodeNext);
  2599. pot = (POBJECTTWIN)GetNodeData(hnodePrev);
  2600. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  2601. if (FolderTwinIntersectsFolder(pcfp, pot->hpath))
  2602. {
  2603. /*
  2604. * A given object twin should only be generated by one of the
  2605. * folder twins in a pair of folder twins.
  2606. */
  2607. ASSERT(! FolderTwinGeneratesObjectTwin(pcfp->pfpOther, pot->hpath, GetBfcString(pot->ptfParent->hsName)));
  2608. bResult = (*egotp)(pot, pvRefData);
  2609. if (! bResult)
  2610. break;
  2611. }
  2612. hnodePrev = hnodeNext;
  2613. }
  2614. /* Was the twin family unlinked? */
  2615. if (IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED))
  2616. /* No. */
  2617. ai++;
  2618. else
  2619. {
  2620. /* Yes. */
  2621. aicPtrs--;
  2622. ASSERT(aicPtrs == GetPtrCount(hpaTwinFamilies));
  2623. TRACE_OUT((TEXT("EnumGeneratedObjectTwins(): Twin family for object %s unlinked by callback."),
  2624. GetBfcString(ptf->hsName)));
  2625. }
  2626. UnlockStub(&(ptf->stub));
  2627. if (! bResult)
  2628. break;
  2629. }
  2630. else
  2631. /* No. Skip it. */
  2632. ai++;
  2633. }
  2634. return(bResult);
  2635. }
  2636. /*
  2637. ** EnumGeneratingFolderTwins()
  2638. **
  2639. **
  2640. **
  2641. ** Arguments:
  2642. **
  2643. ** Returns: FALSE if callback aborted. TRUE if not.
  2644. **
  2645. ** Side Effects: none
  2646. **
  2647. ** N.b., if the egftp callback removes a pair of folder twins, it must remove
  2648. ** the pair from the first folder twin encountered. If it removes the pair of
  2649. ** folder twins from the second folder twin encountered, a folder twin will be
  2650. ** skipped.
  2651. */
  2652. BOOL EnumGeneratingFolderTwins(PCOBJECTTWIN pcot,
  2653. ENUMGENERATINGFOLDERTWINSPROC egftp,
  2654. PVOID pvRefData,
  2655. PULONG pulcGeneratingFolderTwins)
  2656. {
  2657. BOOL bResult = TRUE;
  2658. HPTRARRAY hpaFolderPairs;
  2659. ARRAYINDEX aicPtrs;
  2660. ARRAYINDEX ai;
  2661. /* pvRefData may be any value. */
  2662. ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
  2663. ASSERT(IS_VALID_CODE_PTR(egftp, ENUMGENERATINGFOLDERTWINSPROC));
  2664. ASSERT(IS_VALID_WRITE_PTR(pulcGeneratingFolderTwins, ULONG));
  2665. *pulcGeneratingFolderTwins = 0;
  2666. hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcot->ptfParent->hbr);
  2667. aicPtrs = GetPtrCount(hpaFolderPairs);
  2668. ASSERT(! (aicPtrs % 2));
  2669. ai = 0;
  2670. while (ai < aicPtrs)
  2671. {
  2672. PFOLDERPAIR pfp;
  2673. pfp = GetPtr(hpaFolderPairs, ai);
  2674. if (FolderTwinGeneratesObjectTwin(pfp, pcot->hpath,
  2675. GetBfcString(pcot->ptfParent->hsName)))
  2676. {
  2677. ASSERT(! FolderTwinGeneratesObjectTwin(pfp->pfpOther, pcot->hpath, GetBfcString(pcot->ptfParent->hsName)));
  2678. ASSERT(*pulcGeneratingFolderTwins < ULONG_MAX);
  2679. (*pulcGeneratingFolderTwins)++;
  2680. /*
  2681. * Lock the pair of folder twins so they don't get deleted out from
  2682. * under us.
  2683. */
  2684. LockStub(&(pfp->stub));
  2685. bResult = (*egftp)(pfp, pvRefData);
  2686. if (IsStubFlagSet(&(pfp->stub), STUB_FL_UNLINKED))
  2687. {
  2688. WARNING_OUT((TEXT("EnumGeneratingFolderTwins(): Folder twin pair unlinked during callback.")));
  2689. aicPtrs -= 2;
  2690. ASSERT(! (aicPtrs % 2));
  2691. ASSERT(aicPtrs == GetPtrCount(hpaFolderPairs));
  2692. }
  2693. else
  2694. ai++;
  2695. UnlockStub(&(pfp->stub));
  2696. if (! bResult)
  2697. break;
  2698. }
  2699. else
  2700. ai++;
  2701. }
  2702. return(bResult);
  2703. }
  2704. /*
  2705. ** FolderTwinGeneratesObjectTwin()
  2706. **
  2707. **
  2708. **
  2709. ** Arguments:
  2710. **
  2711. ** Returns:
  2712. **
  2713. ** Side Effects: none
  2714. **
  2715. ** A folder twin or subtree twin is said to generate an object twin when the
  2716. ** following conditions are met:
  2717. **
  2718. ** 1) The folder twin or subtree twin is on the same volume as the object twin.
  2719. **
  2720. ** 2) The name of the object twin (literal) intersects the objects matched by
  2721. ** the folder twin or subtree twin (literal or wildcard).
  2722. **
  2723. ** 3) The folder twin's folder exactly matches the object twin's folder, or the
  2724. ** subtree twin's root folder is a path prefix of the object twin's folder.
  2725. */
  2726. BOOL FolderTwinGeneratesObjectTwin(PCFOLDERPAIR pcfp,
  2727. HPATH hpathFolder,
  2728. LPCTSTR pcszName)
  2729. {
  2730. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  2731. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  2732. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  2733. return(FolderTwinIntersectsFolder(pcfp, hpathFolder) &&
  2734. (IsFolderObjectTwinName(pcszName) ||
  2735. NamesIntersect(pcszName, GetBfcString(pcfp->pfpd->hsName))));
  2736. }
  2737. TWINRESULT WriteFolderPairList(HCACHEDFILE hcf,
  2738. HPTRARRAY hpaFolderPairs)
  2739. {
  2740. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  2741. DWORD dwcbDBFolderTwinListHeaderOffset;
  2742. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  2743. ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
  2744. /* Save initial file position. */
  2745. dwcbDBFolderTwinListHeaderOffset = GetCachedFilePointerPosition(hcf);
  2746. if (dwcbDBFolderTwinListHeaderOffset != INVALID_SEEK_POSITION)
  2747. {
  2748. DBFOLDERTWINLISTHEADER dbftlh;
  2749. /* Leave space for folder twin data header. */
  2750. ZeroMemory(&dbftlh, sizeof(dbftlh));
  2751. if (WriteToCachedFile(hcf, (PCVOID)&dbftlh, sizeof(dbftlh), NULL))
  2752. {
  2753. ARRAYINDEX aicPtrs;
  2754. ARRAYINDEX ai;
  2755. tr = TR_SUCCESS;
  2756. /* Mark all folder pairs unused. */
  2757. ClearFlagInArrayOfStubs(hpaFolderPairs, STUB_FL_USED);
  2758. aicPtrs = GetPtrCount(hpaFolderPairs);
  2759. ASSERT(! (aicPtrs % 2));
  2760. /* Write all folder pairs. */
  2761. for (ai = 0; ai < aicPtrs; ai++)
  2762. {
  2763. PFOLDERPAIR pfp;
  2764. pfp = GetPtr(hpaFolderPairs, ai);
  2765. ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  2766. if (IsStubFlagClear(&(pfp->stub), STUB_FL_USED))
  2767. {
  2768. ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_USED));
  2769. tr = WriteFolderPair(hcf, pfp);
  2770. if (tr == TR_SUCCESS)
  2771. {
  2772. SetStubFlag(&(pfp->stub), STUB_FL_USED);
  2773. SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_USED);
  2774. }
  2775. else
  2776. break;
  2777. }
  2778. }
  2779. /* Save folder twin data header. */
  2780. if (tr == TR_SUCCESS)
  2781. {
  2782. ASSERT(! (aicPtrs % 2));
  2783. dbftlh.lcFolderPairs = aicPtrs / 2;
  2784. tr = WriteDBSegmentHeader(hcf, dwcbDBFolderTwinListHeaderOffset,
  2785. &dbftlh, sizeof(dbftlh));
  2786. if (tr == TR_SUCCESS)
  2787. TRACE_OUT((TEXT("WriteFolderPairList(): Wrote %ld folder pairs."),
  2788. dbftlh.lcFolderPairs));
  2789. }
  2790. }
  2791. }
  2792. return(tr);
  2793. }
  2794. TWINRESULT ReadFolderPairList(HCACHEDFILE hcf, HBRFCASE hbr,
  2795. HHANDLETRANS hhtFolderTrans,
  2796. HHANDLETRANS hhtNameTrans)
  2797. {
  2798. TWINRESULT tr;
  2799. DBFOLDERTWINLISTHEADER dbftlh;
  2800. DWORD dwcbRead;
  2801. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  2802. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  2803. ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
  2804. ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
  2805. if (ReadFromCachedFile(hcf, &dbftlh, sizeof(dbftlh), &dwcbRead) &&
  2806. dwcbRead == sizeof(dbftlh))
  2807. {
  2808. LONG l;
  2809. tr = TR_SUCCESS;
  2810. TRACE_OUT((TEXT("ReadFolderPairList(): Reading %ld folder pairs."),
  2811. dbftlh.lcFolderPairs));
  2812. for (l = 0; l < dbftlh.lcFolderPairs && tr == TR_SUCCESS; l++)
  2813. tr = ReadFolderPair(hcf, hbr, hhtFolderTrans, hhtNameTrans);
  2814. ASSERT(tr != TR_SUCCESS || AreFolderPairsValid(GetBriefcaseFolderPairPtrArray(hbr)));
  2815. }
  2816. else
  2817. tr = TR_CORRUPT_BRIEFCASE;
  2818. return(tr);
  2819. }
  2820. /* Macros
  2821. *********/
  2822. #define HT_ARRAY_ELEMENT(pht, ai) ((((PHANDLETRANS)(hht))->hpHandlePairs)[(ai)])
  2823. /* Types
  2824. ********/
  2825. /* handle translation unit */
  2826. typedef struct _handlepair
  2827. {
  2828. HGENERIC hgenOld;
  2829. HGENERIC hgenNew;
  2830. }
  2831. HANDLEPAIR;
  2832. DECLARE_STANDARD_TYPES(HANDLEPAIR);
  2833. /* handle translation structure */
  2834. typedef struct _handletrans
  2835. {
  2836. /* pointer to array of handle translation units */
  2837. HANDLEPAIR *hpHandlePairs;
  2838. /* number of handle pairs in array */
  2839. LONG lcTotalHandlePairs;
  2840. /* number of used handle pairs in array */
  2841. LONG lcUsedHandlePairs;
  2842. }
  2843. HANDLETRANS;
  2844. DECLARE_STANDARD_TYPES(HANDLETRANS);
  2845. COMPARISONRESULT CompareHandlePairs(PCVOID pchp1, PCVOID pchp2)
  2846. {
  2847. COMPARISONRESULT cr;
  2848. ASSERT(IS_VALID_STRUCT_PTR(pchp1, CHANDLEPAIR));
  2849. ASSERT(IS_VALID_STRUCT_PTR(pchp2, CHANDLEPAIR));
  2850. if (((PHANDLEPAIR)pchp1)->hgenOld < ((PHANDLEPAIR)pchp2)->hgenOld)
  2851. cr = CR_FIRST_SMALLER;
  2852. else if (((PHANDLEPAIR)pchp1)->hgenOld > ((PHANDLEPAIR)pchp2)->hgenOld)
  2853. cr = CR_FIRST_LARGER;
  2854. else
  2855. cr = CR_EQUAL;
  2856. return(cr);
  2857. }
  2858. BOOL CreateHandleTranslator(LONG lcHandles, PHHANDLETRANS phht)
  2859. {
  2860. PHANDLEPAIR hpHandlePairs;
  2861. ASSERT(IS_VALID_WRITE_PTR(phht, HHANDLETRANS));
  2862. *phht = NULL;
  2863. if (AllocateMemory(sizeof(HANDLEPAIR) * lcHandles, &hpHandlePairs))
  2864. {
  2865. PHANDLETRANS phtNew;
  2866. if (AllocateMemory(sizeof(*phtNew), &phtNew))
  2867. {
  2868. /* Success! Fill in HANDLETRANS fields. */
  2869. phtNew->hpHandlePairs = hpHandlePairs;
  2870. phtNew->lcTotalHandlePairs = lcHandles;
  2871. phtNew->lcUsedHandlePairs = 0;
  2872. *phht = (HHANDLETRANS)phtNew;
  2873. ASSERT(IS_VALID_HANDLE(*phht, HANDLETRANS));
  2874. }
  2875. else
  2876. FreeMemory(hpHandlePairs);
  2877. }
  2878. return(*phht != NULL);
  2879. }
  2880. void DestroyHandleTranslator(HHANDLETRANS hht)
  2881. {
  2882. ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  2883. ASSERT(((PHANDLETRANS)hht)->hpHandlePairs);
  2884. FreeMemory(((PHANDLETRANS)hht)->hpHandlePairs);
  2885. FreeMemory((PHANDLETRANS)hht);
  2886. }
  2887. BOOL AddHandleToHandleTranslator(HHANDLETRANS hht,
  2888. HGENERIC hgenOld,
  2889. HGENERIC hgenNew)
  2890. {
  2891. BOOL bRet;
  2892. ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  2893. if (((PHANDLETRANS)hht)->lcUsedHandlePairs < ((PHANDLETRANS)hht)->lcTotalHandlePairs)
  2894. {
  2895. HT_ARRAY_ELEMENT((PHANDLETRANS)hht, ((PHANDLETRANS)hht)->lcUsedHandlePairs).hgenOld = hgenOld;
  2896. HT_ARRAY_ELEMENT((PHANDLETRANS)hht, ((PHANDLETRANS)hht)->lcUsedHandlePairs).hgenNew = hgenNew;
  2897. ((PHANDLETRANS)hht)->lcUsedHandlePairs++;
  2898. bRet = TRUE;
  2899. }
  2900. else
  2901. bRet = FALSE;
  2902. return(bRet);
  2903. }
  2904. void PrepareForHandleTranslation(HHANDLETRANS hht)
  2905. {
  2906. HANDLEPAIR hpTemp;
  2907. ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  2908. HeapSort(((PHANDLETRANS)hht)->hpHandlePairs,
  2909. ((PHANDLETRANS)hht)->lcUsedHandlePairs,
  2910. sizeof((((PHANDLETRANS)hht)->hpHandlePairs)[0]),
  2911. &CompareHandlePairs,
  2912. &hpTemp);
  2913. }
  2914. BOOL TranslateHandle(HHANDLETRANS hht, HGENERIC hgenOld,
  2915. PHGENERIC phgenNew)
  2916. {
  2917. BOOL bFound;
  2918. HANDLEPAIR hpTemp;
  2919. LONG liTarget;
  2920. ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  2921. ASSERT(IS_VALID_WRITE_PTR(phgenNew, HGENERIC));
  2922. hpTemp.hgenOld = hgenOld;
  2923. bFound = BinarySearch(((PHANDLETRANS)hht)->hpHandlePairs,
  2924. ((PHANDLETRANS)hht)->lcUsedHandlePairs,
  2925. sizeof((((PHANDLETRANS)hht)->hpHandlePairs)[0]),
  2926. &CompareHandlePairs,
  2927. &hpTemp,
  2928. &liTarget);
  2929. if (bFound)
  2930. {
  2931. ASSERT(liTarget < ((PHANDLETRANS)hht)->lcUsedHandlePairs);
  2932. *phgenNew = HT_ARRAY_ELEMENT((PHANDLETRANS)hht, liTarget).hgenNew;
  2933. }
  2934. return(bFound);
  2935. }
  2936. /* Macros
  2937. *********/
  2938. /* Add nodes to list in sorted order? */
  2939. #define ADD_NODES_IN_SORTED_ORDER(plist) IS_FLAG_SET((plist)->dwFlags, LIST_FL_SORTED_ADD)
  2940. /* Types
  2941. ********/
  2942. /* list node types */
  2943. typedef struct _node
  2944. {
  2945. struct _node *pnodeNext; /* next node in list */
  2946. struct _node *pnodePrev; /* previous node in list */
  2947. PCVOID pcv; /* node data */
  2948. }
  2949. NODE;
  2950. DECLARE_STANDARD_TYPES(NODE);
  2951. /* list flags */
  2952. typedef enum _listflags
  2953. {
  2954. /* Insert nodes in sorted order. */
  2955. LIST_FL_SORTED_ADD = 0x0001,
  2956. /* flag combinations */
  2957. ALL_LIST_FLAGS = LIST_FL_SORTED_ADD
  2958. }
  2959. LISTFLAGS;
  2960. /*
  2961. * A LIST is just a special node at the head of a list. N.b., the _node
  2962. * structure MUST appear first in the _list structure because a pointer to a
  2963. * list is sometimes used as a pointer to a node.
  2964. */
  2965. typedef struct _list
  2966. {
  2967. NODE node;
  2968. DWORD dwFlags;
  2969. }
  2970. LIST;
  2971. DECLARE_STANDARD_TYPES(LIST);
  2972. /* SearchForNode() return codes */
  2973. typedef enum _addnodeaction
  2974. {
  2975. ANA_FOUND,
  2976. ANA_INSERT_BEFORE_NODE,
  2977. ANA_INSERT_AFTER_NODE,
  2978. ANA_INSERT_AT_HEAD
  2979. }
  2980. ADDNODEACTION;
  2981. DECLARE_STANDARD_TYPES(ADDNODEACTION);
  2982. /* Module Prototypes
  2983. ********************/
  2984. ADDNODEACTION SearchForNode(HLIST, COMPARESORTEDNODESPROC, PCVOID, PHNODE);
  2985. BOOL IsListInSortedOrder(PCLIST, COMPARESORTEDNODESPROC);
  2986. ADDNODEACTION SearchForNode(HLIST hlist,
  2987. COMPARESORTEDNODESPROC csnp,
  2988. PCVOID pcv,
  2989. PHNODE phnode)
  2990. {
  2991. ADDNODEACTION ana;
  2992. ULONG ulcNodes;
  2993. /* pcv may be any value */
  2994. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  2995. ASSERT(IS_VALID_CODE_PTR(csnp, COMPARESORTEDNODESPROC));
  2996. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  2997. ASSERT(ADD_NODES_IN_SORTED_ORDER((PCLIST)hlist));
  2998. ASSERT(IsListInSortedOrder((PCLIST)hlist, csnp));
  2999. /* Yes. Are there any nodes in this list? */
  3000. ulcNodes = GetNodeCount(hlist);
  3001. ASSERT(ulcNodes < LONG_MAX);
  3002. if (ulcNodes > 0)
  3003. {
  3004. LONG lLow = 0;
  3005. LONG lMiddle = 0;
  3006. LONG lHigh = ulcNodes - 1;
  3007. LONG lCurrent = 0;
  3008. int nCmpResult = 0;
  3009. /* Yes. Search for target. */
  3010. EVAL(GetFirstNode(hlist, phnode));
  3011. while (lLow <= lHigh)
  3012. {
  3013. lMiddle = (lLow + lHigh) / 2;
  3014. /* Which way should we seek in the list to get the lMiddle node? */
  3015. if (lCurrent < lMiddle)
  3016. {
  3017. /* Forward from the current node. */
  3018. while (lCurrent < lMiddle)
  3019. {
  3020. EVAL(GetNextNode(*phnode, phnode));
  3021. lCurrent++;
  3022. }
  3023. }
  3024. else if (lCurrent > lMiddle)
  3025. {
  3026. /* Backward from the current node. */
  3027. while (lCurrent > lMiddle)
  3028. {
  3029. EVAL(GetPrevNode(*phnode, phnode));
  3030. lCurrent--;
  3031. }
  3032. }
  3033. nCmpResult = (*csnp)(pcv, GetNodeData(*phnode));
  3034. if (nCmpResult < 0)
  3035. lHigh = lMiddle - 1;
  3036. else if (nCmpResult > 0)
  3037. lLow = lMiddle + 1;
  3038. else
  3039. /* Found a match at *phnode. */
  3040. break;
  3041. }
  3042. /*
  3043. * If (nCmpResult > 0), insert after *phnode.
  3044. *
  3045. * If (nCmpResult < 0), insert before *phnode.
  3046. *
  3047. * If (nCmpResult == 0), string found at *phnode.
  3048. */
  3049. if (nCmpResult > 0)
  3050. ana = ANA_INSERT_AFTER_NODE;
  3051. else if (nCmpResult < 0)
  3052. ana = ANA_INSERT_BEFORE_NODE;
  3053. else
  3054. ana = ANA_FOUND;
  3055. }
  3056. else
  3057. {
  3058. /* No. Insert the target as the only node in the list. */
  3059. *phnode = NULL;
  3060. ana = ANA_INSERT_AT_HEAD;
  3061. }
  3062. return(ana);
  3063. }
  3064. BOOL IsListInSortedOrder(PCLIST pclist, COMPARESORTEDNODESPROC csnp)
  3065. {
  3066. BOOL bResult = TRUE;
  3067. PNODE pnode;
  3068. /* Don't validate pclist here. */
  3069. ASSERT(ADD_NODES_IN_SORTED_ORDER(pclist));
  3070. ASSERT(IS_VALID_CODE_PTR(csnp, COMPARESORTEDNODESPROC));
  3071. pnode = pclist->node.pnodeNext;
  3072. while (pnode)
  3073. {
  3074. PNODE pnodeNext;
  3075. pnodeNext = pnode->pnodeNext;
  3076. if (pnodeNext)
  3077. {
  3078. if ( (*csnp)(pnode->pcv, pnodeNext->pcv) == CR_FIRST_LARGER)
  3079. {
  3080. bResult = FALSE;
  3081. ERROR_OUT((TEXT("IsListInSortedOrder(): Node [%ld] %#lx > following node [%ld] %#lx."),
  3082. pnode,
  3083. pnode->pcv,
  3084. pnodeNext,
  3085. pnodeNext->pcv));
  3086. break;
  3087. }
  3088. pnode = pnodeNext;
  3089. }
  3090. else
  3091. break;
  3092. }
  3093. return(bResult);
  3094. }
  3095. /*
  3096. ** CreateList()
  3097. **
  3098. ** Creates a new list.
  3099. **
  3100. ** Arguments: void
  3101. **
  3102. ** Returns: Handle to new list, or NULL if unsuccessful.
  3103. **
  3104. ** Side Effects: none
  3105. */
  3106. BOOL CreateList(PCNEWLIST pcnl, PHLIST phlist)
  3107. {
  3108. PLIST plist;
  3109. ASSERT(IS_VALID_STRUCT_PTR(pcnl, CNEWLIST));
  3110. ASSERT(IS_VALID_WRITE_PTR(phlist, HLIST));
  3111. /* Try to allocate new list structure. */
  3112. *phlist = NULL;
  3113. if (AllocateMemory(sizeof(*plist), &plist))
  3114. {
  3115. /* List allocated successfully. Initialize list fields. */
  3116. plist->node.pnodeNext = NULL;
  3117. plist->node.pnodePrev = NULL;
  3118. plist->node.pcv = NULL;
  3119. plist->dwFlags = 0;
  3120. if (IS_FLAG_SET(pcnl->dwFlags, NL_FL_SORTED_ADD))
  3121. {
  3122. SET_FLAG(plist->dwFlags, LIST_FL_SORTED_ADD);
  3123. }
  3124. *phlist = (HLIST)plist;
  3125. ASSERT(IS_VALID_HANDLE(*phlist, LIST));
  3126. }
  3127. return(*phlist != NULL);
  3128. }
  3129. /*
  3130. ** DestroyList()
  3131. **
  3132. ** Deletes a list.
  3133. **
  3134. ** Arguments: hlist - handle to list to be deleted
  3135. **
  3136. ** Returns: void
  3137. **
  3138. ** Side Effects: none
  3139. */
  3140. void DestroyList(HLIST hlist)
  3141. {
  3142. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3143. DeleteAllNodes(hlist);
  3144. /* Delete list. */
  3145. FreeMemory((PLIST)hlist);
  3146. }
  3147. BOOL AddNode(HLIST hlist, COMPARESORTEDNODESPROC csnp, PCVOID pcv, PHNODE phnode)
  3148. {
  3149. BOOL bResult;
  3150. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3151. if (ADD_NODES_IN_SORTED_ORDER((PCLIST)hlist))
  3152. {
  3153. ADDNODEACTION ana;
  3154. ana = SearchForNode(hlist, csnp, pcv, phnode);
  3155. ASSERT(ana != ANA_FOUND);
  3156. switch (ana)
  3157. {
  3158. case ANA_INSERT_BEFORE_NODE:
  3159. bResult = InsertNodeBefore(*phnode, csnp, pcv, phnode);
  3160. break;
  3161. case ANA_INSERT_AFTER_NODE:
  3162. bResult = InsertNodeAfter(*phnode, csnp, pcv, phnode);
  3163. break;
  3164. default:
  3165. ASSERT(ana == ANA_INSERT_AT_HEAD);
  3166. bResult = InsertNodeAtFront(hlist, csnp, pcv, phnode);
  3167. break;
  3168. }
  3169. }
  3170. else
  3171. bResult = InsertNodeAtFront(hlist, csnp, pcv, phnode);
  3172. ASSERT(! bResult ||
  3173. IS_VALID_HANDLE(*phnode, NODE));
  3174. return(bResult);
  3175. }
  3176. /*
  3177. ** InsertNodeAtFront()
  3178. **
  3179. ** Inserts a node at the front of a list.
  3180. **
  3181. ** Arguments: hlist - handle to list that node is to be inserted at head of
  3182. ** pcv - data to be stored in node
  3183. **
  3184. ** Returns: Handle to new node, or NULL if unsuccessful.
  3185. **
  3186. ** Side Effects: none
  3187. */
  3188. BOOL InsertNodeAtFront(HLIST hlist, COMPARESORTEDNODESPROC csnp, PCVOID pcv, PHNODE phnode)
  3189. {
  3190. BOOL bResult;
  3191. PNODE pnode;
  3192. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3193. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3194. #ifdef DEBUG
  3195. /* Make sure the correct index was given for insertion. */
  3196. if (ADD_NODES_IN_SORTED_ORDER((PCLIST)hlist))
  3197. {
  3198. HNODE hnodeNew;
  3199. ADDNODEACTION anaNew;
  3200. anaNew = SearchForNode(hlist, csnp, pcv, &hnodeNew);
  3201. ASSERT(anaNew != ANA_FOUND);
  3202. ASSERT(anaNew == ANA_INSERT_AT_HEAD ||
  3203. (anaNew == ANA_INSERT_BEFORE_NODE &&
  3204. hnodeNew == (HNODE)(((PCLIST)hlist)->node.pnodeNext)));
  3205. }
  3206. #endif
  3207. bResult = AllocateMemory(sizeof(*pnode), &pnode);
  3208. if (bResult)
  3209. {
  3210. /* Add new node to front of list. */
  3211. pnode->pnodePrev = (PNODE)hlist;
  3212. pnode->pnodeNext = ((PLIST)hlist)->node.pnodeNext;
  3213. pnode->pcv = pcv;
  3214. ((PLIST)hlist)->node.pnodeNext = pnode;
  3215. /* Any more nodes in list? */
  3216. if (pnode->pnodeNext)
  3217. pnode->pnodeNext->pnodePrev = pnode;
  3218. *phnode = (HNODE)pnode;
  3219. }
  3220. ASSERT(! bResult ||
  3221. IS_VALID_HANDLE(*phnode, NODE));
  3222. return(bResult);
  3223. }
  3224. /*
  3225. ** InsertNodeBefore()
  3226. **
  3227. ** Inserts a new node in a list before a given node.
  3228. **
  3229. ** Arguments: hnode - handle to node that new node is to be inserted before
  3230. ** pcv - data to be stored in node
  3231. **
  3232. ** Returns: Handle to new node, or NULL if unsuccessful.
  3233. **
  3234. ** Side Effects: none
  3235. */
  3236. BOOL InsertNodeBefore(HNODE hnode, COMPARESORTEDNODESPROC csnp, PCVOID pcv, PHNODE phnode)
  3237. {
  3238. BOOL bResult;
  3239. PNODE pnode;
  3240. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3241. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3242. #ifdef DEBUG
  3243. {
  3244. HLIST hlistParent;
  3245. /* Make sure the correct index was given for insertion. */
  3246. hlistParent = GetList(hnode);
  3247. if (ADD_NODES_IN_SORTED_ORDER((PCLIST)hlistParent))
  3248. {
  3249. HNODE hnodeNew;
  3250. ADDNODEACTION anaNew;
  3251. anaNew = SearchForNode(hlistParent, csnp, pcv, &hnodeNew);
  3252. ASSERT(anaNew != ANA_FOUND);
  3253. ASSERT((anaNew == ANA_INSERT_BEFORE_NODE &&
  3254. hnodeNew == hnode) ||
  3255. (anaNew == ANA_INSERT_AFTER_NODE &&
  3256. hnodeNew == (HNODE)(((PCNODE)hnode)->pnodePrev)) ||
  3257. (anaNew == ANA_INSERT_AT_HEAD &&
  3258. hnode == (HNODE)(((PCLIST)hlistParent)->node.pnodeNext)));
  3259. }
  3260. }
  3261. #endif
  3262. bResult = AllocateMemory(sizeof(*pnode), &pnode);
  3263. if (bResult)
  3264. {
  3265. /* Insert new node before given node. */
  3266. pnode->pnodePrev = ((PNODE)hnode)->pnodePrev;
  3267. pnode->pnodeNext = (PNODE)hnode;
  3268. pnode->pcv = pcv;
  3269. ((PNODE)hnode)->pnodePrev->pnodeNext = pnode;
  3270. ((PNODE)hnode)->pnodePrev = pnode;
  3271. *phnode = (HNODE)pnode;
  3272. }
  3273. ASSERT(! bResult ||
  3274. IS_VALID_HANDLE(*phnode, NODE));
  3275. return(bResult);
  3276. }
  3277. /*
  3278. ** InsertNodeAfter()
  3279. **
  3280. ** Inserts a new node in a list after a given node.
  3281. **
  3282. ** Arguments: hnode - handle to node that new node is to be inserted after
  3283. ** pcv - data to be stored in node
  3284. **
  3285. ** Returns: Handle to new node, or NULL if unsuccessful.
  3286. **
  3287. ** Side Effects: none
  3288. */
  3289. BOOL InsertNodeAfter(HNODE hnode, COMPARESORTEDNODESPROC csnp, PCVOID pcv, PHNODE phnode)
  3290. {
  3291. BOOL bResult;
  3292. PNODE pnode;
  3293. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3294. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3295. #ifdef DEBUG
  3296. /* Make sure the correct index was given for insertion. */
  3297. {
  3298. HLIST hlistParent;
  3299. /* Make sure the correct index was given for insertion. */
  3300. hlistParent = GetList(hnode);
  3301. if (ADD_NODES_IN_SORTED_ORDER((PCLIST)hlistParent))
  3302. {
  3303. HNODE hnodeNew;
  3304. ADDNODEACTION anaNew;
  3305. anaNew = SearchForNode(hlistParent, csnp, pcv, &hnodeNew);
  3306. ASSERT(anaNew != ANA_FOUND);
  3307. ASSERT((anaNew == ANA_INSERT_AFTER_NODE &&
  3308. hnodeNew == hnode) ||
  3309. (anaNew == ANA_INSERT_BEFORE_NODE &&
  3310. hnodeNew == (HNODE)(((PCNODE)hnode)->pnodeNext)));
  3311. }
  3312. }
  3313. #endif
  3314. bResult = AllocateMemory(sizeof(*pnode), &pnode);
  3315. if (bResult)
  3316. {
  3317. /* Insert new node after given node. */
  3318. pnode->pnodePrev = (PNODE)hnode;
  3319. pnode->pnodeNext = ((PNODE)hnode)->pnodeNext;
  3320. pnode->pcv = pcv;
  3321. /* Are we inserting after the tail of the list? */
  3322. if (((PNODE)hnode)->pnodeNext)
  3323. /* No. */
  3324. ((PNODE)hnode)->pnodeNext->pnodePrev = pnode;
  3325. ((PNODE)hnode)->pnodeNext = pnode;
  3326. *phnode = (HNODE)pnode;
  3327. }
  3328. ASSERT(! bResult ||
  3329. IS_VALID_HANDLE(*phnode, NODE));
  3330. return(bResult);
  3331. }
  3332. /*
  3333. ** DeleteNode()
  3334. **
  3335. ** Removes a node from a list.
  3336. **
  3337. ** Arguments: hnode - handle to node to be removed
  3338. **
  3339. ** Returns: void
  3340. **
  3341. ** Side Effects: none
  3342. */
  3343. void DeleteNode(HNODE hnode)
  3344. {
  3345. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3346. /*
  3347. * There is always a previous node for normal list nodes. Even the head
  3348. * list node is preceded by the list's leading LIST node.
  3349. */
  3350. ((PNODE)hnode)->pnodePrev->pnodeNext = ((PNODE)hnode)->pnodeNext;
  3351. /* Any more nodes in list? */
  3352. if (((PNODE)hnode)->pnodeNext)
  3353. ((PNODE)hnode)->pnodeNext->pnodePrev = ((PNODE)hnode)->pnodePrev;
  3354. FreeMemory((PNODE)hnode);
  3355. }
  3356. void DeleteAllNodes(HLIST hlist)
  3357. {
  3358. PNODE pnodePrev;
  3359. PNODE pnode;
  3360. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3361. /* Walk list, starting with first node after head, deleting each node. */
  3362. pnodePrev = ((PLIST)hlist)->node.pnodeNext;
  3363. /*
  3364. * Deleting the tail node in the loop forces us to add an extra
  3365. * comparison to the body of the loop. Trade speed for size here.
  3366. */
  3367. while (pnodePrev)
  3368. {
  3369. pnode = pnodePrev->pnodeNext;
  3370. FreeMemory(pnodePrev);
  3371. pnodePrev = pnode;
  3372. if (pnode)
  3373. pnode = pnode->pnodeNext;
  3374. }
  3375. ((PLIST)hlist)->node.pnodeNext = NULL;
  3376. }
  3377. /*
  3378. ** GetNodeData()
  3379. **
  3380. ** Gets the data stored in a node.
  3381. **
  3382. ** Arguments: hnode - handle to node whose data is to be returned
  3383. **
  3384. ** Returns: Pointer to node's data.
  3385. **
  3386. ** Side Effects: none
  3387. */
  3388. PVOID GetNodeData(HNODE hnode)
  3389. {
  3390. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3391. return((PVOID)(((PNODE)hnode)->pcv));
  3392. }
  3393. /*
  3394. ** SetNodeData()
  3395. **
  3396. ** Sets the data stored in a node.
  3397. **
  3398. ** Arguments: hnode - handle to node whose data is to be set
  3399. ** pcv - node data
  3400. **
  3401. ** Returns: void
  3402. **
  3403. ** Side Effects: none
  3404. */
  3405. void SetNodeData(HNODE hnode, PCVOID pcv)
  3406. {
  3407. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3408. ((PNODE)hnode)->pcv = pcv;
  3409. }
  3410. /*
  3411. ** GetNodeCount()
  3412. **
  3413. ** Counts the number of nodes in a list.
  3414. **
  3415. ** Arguments: hlist - handle to list whose nodes are to be counted
  3416. **
  3417. ** Returns: Number of nodes in list.
  3418. **
  3419. ** Side Effects: none
  3420. **
  3421. ** N.b., this is an O(n) operation since we don't explicitly keep track of the
  3422. ** number of nodes in a list.
  3423. */
  3424. ULONG GetNodeCount(HLIST hlist)
  3425. {
  3426. PNODE pnode;
  3427. ULONG ulcNodes;
  3428. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3429. ulcNodes = 0;
  3430. for (pnode = ((PLIST)hlist)->node.pnodeNext;
  3431. pnode;
  3432. pnode = pnode->pnodeNext)
  3433. {
  3434. ASSERT(ulcNodes < ULONG_MAX);
  3435. ulcNodes++;
  3436. }
  3437. return(ulcNodes);
  3438. }
  3439. /*
  3440. ** IsListEmpty()
  3441. **
  3442. ** Determines whether or not a list is empty.
  3443. **
  3444. ** Arguments: hlist - handle to list to be checked
  3445. **
  3446. ** Returns: TRUE if list is empty, or FALSE if not.
  3447. **
  3448. ** Side Effects: none
  3449. */
  3450. BOOL IsListEmpty(HLIST hlist)
  3451. {
  3452. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3453. return(((PLIST)hlist)->node.pnodeNext == NULL);
  3454. }
  3455. /*
  3456. ** GetFirstNode()
  3457. **
  3458. ** Gets the head node in a list.
  3459. **
  3460. ** Arguments: hlist - handle to list whose head node is to be retrieved
  3461. **
  3462. ** Returns: Handle to head list node, or NULL if list is empty.
  3463. **
  3464. ** Side Effects: none
  3465. */
  3466. BOOL GetFirstNode(HLIST hlist, PHNODE phnode)
  3467. {
  3468. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3469. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3470. *phnode = (HNODE)(((PLIST)hlist)->node.pnodeNext);
  3471. ASSERT(! *phnode || IS_VALID_HANDLE(*phnode, NODE));
  3472. return(*phnode != NULL);
  3473. }
  3474. /*
  3475. ** GetNextNode()
  3476. **
  3477. ** Gets the next node in a list.
  3478. **
  3479. ** Arguments: hnode - handle to current node
  3480. ** phnode - pointer to HNODE to be filled in with handle to next
  3481. ** node in list, *phnode is only valid if GetNextNode()
  3482. ** returns TRUE
  3483. **
  3484. ** Returns: TRUE if there is another node in the list, or FALSE if there
  3485. ** are no more nodes in the list.
  3486. **
  3487. ** Side Effects: none
  3488. */
  3489. BOOL GetNextNode(HNODE hnode, PHNODE phnode)
  3490. {
  3491. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3492. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3493. *phnode = (HNODE)(((PNODE)hnode)->pnodeNext);
  3494. ASSERT(! *phnode || IS_VALID_HANDLE(*phnode, NODE));
  3495. return(*phnode != NULL);
  3496. }
  3497. /*
  3498. ** GetPrevNode()
  3499. **
  3500. ** Gets the previous node in a list.
  3501. **
  3502. ** Arguments: hnode - handle to current node
  3503. **
  3504. ** Returns: Handle to previous node in list, or NULL if there are no
  3505. ** previous nodes in the list.
  3506. **
  3507. ** Side Effects: none
  3508. */
  3509. BOOL GetPrevNode(HNODE hnode, PHNODE phnode)
  3510. {
  3511. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3512. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3513. /* Is this the first node in the list? */
  3514. if (((PNODE)hnode)->pnodePrev->pnodePrev)
  3515. {
  3516. *phnode = (HNODE)(((PNODE)hnode)->pnodePrev);
  3517. ASSERT(IS_VALID_HANDLE(*phnode, NODE));
  3518. }
  3519. else
  3520. *phnode = NULL;
  3521. return(*phnode != NULL);
  3522. }
  3523. /*
  3524. ** AppendList()
  3525. **
  3526. ** Appends one list on to another, leaving the source list empty.
  3527. **
  3528. ** Arguments: hlistDest - handle to destination list to append to
  3529. ** hlistSrc - handle to source list to truncate
  3530. **
  3531. ** Returns: void
  3532. **
  3533. ** Side Effects: none
  3534. **
  3535. ** N.b., all HNODEs from both lists remain valid.
  3536. */
  3537. void AppendList(HLIST hlistDest, HLIST hlistSrc)
  3538. {
  3539. PNODE pnode;
  3540. ASSERT(IS_VALID_HANDLE(hlistDest, LIST));
  3541. ASSERT(IS_VALID_HANDLE(hlistSrc, LIST));
  3542. if (hlistSrc != hlistDest)
  3543. {
  3544. /* Find last node in destination list to append to. */
  3545. /*
  3546. * N.b., start with the actual LIST node here, not the first node in the
  3547. * list, in case the list is empty.
  3548. */
  3549. for (pnode = &((PLIST)hlistDest)->node;
  3550. pnode->pnodeNext;
  3551. pnode = pnode->pnodeNext)
  3552. ;
  3553. /* Append the source list to the last node in the destination list. */
  3554. pnode->pnodeNext = ((PLIST)hlistSrc)->node.pnodeNext;
  3555. if (pnode->pnodeNext)
  3556. pnode->pnodeNext->pnodePrev = pnode;
  3557. ((PLIST)hlistSrc)->node.pnodeNext = NULL;
  3558. }
  3559. else
  3560. WARNING_OUT((TEXT("AppendList(): Source list same as destination list (%#lx)."),
  3561. hlistDest));
  3562. }
  3563. BOOL SearchSortedList(HLIST hlist, COMPARESORTEDNODESPROC csnp,
  3564. PCVOID pcv, PHNODE phnode)
  3565. {
  3566. BOOL bResult;
  3567. /* pcv may be any value */
  3568. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3569. ASSERT(IS_VALID_CODE_PTR(csnp, COMPARESORTEDNODESPROC));
  3570. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  3571. ASSERT(ADD_NODES_IN_SORTED_ORDER((PCLIST)hlist));
  3572. bResult = (SearchForNode(hlist, csnp, pcv, phnode) == ANA_FOUND);
  3573. ASSERT(! bResult ||
  3574. IS_VALID_HANDLE(*phnode, NODE));
  3575. return(bResult);
  3576. }
  3577. BOOL SearchUnsortedList(HLIST hlist, COMPAREUNSORTEDNODESPROC cunp,
  3578. PCVOID pcv, PHNODE phn)
  3579. {
  3580. PNODE pnode;
  3581. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3582. ASSERT(IS_VALID_CODE_PTR(cunp, COMPAREUNSORTEDNODESPROC));
  3583. ASSERT(IS_VALID_WRITE_PTR(phn, HNODE));
  3584. *phn = NULL;
  3585. for (pnode = ((PLIST)hlist)->node.pnodeNext;
  3586. pnode;
  3587. pnode = pnode->pnodeNext)
  3588. {
  3589. if ((*cunp)(pcv, pnode->pcv) == CR_EQUAL)
  3590. {
  3591. *phn = (HNODE)pnode;
  3592. break;
  3593. }
  3594. }
  3595. return(*phn != NULL);
  3596. }
  3597. /*
  3598. ** WalkList()
  3599. **
  3600. ** Walks a list, calling a callback function with each list node's data and
  3601. ** caller supplied data.
  3602. **
  3603. ** Arguments: hlist - handle to list to be searched
  3604. ** wlp - callback function to be called with each list node's
  3605. ** data, called as:
  3606. **
  3607. ** bContinue = (*wlwdp)(pv, pvRefData);
  3608. **
  3609. ** wlp should return TRUE to continue the walk, or FALSE
  3610. ** to halt the walk
  3611. ** pvRefData - data to pass to callback function
  3612. **
  3613. ** Returns: FALSE if callback function aborted the walk. TRUE if the
  3614. ** walk completed.
  3615. **
  3616. ** N.b., the callback function is allowed to delete the node it is passed.
  3617. **
  3618. ** Side Effects: none
  3619. */
  3620. BOOL WalkList(HLIST hlist, WALKLIST wlp, PVOID pvRefData)
  3621. {
  3622. BOOL bResult = TRUE;
  3623. PNODE pnode;
  3624. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  3625. ASSERT(IS_VALID_CODE_PTR(wlp, WALKLISTPROC));
  3626. pnode = ((PLIST)hlist)->node.pnodeNext;
  3627. while (pnode)
  3628. {
  3629. PNODE pnodeNext;
  3630. pnodeNext = pnode->pnodeNext;
  3631. if ((*wlp)((PVOID)(pnode->pcv), pvRefData))
  3632. pnode = pnodeNext;
  3633. else
  3634. {
  3635. bResult = FALSE;
  3636. break;
  3637. }
  3638. }
  3639. return(bResult);
  3640. }
  3641. #ifdef DEBUG
  3642. HLIST GetList(HNODE hnode)
  3643. {
  3644. PCNODE pcnode;
  3645. ASSERT(IS_VALID_HANDLE(hnode, NODE));
  3646. ASSERT(((PCNODE)hnode)->pnodePrev);
  3647. for (pcnode = (PCNODE)hnode; pcnode->pnodePrev; pcnode = pcnode->pnodePrev)
  3648. ;
  3649. return((HLIST)pcnode);
  3650. }
  3651. #endif
  3652. /* Macros
  3653. *********/
  3654. COMPARISONRESULT MyMemComp(PCVOID pcv1, PCVOID pcv2, DWORD dwcbSize)
  3655. {
  3656. int nResult = 0;
  3657. PCBYTE pcbyte1 = pcv1;
  3658. PCBYTE pcbyte2 = pcv2;
  3659. ASSERT(IS_VALID_READ_BUFFER_PTR(pcv1, BYTE, (UINT)dwcbSize));
  3660. ASSERT(IS_VALID_READ_BUFFER_PTR(pcv2, BYTE, (UINT)dwcbSize));
  3661. while (dwcbSize > 0 &&
  3662. ! (nResult = *pcbyte1 - *pcbyte2))
  3663. {
  3664. pcbyte1++;
  3665. pcbyte2++;
  3666. dwcbSize--;
  3667. }
  3668. return(MapIntToComparisonResult(nResult));
  3669. }
  3670. BOOL AllocateMemory(DWORD dwcbSize, PVOID *ppvNew)
  3671. {
  3672. *ppvNew = PoolMemGetAlignedMemory (g_BrfcasePool, dwcbSize);
  3673. return(*ppvNew != NULL);
  3674. }
  3675. void FreeMemory(PVOID pvOld)
  3676. {
  3677. PoolMemReleaseMemory (g_BrfcasePool, pvOld);
  3678. }
  3679. BOOL ReallocateMemory(PVOID pvOld, DWORD dwcbOldSize, DWORD dwcbNewSize, PVOID *ppvNew)
  3680. {
  3681. if (AllocateMemory (dwcbNewSize, ppvNew)) {
  3682. CopyMemory (*ppvNew, pvOld, dwcbOldSize);
  3683. }
  3684. return(*ppvNew != NULL);
  3685. }
  3686. /* Constants
  3687. ************/
  3688. /* PATHLIST PTRARRAY allocation parameters */
  3689. #define NUM_START_PATHS (32)
  3690. #define NUM_PATHS_TO_ADD (32)
  3691. /* PATHLIST string table allocation parameters */
  3692. #define NUM_PATH_HASH_BUCKETS (67)
  3693. /* Types
  3694. ********/
  3695. /* path list */
  3696. typedef struct _pathlist
  3697. {
  3698. /* array of pointers to PATHs */
  3699. HPTRARRAY hpa;
  3700. /* list of volumes */
  3701. HVOLUMELIST hvl;
  3702. /* table of path suffix strings */
  3703. HSTRINGTABLE hst;
  3704. }
  3705. PATHLIST;
  3706. DECLARE_STANDARD_TYPES(PATHLIST);
  3707. /* path structure */
  3708. typedef struct _path
  3709. {
  3710. /* reference count */
  3711. ULONG ulcLock;
  3712. /* handle to parent volume */
  3713. HVOLUME hvol;
  3714. /* handle to path suffix string */
  3715. HSTRING hsPathSuffix;
  3716. /* pointer to PATH's parent PATHLIST */
  3717. PPATHLIST pplParent;
  3718. }
  3719. PATH;
  3720. DECLARE_STANDARD_TYPES(PATH);
  3721. /* PATH search structure used by PathSearchCmp() */
  3722. typedef struct _pathsearchinfo
  3723. {
  3724. HVOLUME hvol;
  3725. LPCTSTR pcszPathSuffix;
  3726. }
  3727. PATHSEARCHINFO;
  3728. DECLARE_STANDARD_TYPES(PATHSEARCHINFO);
  3729. /* database path list header */
  3730. typedef struct _dbpathlistheader
  3731. {
  3732. /* number of paths in list */
  3733. LONG lcPaths;
  3734. }
  3735. DBPATHLISTHEADER;
  3736. DECLARE_STANDARD_TYPES(DBPATHLISTHEADER);
  3737. /* database path structure */
  3738. typedef struct _dbpath
  3739. {
  3740. /* old handle to path */
  3741. HPATH hpath;
  3742. /* old handle to parent volume */
  3743. HVOLUME hvol;
  3744. /* old handle to path suffix string */
  3745. HSTRING hsPathSuffix;
  3746. }
  3747. DBPATH;
  3748. DECLARE_STANDARD_TYPES(DBPATH);
  3749. /* Module Prototypes
  3750. ********************/
  3751. COMPARISONRESULT PathSortCmp(PCVOID, PCVOID);
  3752. COMPARISONRESULT PathSearchCmp(PCVOID, PCVOID);
  3753. BOOL UnifyPath(PPATHLIST, HVOLUME, LPCTSTR, PPATH *);
  3754. BOOL CreatePath(PPATHLIST, HVOLUME, LPCTSTR, PPATH *);
  3755. void DestroyPath(PPATH);
  3756. void UnlinkPath(PCPATH);
  3757. void LockPath(PPATH);
  3758. BOOL UnlockPath(PPATH);
  3759. PATHRESULT TranslateVOLUMERESULTToPATHRESULT(VOLUMERESULT);
  3760. TWINRESULT WritePath(HCACHEDFILE, PPATH);
  3761. TWINRESULT ReadPath(HCACHEDFILE, PPATHLIST, HHANDLETRANS, HHANDLETRANS, HHANDLETRANS);
  3762. /*
  3763. ** PathSortCmp()
  3764. **
  3765. ** Pointer comparison function used to sort the module array of paths.
  3766. **
  3767. ** Arguments: pcpath1 - pointer to first path
  3768. ** pcpath2 - pointer to second path
  3769. **
  3770. ** Returns:
  3771. **
  3772. ** Side Effects: none
  3773. **
  3774. ** The internal paths are sorted by:
  3775. ** 1) volume
  3776. ** 2) path suffix
  3777. ** 3) pointer value
  3778. */
  3779. COMPARISONRESULT PathSortCmp(PCVOID pcpath1, PCVOID pcpath2)
  3780. {
  3781. COMPARISONRESULT cr;
  3782. ASSERT(IS_VALID_STRUCT_PTR(pcpath1, CPATH));
  3783. ASSERT(IS_VALID_STRUCT_PTR(pcpath2, CPATH));
  3784. cr = CompareVolumes(((PCPATH)pcpath1)->hvol,
  3785. ((PCPATH)pcpath2)->hvol);
  3786. if (cr == CR_EQUAL)
  3787. {
  3788. cr = ComparePathStringsByHandle(((PCPATH)pcpath1)->hsPathSuffix,
  3789. ((PCPATH)pcpath2)->hsPathSuffix);
  3790. if (cr == CR_EQUAL)
  3791. cr = ComparePointers(pcpath1, pcpath2);
  3792. }
  3793. return(cr);
  3794. }
  3795. /*
  3796. ** PathSearchCmp()
  3797. **
  3798. ** Pointer comparison function used to search for a path.
  3799. **
  3800. ** Arguments: pcpathsi - pointer to PATHSEARCHINFO describing path to
  3801. ** search for
  3802. ** pcpath - pointer to path to examine
  3803. **
  3804. ** Returns:
  3805. **
  3806. ** Side Effects: none
  3807. **
  3808. ** The internal paths are searched by:
  3809. ** 1) volume
  3810. ** 2) path suffix string
  3811. */
  3812. COMPARISONRESULT PathSearchCmp(PCVOID pcpathsi, PCVOID pcpath)
  3813. {
  3814. COMPARISONRESULT cr;
  3815. ASSERT(IS_VALID_STRUCT_PTR(pcpath, CPATH));
  3816. cr = CompareVolumes(((PCPATHSEARCHINFO)pcpathsi)->hvol,
  3817. ((PCPATH)pcpath)->hvol);
  3818. if (cr == CR_EQUAL)
  3819. cr = ComparePathStrings(((PCPATHSEARCHINFO)pcpathsi)->pcszPathSuffix,
  3820. GetBfcString(((PCPATH)pcpath)->hsPathSuffix));
  3821. return(cr);
  3822. }
  3823. BOOL UnifyPath(PPATHLIST ppl, HVOLUME hvol, LPCTSTR pcszPathSuffix,
  3824. PPATH *pppath)
  3825. {
  3826. BOOL bResult = FALSE;
  3827. ASSERT(IS_VALID_STRUCT_PTR(ppl, CPATHLIST));
  3828. ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
  3829. ASSERT(IsValidPathSuffix(pcszPathSuffix));
  3830. ASSERT(IS_VALID_WRITE_PTR(pppath, PPATH));
  3831. /* Allocate space for PATH structure. */
  3832. if (AllocateMemory(sizeof(**pppath), pppath))
  3833. {
  3834. if (CopyVolume(hvol, ppl->hvl, &((*pppath)->hvol)))
  3835. {
  3836. if (AddString(pcszPathSuffix, ppl->hst, GetHashBucketIndex, &((*pppath)->hsPathSuffix)))
  3837. {
  3838. ARRAYINDEX aiUnused;
  3839. /* Initialize remaining PATH fields. */
  3840. (*pppath)->ulcLock = 0;
  3841. (*pppath)->pplParent = ppl;
  3842. /* Add new PATH to array. */
  3843. if (AddPtr(ppl->hpa, PathSortCmp, *pppath, &aiUnused))
  3844. bResult = TRUE;
  3845. else
  3846. {
  3847. DeleteString((*pppath)->hsPathSuffix);
  3848. UNIFYPATH_BAIL1:
  3849. DeleteVolume((*pppath)->hvol);
  3850. UNIFYPATH_BAIL2:
  3851. FreeMemory(*pppath);
  3852. }
  3853. }
  3854. else
  3855. goto UNIFYPATH_BAIL1;
  3856. }
  3857. else
  3858. goto UNIFYPATH_BAIL2;
  3859. }
  3860. ASSERT(! bResult ||
  3861. IS_VALID_STRUCT_PTR(*pppath, CPATH));
  3862. return(bResult);
  3863. }
  3864. BOOL CreatePath(PPATHLIST ppl, HVOLUME hvol, LPCTSTR pcszPathSuffix,
  3865. PPATH *pppath)
  3866. {
  3867. BOOL bResult;
  3868. ARRAYINDEX aiFound;
  3869. PATHSEARCHINFO pathsi;
  3870. ASSERT(IS_VALID_STRUCT_PTR(ppl, CPATHLIST));
  3871. ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
  3872. ASSERT(IsValidPathSuffix(pcszPathSuffix));
  3873. ASSERT(IS_VALID_WRITE_PTR(pppath, CPATH));
  3874. /* Does a path for the given volume and path suffix already exist? */
  3875. pathsi.hvol = hvol;
  3876. pathsi.pcszPathSuffix = pcszPathSuffix;
  3877. bResult = SearchSortedArray(ppl->hpa, &PathSearchCmp, &pathsi, &aiFound);
  3878. if (bResult)
  3879. /* Yes. Return it. */
  3880. *pppath = GetPtr(ppl->hpa, aiFound);
  3881. else
  3882. bResult = UnifyPath(ppl, hvol, pcszPathSuffix, pppath);
  3883. if (bResult)
  3884. LockPath(*pppath);
  3885. ASSERT(! bResult ||
  3886. IS_VALID_STRUCT_PTR(*pppath, CPATH));
  3887. return(bResult);
  3888. }
  3889. void DestroyPath(PPATH ppath)
  3890. {
  3891. ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
  3892. DeleteVolume(ppath->hvol);
  3893. DeleteString(ppath->hsPathSuffix);
  3894. FreeMemory(ppath);
  3895. }
  3896. void UnlinkPath(PCPATH pcpath)
  3897. {
  3898. HPTRARRAY hpa;
  3899. ARRAYINDEX aiFound;
  3900. ASSERT(IS_VALID_STRUCT_PTR(pcpath, CPATH));
  3901. hpa = pcpath->pplParent->hpa;
  3902. if (EVAL(SearchSortedArray(hpa, &PathSortCmp, pcpath, &aiFound)))
  3903. {
  3904. ASSERT(GetPtr(hpa, aiFound) == pcpath);
  3905. DeletePtr(hpa, aiFound);
  3906. }
  3907. }
  3908. void LockPath(PPATH ppath)
  3909. {
  3910. ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
  3911. ASSERT(ppath->ulcLock < ULONG_MAX);
  3912. ppath->ulcLock++;
  3913. }
  3914. BOOL UnlockPath(PPATH ppath)
  3915. {
  3916. ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
  3917. if (EVAL(ppath->ulcLock > 0))
  3918. ppath->ulcLock--;
  3919. return(ppath->ulcLock > 0);
  3920. }
  3921. PATHRESULT TranslateVOLUMERESULTToPATHRESULT(VOLUMERESULT vr)
  3922. {
  3923. PATHRESULT pr;
  3924. switch (vr)
  3925. {
  3926. case VR_SUCCESS:
  3927. pr = PR_SUCCESS;
  3928. break;
  3929. case VR_UNAVAILABLE_VOLUME:
  3930. pr = PR_UNAVAILABLE_VOLUME;
  3931. break;
  3932. case VR_OUT_OF_MEMORY:
  3933. pr = PR_OUT_OF_MEMORY;
  3934. break;
  3935. default:
  3936. ASSERT(vr == VR_INVALID_PATH);
  3937. pr = PR_INVALID_PATH;
  3938. break;
  3939. }
  3940. return(pr);
  3941. }
  3942. TWINRESULT WritePath(HCACHEDFILE hcf, PPATH ppath)
  3943. {
  3944. TWINRESULT tr;
  3945. DBPATH dbpath;
  3946. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  3947. ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
  3948. /* Write database path. */
  3949. dbpath.hpath = (HPATH)ppath;
  3950. dbpath.hvol = ppath->hvol;
  3951. dbpath.hsPathSuffix = ppath->hsPathSuffix;
  3952. if (WriteToCachedFile(hcf, (PCVOID)&dbpath, sizeof(dbpath), NULL))
  3953. tr = TR_SUCCESS;
  3954. else
  3955. tr = TR_BRIEFCASE_WRITE_FAILED;
  3956. return(tr);
  3957. }
  3958. TWINRESULT ReadPath(HCACHEDFILE hcf, PPATHLIST ppl,
  3959. HHANDLETRANS hhtVolumes,
  3960. HHANDLETRANS hhtStrings,
  3961. HHANDLETRANS hhtPaths)
  3962. {
  3963. TWINRESULT tr;
  3964. DBPATH dbpath;
  3965. DWORD dwcbRead;
  3966. HVOLUME hvol;
  3967. HSTRING hsPathSuffix;
  3968. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  3969. ASSERT(IS_VALID_STRUCT_PTR(ppl, CPATHLIST));
  3970. ASSERT(IS_VALID_HANDLE(hhtVolumes, HANDLETRANS));
  3971. ASSERT(IS_VALID_HANDLE(hhtStrings, HANDLETRANS));
  3972. ASSERT(IS_VALID_HANDLE(hhtPaths, HANDLETRANS));
  3973. if (ReadFromCachedFile(hcf, &dbpath, sizeof(dbpath), &dwcbRead) &&
  3974. dwcbRead == sizeof(dbpath) &&
  3975. TranslateHandle(hhtVolumes, (HGENERIC)(dbpath.hvol), (PHGENERIC)&hvol) &&
  3976. TranslateHandle(hhtStrings, (HGENERIC)(dbpath.hsPathSuffix), (PHGENERIC)&hsPathSuffix))
  3977. {
  3978. PPATH ppath;
  3979. if (CreatePath(ppl, hvol, GetBfcString(hsPathSuffix), &ppath))
  3980. {
  3981. /*
  3982. * To leave read paths with 0 initial lock count, we must undo
  3983. * the LockPath() performed by CreatePath().
  3984. */
  3985. UnlockPath(ppath);
  3986. if (AddHandleToHandleTranslator(hhtPaths,
  3987. (HGENERIC)(dbpath.hpath),
  3988. (HGENERIC)ppath))
  3989. tr = TR_SUCCESS;
  3990. else
  3991. {
  3992. UnlinkPath(ppath);
  3993. DestroyPath(ppath);
  3994. tr = TR_OUT_OF_MEMORY;
  3995. }
  3996. }
  3997. else
  3998. tr = TR_OUT_OF_MEMORY;
  3999. }
  4000. else
  4001. tr = TR_CORRUPT_BRIEFCASE;
  4002. return(tr);
  4003. }
  4004. BOOL CreatePathList(DWORD dwFlags, HWND hwndOwner, PHPATHLIST phpl)
  4005. {
  4006. BOOL bResult = FALSE;
  4007. PPATHLIST ppl;
  4008. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RLI_IFLAGS));
  4009. ASSERT(IS_FLAG_CLEAR(dwFlags, RLI_IFL_ALLOW_UI) ||
  4010. IS_VALID_HANDLE(hwndOwner, WND));
  4011. ASSERT(IS_VALID_WRITE_PTR(phpl, HPATHLIST));
  4012. if (AllocateMemory(sizeof(*ppl), &ppl))
  4013. {
  4014. NEWPTRARRAY npa;
  4015. /* Create pointer array of paths. */
  4016. npa.aicInitialPtrs = NUM_START_PATHS;
  4017. npa.aicAllocGranularity = NUM_PATHS_TO_ADD;
  4018. npa.dwFlags = NPA_FL_SORTED_ADD;
  4019. if (CreatePtrArray(&npa, &(ppl->hpa)))
  4020. {
  4021. if (CreateVolumeList(dwFlags, hwndOwner, &(ppl->hvl)))
  4022. {
  4023. NEWSTRINGTABLE nszt;
  4024. /* Create string table for path suffix strings. */
  4025. nszt.hbc = NUM_PATH_HASH_BUCKETS;
  4026. if (CreateStringTable(&nszt, &(ppl->hst)))
  4027. {
  4028. *phpl = (HPATHLIST)ppl;
  4029. bResult = TRUE;
  4030. }
  4031. else
  4032. {
  4033. DestroyVolumeList(ppl->hvl);
  4034. CREATEPATHLIST_BAIL1:
  4035. DestroyPtrArray(ppl->hpa);
  4036. CREATEPATHLIST_BAIL2:
  4037. FreeMemory(ppl);
  4038. }
  4039. }
  4040. else
  4041. goto CREATEPATHLIST_BAIL1;
  4042. }
  4043. else
  4044. goto CREATEPATHLIST_BAIL2;
  4045. }
  4046. ASSERT(! bResult ||
  4047. IS_VALID_HANDLE(*phpl, PATHLIST));
  4048. return(bResult);
  4049. }
  4050. void DestroyPathList(HPATHLIST hpl)
  4051. {
  4052. ARRAYINDEX aicPtrs;
  4053. ARRAYINDEX ai;
  4054. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4055. /* First free all paths in array. */
  4056. aicPtrs = GetPtrCount(((PCPATHLIST)hpl)->hpa);
  4057. for (ai = 0; ai < aicPtrs; ai++)
  4058. DestroyPath(GetPtr(((PCPATHLIST)hpl)->hpa, ai));
  4059. /* Now wipe out the array. */
  4060. DestroyPtrArray(((PCPATHLIST)hpl)->hpa);
  4061. ASSERT(! GetVolumeCount(((PCPATHLIST)hpl)->hvl));
  4062. DestroyVolumeList(((PCPATHLIST)hpl)->hvl);
  4063. ASSERT(! GetStringCount(((PCPATHLIST)hpl)->hst));
  4064. DestroyStringTable(((PCPATHLIST)hpl)->hst);
  4065. FreeMemory((PPATHLIST)hpl);
  4066. }
  4067. void InvalidatePathListInfo(HPATHLIST hpl)
  4068. {
  4069. InvalidateVolumeListInfo(((PCPATHLIST)hpl)->hvl);
  4070. }
  4071. void ClearPathListInfo(HPATHLIST hpl)
  4072. {
  4073. ClearVolumeListInfo(((PCPATHLIST)hpl)->hvl);
  4074. }
  4075. PATHRESULT AddPath(HPATHLIST hpl, LPCTSTR pcszPath, PHPATH phpath)
  4076. {
  4077. PATHRESULT pr;
  4078. HVOLUME hvol;
  4079. TCHAR rgchPathSuffix[MAX_PATH_LEN];
  4080. LPCTSTR pszPath;
  4081. WCHAR szUnicode[MAX_PATH];
  4082. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4083. ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
  4084. ASSERT(IS_VALID_WRITE_PTR(phpath, HPATH));
  4085. // On NT, we want to convert a unicode string to an ANSI shortened path for
  4086. // the sake of interop
  4087. {
  4088. CHAR szAnsi[MAX_PATH];
  4089. szUnicode[0] = L'\0';
  4090. WideCharToMultiByte( OurGetACP(), 0, pcszPath, -1, szAnsi, ARRAYSIZE(szAnsi), NULL, NULL);
  4091. MultiByteToWideChar( OurGetACP(), 0, szAnsi, -1, szUnicode, ARRAYSIZE(szUnicode));
  4092. if (lstrcmp(szUnicode, pcszPath))
  4093. {
  4094. // Cannot convert losslessly from Unicode -> Ansi, so get the short path
  4095. lstrcpy(szUnicode, pcszPath);
  4096. SheShortenPath(szUnicode, TRUE);
  4097. pszPath = szUnicode;
  4098. }
  4099. else
  4100. {
  4101. // It will convert OK, so just use the original
  4102. pszPath = pcszPath;
  4103. }
  4104. }
  4105. pr = TranslateVOLUMERESULTToPATHRESULT(
  4106. AddVolume(((PCPATHLIST)hpl)->hvl, pszPath, &hvol, rgchPathSuffix));
  4107. if (pr == PR_SUCCESS)
  4108. {
  4109. PPATH ppath;
  4110. if (CreatePath((PPATHLIST)hpl, hvol, rgchPathSuffix, &ppath))
  4111. *phpath = (HPATH)ppath;
  4112. else
  4113. pr = PR_OUT_OF_MEMORY;
  4114. DeleteVolume(hvol);
  4115. }
  4116. ASSERT(pr != PR_SUCCESS ||
  4117. IS_VALID_HANDLE(*phpath, PATH));
  4118. return(pr);
  4119. }
  4120. BOOL AddChildPath(HPATHLIST hpl, HPATH hpathParent,
  4121. LPCTSTR pcszSubPath, PHPATH phpathChild)
  4122. {
  4123. BOOL bResult;
  4124. TCHAR rgchChildPathSuffix[MAX_PATH_LEN];
  4125. LPCTSTR pcszPathSuffix;
  4126. LPTSTR pszPathSuffixEnd;
  4127. PPATH ppathChild;
  4128. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4129. ASSERT(IS_VALID_HANDLE(hpathParent, PATH));
  4130. ASSERT(IS_VALID_STRING_PTR(pcszSubPath, CSTR));
  4131. ASSERT(IS_VALID_WRITE_PTR(phpathChild, HPATH));
  4132. ComposePath(rgchChildPathSuffix,
  4133. GetBfcString(((PCPATH)hpathParent)->hsPathSuffix),
  4134. pcszSubPath);
  4135. pcszPathSuffix = rgchChildPathSuffix;
  4136. if (IS_SLASH(*pcszPathSuffix))
  4137. pcszPathSuffix++;
  4138. pszPathSuffixEnd = CharPrev(pcszPathSuffix,
  4139. pcszPathSuffix + lstrlen(pcszPathSuffix));
  4140. if (IS_SLASH(*pszPathSuffixEnd))
  4141. *pszPathSuffixEnd = TEXT('\0');
  4142. ASSERT(IsValidPathSuffix(pcszPathSuffix));
  4143. bResult = CreatePath((PPATHLIST)hpl, ((PCPATH)hpathParent)->hvol,
  4144. pcszPathSuffix, &ppathChild);
  4145. if (bResult)
  4146. *phpathChild = (HPATH)ppathChild;
  4147. ASSERT(! bResult ||
  4148. IS_VALID_HANDLE(*phpathChild, PATH));
  4149. return(bResult);
  4150. }
  4151. void DeletePath(HPATH hpath)
  4152. {
  4153. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4154. if (! UnlockPath((PPATH)hpath))
  4155. {
  4156. UnlinkPath((PPATH)hpath);
  4157. DestroyPath((PPATH)hpath);
  4158. }
  4159. }
  4160. BOOL CopyPath(HPATH hpathSrc, HPATHLIST hplDest, PHPATH phpathCopy)
  4161. {
  4162. BOOL bResult;
  4163. PPATH ppath;
  4164. ASSERT(IS_VALID_HANDLE(hpathSrc, PATH));
  4165. ASSERT(IS_VALID_HANDLE(hplDest, PATHLIST));
  4166. ASSERT(IS_VALID_WRITE_PTR(phpathCopy, HPATH));
  4167. /* Is the destination path list the source path's path list? */
  4168. if (((PCPATH)hpathSrc)->pplParent == (PCPATHLIST)hplDest)
  4169. {
  4170. /* Yes. Use the source path. */
  4171. LockPath((PPATH)hpathSrc);
  4172. ppath = (PPATH)hpathSrc;
  4173. bResult = TRUE;
  4174. }
  4175. else
  4176. bResult = CreatePath((PPATHLIST)hplDest, ((PCPATH)hpathSrc)->hvol,
  4177. GetBfcString(((PCPATH)hpathSrc)->hsPathSuffix),
  4178. &ppath);
  4179. if (bResult)
  4180. *phpathCopy = (HPATH)ppath;
  4181. ASSERT(! bResult ||
  4182. IS_VALID_HANDLE(*phpathCopy, PATH));
  4183. return(bResult);
  4184. }
  4185. void GetPathString(HPATH hpath, LPTSTR pszPathBuf)
  4186. {
  4187. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4188. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathBuf, STR, MAX_PATH_LEN));
  4189. GetPathRootString(hpath, pszPathBuf);
  4190. CatPath(pszPathBuf, GetBfcString(((PPATH)hpath)->hsPathSuffix));
  4191. ASSERT(IsCanonicalPath(pszPathBuf));
  4192. }
  4193. void GetPathRootString(HPATH hpath, LPTSTR pszPathRootBuf)
  4194. {
  4195. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4196. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathRootBuf, STR, MAX_PATH_LEN));
  4197. GetVolumeRootPath(((PPATH)hpath)->hvol, pszPathRootBuf);
  4198. ASSERT(IsCanonicalPath(pszPathRootBuf));
  4199. }
  4200. void GetPathSuffixString(HPATH hpath, LPTSTR pszPathSuffixBuf)
  4201. {
  4202. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4203. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathSuffixBuf, STR, MAX_PATH_LEN));
  4204. ASSERT(lstrlen(GetBfcString(((PPATH)hpath)->hsPathSuffix)) < MAX_PATH_LEN);
  4205. MyLStrCpyN(pszPathSuffixBuf, GetBfcString(((PPATH)hpath)->hsPathSuffix), MAX_PATH_LEN);
  4206. ASSERT(IsValidPathSuffix(pszPathSuffixBuf));
  4207. }
  4208. BOOL AllocatePathString(HPATH hpath, LPTSTR *ppszPath)
  4209. {
  4210. TCHAR rgchPath[MAX_PATH_LEN];
  4211. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4212. ASSERT(IS_VALID_WRITE_PTR(ppszPath, LPTSTR));
  4213. GetPathString(hpath, rgchPath);
  4214. return(StringCopy2(rgchPath, ppszPath));
  4215. }
  4216. ULONG GetPathCount(HPATHLIST hpl)
  4217. {
  4218. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4219. return(GetPtrCount(((PCPATHLIST)hpl)->hpa));
  4220. }
  4221. BOOL IsPathVolumeAvailable(HPATH hpath)
  4222. {
  4223. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4224. return(IsVolumeAvailable(((PCPATH)hpath)->hvol));
  4225. }
  4226. HVOLUMEID GetPathVolumeID(HPATH hpath)
  4227. {
  4228. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4229. return((HVOLUMEID)hpath);
  4230. }
  4231. /*
  4232. ** MyIsPathOnVolume()
  4233. **
  4234. **
  4235. **
  4236. ** Arguments:
  4237. **
  4238. ** Returns:
  4239. **
  4240. ** Side Effects: none
  4241. **
  4242. ** MyIsPathOnVolume() will fail for a new root path alias for a volume. E.g.,
  4243. ** if the same net resource is connected to both X: and Y:, MyIsPathOnVolume()
  4244. ** will only return TRUE for the drive root path that the net resource was
  4245. ** connected to through the given HVOLUME.
  4246. */
  4247. BOOL MyIsPathOnVolume(LPCTSTR pcszPath, HPATH hpath)
  4248. {
  4249. BOOL bResult;
  4250. TCHAR rgchVolumeRootPath[MAX_PATH_LEN];
  4251. ASSERT(IsFullPath(pcszPath));
  4252. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4253. if (IsVolumeAvailable(((PPATH)hpath)->hvol))
  4254. {
  4255. GetVolumeRootPath(((PPATH)hpath)->hvol, rgchVolumeRootPath);
  4256. bResult = (MyLStrCmpNI(pcszPath, rgchVolumeRootPath,
  4257. lstrlen(rgchVolumeRootPath))
  4258. == CR_EQUAL);
  4259. }
  4260. else
  4261. {
  4262. TRACE_OUT((TEXT("MyIsPathOnVolume(): Failing on unavailable volume %s."),
  4263. DebugGetVolumeRootPath(((PPATH)hpath)->hvol, rgchVolumeRootPath)));
  4264. bResult = FALSE;
  4265. }
  4266. return(bResult);
  4267. }
  4268. /*
  4269. ** ComparePaths()
  4270. **
  4271. **
  4272. **
  4273. ** Arguments:
  4274. **
  4275. ** Returns:
  4276. **
  4277. ** Side Effects: none
  4278. **
  4279. ** PATHs are compared by:
  4280. ** 1) volume
  4281. ** 2) path suffix
  4282. */
  4283. COMPARISONRESULT ComparePaths(HPATH hpath1, HPATH hpath2)
  4284. {
  4285. COMPARISONRESULT cr;
  4286. ASSERT(IS_VALID_HANDLE(hpath1, PATH));
  4287. ASSERT(IS_VALID_HANDLE(hpath2, PATH));
  4288. /* This comparison works across path lists. */
  4289. cr = ComparePathVolumes(hpath1, hpath2);
  4290. if (cr == CR_EQUAL)
  4291. cr = ComparePathStringsByHandle(((PCPATH)hpath1)->hsPathSuffix,
  4292. ((PCPATH)hpath2)->hsPathSuffix);
  4293. return(cr);
  4294. }
  4295. COMPARISONRESULT ComparePathVolumes(HPATH hpath1, HPATH hpath2)
  4296. {
  4297. ASSERT(IS_VALID_HANDLE(hpath1, PATH));
  4298. ASSERT(IS_VALID_HANDLE(hpath2, PATH));
  4299. return(CompareVolumes(((PCPATH)hpath1)->hvol, ((PCPATH)hpath2)->hvol));
  4300. }
  4301. /*
  4302. ** IsPathPrefix()
  4303. **
  4304. ** Determines whether or not one path is a prefix of another.
  4305. **
  4306. ** Arguments: hpathChild - whole path (longer or same length)
  4307. ** hpathParent - prefix path to test (shorter or same length)
  4308. **
  4309. ** Returns: TRUE if the second path is a prefix of the first path. FALSE
  4310. ** if not.
  4311. **
  4312. ** Side Effects: none
  4313. **
  4314. ** Read 'IsPathPrefix(A, B)' as 'Is A in B's subtree?'.
  4315. */
  4316. BOOL IsPathPrefix(HPATH hpathChild, HPATH hpathParent)
  4317. {
  4318. BOOL bResult;
  4319. ASSERT(IS_VALID_HANDLE(hpathParent, PATH));
  4320. ASSERT(IS_VALID_HANDLE(hpathChild, PATH));
  4321. if (ComparePathVolumes(hpathParent, hpathChild) == CR_EQUAL)
  4322. {
  4323. TCHAR rgchParentSuffix[MAX_PATH_LEN];
  4324. TCHAR rgchChildSuffix[MAX_PATH_LEN];
  4325. int nParentSuffixLen;
  4326. int nChildSuffixLen;
  4327. /* Ignore path roots when comparing path strings. */
  4328. GetPathSuffixString(hpathParent, rgchParentSuffix);
  4329. GetPathSuffixString(hpathChild, rgchChildSuffix);
  4330. /* Only root paths should have no path suffix off the root. */
  4331. nParentSuffixLen = lstrlen(rgchParentSuffix);
  4332. nChildSuffixLen = lstrlen(rgchChildSuffix);
  4333. /*
  4334. * The parent path is a path prefix of the child path iff:
  4335. * 1) The parent's path suffix string is shorter than or the same
  4336. * length as the child's path suffix string.
  4337. * 2) The two path suffix strings match through the length of the
  4338. * parent's path suffix string.
  4339. * 3) The prefix of the child's path suffix string is followed
  4340. * immediately by a null terminator or a path separator.
  4341. */
  4342. bResult = (nChildSuffixLen >= nParentSuffixLen &&
  4343. MyLStrCmpNI(rgchParentSuffix, rgchChildSuffix,
  4344. nParentSuffixLen) == CR_EQUAL &&
  4345. (nChildSuffixLen == nParentSuffixLen || /* same paths */
  4346. ! nParentSuffixLen || /* root parent */
  4347. IS_SLASH(rgchChildSuffix[nParentSuffixLen]))); /* non-root parent */
  4348. }
  4349. else
  4350. bResult = FALSE;
  4351. return(bResult);
  4352. }
  4353. /*
  4354. ** SubtreesIntersect()
  4355. **
  4356. **
  4357. **
  4358. ** Arguments:
  4359. **
  4360. ** Returns:
  4361. **
  4362. ** Side Effects: none
  4363. **
  4364. ** N.b., two subtrees cannot both intersect a third subtree unless they
  4365. ** intersect each other.
  4366. */
  4367. BOOL SubtreesIntersect(HPATH hpath1, HPATH hpath2)
  4368. {
  4369. ASSERT(IS_VALID_HANDLE(hpath1, PATH));
  4370. ASSERT(IS_VALID_HANDLE(hpath2, PATH));
  4371. return(IsPathPrefix(hpath1, hpath2) ||
  4372. IsPathPrefix(hpath2, hpath1));
  4373. }
  4374. /*
  4375. ** FindEndOfRootSpec()
  4376. **
  4377. ** Finds the end of the root specification in a path string.
  4378. **
  4379. ** Arguments: pcszPath - path to examine for root specification
  4380. ** hpath - handle to PATH that path string was generated from
  4381. **
  4382. ** Returns: pointer to first character after end of root specification
  4383. **
  4384. ** Side Effects: none
  4385. **
  4386. */
  4387. LPTSTR FindEndOfRootSpec(LPCTSTR pcszFullPath, HPATH hpath)
  4388. {
  4389. LPCTSTR pcsz;
  4390. UINT ucchPathLen;
  4391. UINT ucchSuffixLen;
  4392. ASSERT(IsCanonicalPath(pcszFullPath));
  4393. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  4394. ucchPathLen = lstrlen(pcszFullPath);
  4395. ucchSuffixLen = lstrlen(GetBfcString(((PCPATH)hpath)->hsPathSuffix));
  4396. pcsz = pcszFullPath + ucchPathLen;
  4397. if (ucchPathLen > ucchSuffixLen)
  4398. pcsz -= ucchSuffixLen;
  4399. else
  4400. /* Assume path is root path. */
  4401. ERROR_OUT((TEXT("FindEndOfRootSpec(): Path suffix %s is longer than full path %s."),
  4402. GetBfcString(((PCPATH)hpath)->hsPathSuffix),
  4403. pcszFullPath));
  4404. ASSERT(IsValidPathSuffix(pcsz));
  4405. return((LPTSTR)pcsz);
  4406. }
  4407. LPTSTR FindChildPathSuffix(HPATH hpathParent, HPATH hpathChild,
  4408. LPTSTR pszChildSuffixBuf)
  4409. {
  4410. LPCTSTR pcszChildSuffix;
  4411. TCHAR rgchParentSuffix[MAX_PATH_LEN];
  4412. ASSERT(IS_VALID_HANDLE(hpathParent, PATH));
  4413. ASSERT(IS_VALID_HANDLE(hpathChild, PATH));
  4414. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszChildSuffixBuf, STR, MAX_PATH_LEN));
  4415. ASSERT(IsPathPrefix(hpathChild, hpathParent));
  4416. GetPathSuffixString(hpathParent, rgchParentSuffix);
  4417. GetPathSuffixString(hpathChild, pszChildSuffixBuf);
  4418. ASSERT(lstrlen(rgchParentSuffix) <= lstrlen(pszChildSuffixBuf));
  4419. pcszChildSuffix = pszChildSuffixBuf + lstrlen(rgchParentSuffix);
  4420. if (IS_SLASH(*pcszChildSuffix))
  4421. pcszChildSuffix++;
  4422. ASSERT(IsValidPathSuffix(pcszChildSuffix));
  4423. return((LPTSTR)pcszChildSuffix);
  4424. }
  4425. COMPARISONRESULT ComparePointers(PCVOID pcv1, PCVOID pcv2)
  4426. {
  4427. COMPARISONRESULT cr;
  4428. /* pcv1 and pcv2 may be any value. */
  4429. if (pcv1 < pcv2)
  4430. cr = CR_FIRST_SMALLER;
  4431. else if (pcv1 > pcv2)
  4432. cr = CR_FIRST_LARGER;
  4433. else
  4434. cr = CR_EQUAL;
  4435. return(cr);
  4436. }
  4437. TWINRESULT TWINRESULTFromLastError(TWINRESULT tr)
  4438. {
  4439. switch (GetLastError())
  4440. {
  4441. case ERROR_OUTOFMEMORY:
  4442. tr = TR_OUT_OF_MEMORY;
  4443. break;
  4444. default:
  4445. break;
  4446. }
  4447. return(tr);
  4448. }
  4449. TWINRESULT WritePathList(HCACHEDFILE hcf, HPATHLIST hpl)
  4450. {
  4451. TWINRESULT tr;
  4452. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  4453. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4454. tr = WriteVolumeList(hcf, ((PCPATHLIST)hpl)->hvl);
  4455. if (tr == TR_SUCCESS)
  4456. {
  4457. tr = WriteStringTable(hcf, ((PCPATHLIST)hpl)->hst);
  4458. if (tr == TR_SUCCESS)
  4459. {
  4460. DWORD dwcbDBPathListHeaderOffset;
  4461. tr = TR_BRIEFCASE_WRITE_FAILED;
  4462. /* Save initial file position. */
  4463. dwcbDBPathListHeaderOffset = GetCachedFilePointerPosition(hcf);
  4464. if (dwcbDBPathListHeaderOffset != INVALID_SEEK_POSITION)
  4465. {
  4466. DBPATHLISTHEADER dbplh;
  4467. /* Leave space for path list header. */
  4468. ZeroMemory(&dbplh, sizeof(dbplh));
  4469. if (WriteToCachedFile(hcf, (PCVOID)&dbplh, sizeof(dbplh), NULL))
  4470. {
  4471. ARRAYINDEX aicPtrs;
  4472. ARRAYINDEX ai;
  4473. LONG lcPaths = 0;
  4474. tr = TR_SUCCESS;
  4475. aicPtrs = GetPtrCount(((PCPATHLIST)hpl)->hpa);
  4476. /* Write all paths. */
  4477. for (ai = 0; ai < aicPtrs; ai++)
  4478. {
  4479. PPATH ppath;
  4480. ppath = GetPtr(((PCPATHLIST)hpl)->hpa, ai);
  4481. /*
  4482. * As a sanity check, don't save any path with a lock count
  4483. * of 0. A 0 lock count implies that the path has not been
  4484. * referenced since it was restored from the database, or
  4485. * something is broken.
  4486. */
  4487. if (ppath->ulcLock > 0)
  4488. {
  4489. tr = WritePath(hcf, ppath);
  4490. if (tr == TR_SUCCESS)
  4491. {
  4492. ASSERT(lcPaths < LONG_MAX);
  4493. lcPaths++;
  4494. }
  4495. else
  4496. break;
  4497. }
  4498. else
  4499. ERROR_OUT((TEXT("WritePathList(): PATH for path %s has 0 lock count and will not be written."),
  4500. DebugGetPathString((HPATH)ppath)));
  4501. }
  4502. /* Save path list header. */
  4503. if (tr == TR_SUCCESS)
  4504. {
  4505. dbplh.lcPaths = lcPaths;
  4506. tr = WriteDBSegmentHeader(hcf, dwcbDBPathListHeaderOffset, &dbplh,
  4507. sizeof(dbplh));
  4508. TRACE_OUT((TEXT("WritePathList(): Wrote %ld paths."),
  4509. dbplh.lcPaths));
  4510. }
  4511. }
  4512. }
  4513. }
  4514. }
  4515. return(tr);
  4516. }
  4517. TWINRESULT ReadPathList(HCACHEDFILE hcf, HPATHLIST hpl,
  4518. PHHANDLETRANS phht)
  4519. {
  4520. TWINRESULT tr;
  4521. HHANDLETRANS hhtVolumes;
  4522. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  4523. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4524. ASSERT(IS_VALID_WRITE_PTR(phht, HHANDLETRANS));
  4525. tr = ReadVolumeList(hcf, ((PCPATHLIST)hpl)->hvl, &hhtVolumes);
  4526. if (tr == TR_SUCCESS)
  4527. {
  4528. HHANDLETRANS hhtStrings;
  4529. tr = ReadStringTable(hcf, ((PCPATHLIST)hpl)->hst, &hhtStrings);
  4530. if (tr == TR_SUCCESS)
  4531. {
  4532. DBPATHLISTHEADER dbplh;
  4533. DWORD dwcbRead;
  4534. tr = TR_CORRUPT_BRIEFCASE;
  4535. if (ReadFromCachedFile(hcf, &dbplh, sizeof(dbplh), &dwcbRead) &&
  4536. dwcbRead == sizeof(dbplh))
  4537. {
  4538. HHANDLETRANS hht;
  4539. if (CreateHandleTranslator(dbplh.lcPaths, &hht))
  4540. {
  4541. LONG l;
  4542. tr = TR_SUCCESS;
  4543. TRACE_OUT((TEXT("ReadPathList(): Reading %ld paths."),
  4544. dbplh.lcPaths));
  4545. for (l = 0; l < dbplh.lcPaths; l++)
  4546. {
  4547. tr = ReadPath(hcf, (PPATHLIST)hpl, hhtVolumes, hhtStrings,
  4548. hht);
  4549. if (tr != TR_SUCCESS)
  4550. break;
  4551. }
  4552. if (tr == TR_SUCCESS)
  4553. {
  4554. PrepareForHandleTranslation(hht);
  4555. *phht = hht;
  4556. ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
  4557. ASSERT(IS_VALID_HANDLE(*phht, HANDLETRANS));
  4558. }
  4559. else
  4560. DestroyHandleTranslator(hht);
  4561. }
  4562. else
  4563. tr = TR_OUT_OF_MEMORY;
  4564. }
  4565. DestroyHandleTranslator(hhtStrings);
  4566. }
  4567. DestroyHandleTranslator(hhtVolumes);
  4568. }
  4569. return(tr);
  4570. }
  4571. /* Macros
  4572. *********/
  4573. /* extract array element */
  4574. #define ARRAY_ELEMENT(ppa, ai) (((ppa)->ppcvArray)[(ai)])
  4575. /* Add pointers to array in sorted order? */
  4576. #define ADD_PTRS_IN_SORTED_ORDER(ppa) IS_FLAG_SET((ppa)->dwFlags, PA_FL_SORTED_ADD)
  4577. /* Types
  4578. ********/
  4579. /* pointer array flags */
  4580. typedef enum _ptrarrayflags
  4581. {
  4582. /* Insert elements in sorted order. */
  4583. PA_FL_SORTED_ADD = 0x0001,
  4584. /* flag combinations */
  4585. ALL_PA_FLAGS = PA_FL_SORTED_ADD
  4586. }
  4587. PTRARRAYFLAGS;
  4588. /* pointer array structure */
  4589. /*
  4590. * Free elements in the ppcvArray[] array lie between indexes (aicPtrsUsed)
  4591. * and (aiLast), inclusive.
  4592. */
  4593. typedef struct _ptrarray
  4594. {
  4595. /* elements to grow array by after it fills up */
  4596. ARRAYINDEX aicPtrsToGrowBy;
  4597. /* array flags */
  4598. DWORD dwFlags;
  4599. /* pointer to base of array */
  4600. PCVOID *ppcvArray;
  4601. /* index of last element allocated in array */
  4602. ARRAYINDEX aicPtrsAllocated;
  4603. /*
  4604. * (We keep a count of the number of elements used instead of the index of
  4605. * the last element used so that this value is 0 for an empty array, and not
  4606. * some non-zero sentinel value.)
  4607. */
  4608. /* number of elements used in array */
  4609. ARRAYINDEX aicPtrsUsed;
  4610. }
  4611. PTRARRAY;
  4612. DECLARE_STANDARD_TYPES(PTRARRAY);
  4613. /* Module Prototypes
  4614. ********************/
  4615. BOOL AddAFreePtrToEnd(PPTRARRAY);
  4616. void PtrHeapSwap(PPTRARRAY, ARRAYINDEX, ARRAYINDEX);
  4617. void PtrHeapSift(PPTRARRAY, ARRAYINDEX, ARRAYINDEX, COMPARESORTEDPTRSPROC);
  4618. /*
  4619. ** AddAFreePtrToEnd()
  4620. **
  4621. ** Adds a free element to the end of an array.
  4622. **
  4623. ** Arguments: pa - pointer to array
  4624. **
  4625. ** Returns: TRUE if successful, or FALSE if not.
  4626. **
  4627. ** Side Effects: May grow the array.
  4628. */
  4629. BOOL AddAFreePtrToEnd(PPTRARRAY pa)
  4630. {
  4631. BOOL bResult;
  4632. ASSERT(IS_VALID_STRUCT_PTR(pa, CPTRARRAY));
  4633. /* Are there any free elements in the array? */
  4634. if (pa->aicPtrsUsed < pa->aicPtrsAllocated)
  4635. /* Yes. Return the next free pointer. */
  4636. bResult = TRUE;
  4637. else
  4638. {
  4639. ARRAYINDEX aicNewPtrs = pa->aicPtrsAllocated + pa->aicPtrsToGrowBy;
  4640. PCVOID *ppcvArray;
  4641. bResult = FALSE;
  4642. /* Try to grow the array. */
  4643. /* Blow off unlikely overflow conditions as ASSERT()s. */
  4644. ASSERT(pa->aicPtrsAllocated <= ARRAYINDEX_MAX + 1);
  4645. ASSERT(ARRAYINDEX_MAX + 1 - pa->aicPtrsToGrowBy >= pa->aicPtrsAllocated);
  4646. /* Try to grow the array. */
  4647. if (ReallocateMemory(
  4648. (PVOID)(pa->ppcvArray),
  4649. pa->aicPtrsAllocated * sizeof(*ppcvArray),
  4650. aicNewPtrs * sizeof(*ppcvArray),
  4651. (PVOID *)(&ppcvArray)
  4652. ))
  4653. {
  4654. /*
  4655. * Array reallocated successfully. Set up PTRARRAY fields, and return
  4656. * the first free index.
  4657. */
  4658. pa->ppcvArray = ppcvArray;
  4659. pa->aicPtrsAllocated = aicNewPtrs;
  4660. bResult = TRUE;
  4661. }
  4662. }
  4663. return(bResult);
  4664. }
  4665. /*
  4666. ** PtrHeapSwap()
  4667. **
  4668. ** Swaps two elements in an array.
  4669. **
  4670. ** Arguments: pa - pointer to array
  4671. ** aiFirst - index of first element
  4672. ** aiSecond - index of second element
  4673. **
  4674. ** Returns: void
  4675. **
  4676. ** Side Effects: none
  4677. */
  4678. void PtrHeapSwap(PPTRARRAY pa, ARRAYINDEX ai1, ARRAYINDEX ai2)
  4679. {
  4680. PCVOID pcvTemp;
  4681. ASSERT(IS_VALID_STRUCT_PTR(pa, CPTRARRAY));
  4682. ASSERT(ai1 >= 0);
  4683. ASSERT(ai1 < pa->aicPtrsUsed);
  4684. ASSERT(ai2 >= 0);
  4685. ASSERT(ai2 < pa->aicPtrsUsed);
  4686. pcvTemp = ARRAY_ELEMENT(pa, ai1);
  4687. ARRAY_ELEMENT(pa, ai1) = ARRAY_ELEMENT(pa, ai2);
  4688. ARRAY_ELEMENT(pa, ai2) = pcvTemp;
  4689. }
  4690. /*
  4691. ** PtrHeapSift()
  4692. **
  4693. ** Sifts an element down in an array until the partially ordered tree property
  4694. ** is retored.
  4695. **
  4696. ** Arguments: pa - pointer to array
  4697. ** aiFirst - index of element to sift down
  4698. ** aiLast - index of last element in subtree
  4699. ** cspp - element comparison callback function to be called to
  4700. ** compare elements
  4701. **
  4702. ** Returns: void
  4703. **
  4704. ** Side Effects: none
  4705. */
  4706. void PtrHeapSift(PPTRARRAY pa, ARRAYINDEX aiFirst, ARRAYINDEX aiLast,
  4707. COMPARESORTEDPTRSPROC cspp)
  4708. {
  4709. ARRAYINDEX ai;
  4710. PCVOID pcvTemp;
  4711. ASSERT(IS_VALID_STRUCT_PTR(pa, CPTRARRAY));
  4712. ASSERT(IS_VALID_CODE_PTR(cspp, COMPARESORTEDPTRSPROC));
  4713. ASSERT(aiFirst >= 0);
  4714. ASSERT(aiFirst < pa->aicPtrsUsed);
  4715. ASSERT(aiLast >= 0);
  4716. ASSERT(aiLast < pa->aicPtrsUsed);
  4717. ai = aiFirst * 2;
  4718. pcvTemp = ARRAY_ELEMENT(pa, aiFirst);
  4719. while (ai <= aiLast)
  4720. {
  4721. if (ai < aiLast &&
  4722. (*cspp)(ARRAY_ELEMENT(pa, ai), ARRAY_ELEMENT(pa, ai + 1)) == CR_FIRST_SMALLER)
  4723. ai++;
  4724. if ((*cspp)(pcvTemp, ARRAY_ELEMENT(pa, ai)) != CR_FIRST_SMALLER)
  4725. break;
  4726. ARRAY_ELEMENT(pa, aiFirst) = ARRAY_ELEMENT(pa, ai);
  4727. aiFirst = ai;
  4728. ai *= 2;
  4729. }
  4730. ARRAY_ELEMENT(pa, aiFirst) = pcvTemp;
  4731. }
  4732. /*
  4733. ** CreatePtrArray()
  4734. **
  4735. ** Creates a pointer array.
  4736. **
  4737. ** Arguments: pcna - pointer to NEWPTRARRAY describing the array to be
  4738. ** created
  4739. **
  4740. ** Returns: Handle to the new array if successful, or NULL if
  4741. ** unsuccessful.
  4742. **
  4743. ** Side Effects: none
  4744. */
  4745. BOOL CreatePtrArray(PCNEWPTRARRAY pcna, PHPTRARRAY phpa)
  4746. {
  4747. PCVOID *ppcvArray;
  4748. ASSERT(IS_VALID_STRUCT_PTR(pcna, CNEWPTRARRAY));
  4749. ASSERT(IS_VALID_WRITE_PTR(phpa, HPTRARRAY));
  4750. /* Try to allocate the initial array. */
  4751. *phpa = NULL;
  4752. if (AllocateMemory(pcna->aicInitialPtrs * sizeof(*ppcvArray), (PVOID *)(&ppcvArray)))
  4753. {
  4754. PPTRARRAY pa;
  4755. /* Try to allocate PTRARRAY structure. */
  4756. if (AllocateMemory(sizeof(*pa), &pa))
  4757. {
  4758. /* Initialize PTRARRAY fields. */
  4759. pa->aicPtrsToGrowBy = pcna->aicAllocGranularity;
  4760. pa->ppcvArray = ppcvArray;
  4761. pa->aicPtrsAllocated = pcna->aicInitialPtrs;
  4762. pa->aicPtrsUsed = 0;
  4763. /* Set flags. */
  4764. if (IS_FLAG_SET(pcna->dwFlags, NPA_FL_SORTED_ADD))
  4765. pa->dwFlags = PA_FL_SORTED_ADD;
  4766. else
  4767. pa->dwFlags = 0;
  4768. *phpa = (HPTRARRAY)pa;
  4769. ASSERT(IS_VALID_HANDLE(*phpa, PTRARRAY));
  4770. }
  4771. else
  4772. /* Unlock and free array (ignoring return values). */
  4773. FreeMemory((PVOID)(ppcvArray));
  4774. }
  4775. return(*phpa != NULL);
  4776. }
  4777. void DestroyPtrArray(HPTRARRAY hpa)
  4778. {
  4779. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4780. /* Free the array. */
  4781. ASSERT(((PCPTRARRAY)hpa)->ppcvArray);
  4782. FreeMemory((PVOID)(((PCPTRARRAY)hpa)->ppcvArray));
  4783. /* Free PTRARRAY structure. */
  4784. FreeMemory((PPTRARRAY)hpa);
  4785. }
  4786. /*
  4787. ** InsertPtr()
  4788. **
  4789. ** Adds an element to an array at a given index.
  4790. **
  4791. ** Arguments: hpa - handle to array that element is to be added to
  4792. ** aiInsert - index where new element is to be inserted
  4793. ** pcvNew - pointer to element to add to array
  4794. **
  4795. ** Returns: TRUE if the element was inserted successfully, or FALSE if
  4796. ** not.
  4797. **
  4798. ** Side Effects: The array may be grown.
  4799. **
  4800. ** N.b., for an array marked PA_FL_SORTED_ADD, this index should only be
  4801. ** retrieved using SearchSortedArray(), or the sorted order will be destroyed.
  4802. */
  4803. BOOL InsertPtr(HPTRARRAY hpa, COMPARESORTEDPTRSPROC cspp, ARRAYINDEX aiInsert, PCVOID pcvNew)
  4804. {
  4805. BOOL bResult;
  4806. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4807. ASSERT(aiInsert >= 0);
  4808. ASSERT(aiInsert <= ((PCPTRARRAY)hpa)->aicPtrsUsed);
  4809. #ifdef DEBUG
  4810. /* Make sure the correct index was given for insertion. */
  4811. if (ADD_PTRS_IN_SORTED_ORDER((PCPTRARRAY)hpa))
  4812. {
  4813. ARRAYINDEX aiNew;
  4814. EVAL(! SearchSortedArray(hpa, cspp, pcvNew, &aiNew));
  4815. ASSERT(aiInsert == aiNew);
  4816. }
  4817. #endif
  4818. /* Get a free element in the array. */
  4819. bResult = AddAFreePtrToEnd((PPTRARRAY)hpa);
  4820. if (bResult)
  4821. {
  4822. ASSERT(((PCPTRARRAY)hpa)->aicPtrsUsed < ARRAYINDEX_MAX);
  4823. /* Open a slot for the new element. */
  4824. MoveMemory((PVOID)& ARRAY_ELEMENT((PPTRARRAY)hpa, aiInsert + 1),
  4825. & ARRAY_ELEMENT((PPTRARRAY)hpa, aiInsert),
  4826. (((PCPTRARRAY)hpa)->aicPtrsUsed - aiInsert) * sizeof(ARRAY_ELEMENT((PCPTRARRAY)hpa, 0)));
  4827. /* Put the new element in the open slot. */
  4828. ARRAY_ELEMENT((PPTRARRAY)hpa, aiInsert) = pcvNew;
  4829. ((PPTRARRAY)hpa)->aicPtrsUsed++;
  4830. }
  4831. return(bResult);
  4832. }
  4833. /*
  4834. ** AddPtr()
  4835. **
  4836. ** Adds an element to an array, in sorted order if so specified at
  4837. ** CreatePtrArray() time.
  4838. **
  4839. ** Arguments: hpa - handle to array that element is to be added to
  4840. ** pcvNew - pointer to element to be added to array
  4841. ** pai - pointer to ARRAYINDEX to be filled in with index of
  4842. ** new element, may be NULL
  4843. **
  4844. ** Returns: TWINRESULT
  4845. **
  4846. ** Side Effects: The array may be grown.
  4847. */
  4848. BOOL AddPtr(HPTRARRAY hpa, COMPARESORTEDPTRSPROC cspp, PCVOID pcvNew, PARRAYINDEX pai)
  4849. {
  4850. BOOL bResult;
  4851. ARRAYINDEX aiNew;
  4852. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4853. ASSERT(! pai || IS_VALID_WRITE_PTR(pai, ARRAYINDEX));
  4854. /* Find out where the new element should go. */
  4855. if (ADD_PTRS_IN_SORTED_ORDER((PCPTRARRAY)hpa))
  4856. EVAL(! SearchSortedArray(hpa, cspp, pcvNew, &aiNew));
  4857. else
  4858. aiNew = ((PCPTRARRAY)hpa)->aicPtrsUsed;
  4859. bResult = InsertPtr(hpa, cspp, aiNew, pcvNew);
  4860. if (bResult && pai)
  4861. *pai = aiNew;
  4862. return(bResult);
  4863. }
  4864. /*
  4865. ** DeletePtr()
  4866. **
  4867. ** Removes an element from an element array.
  4868. **
  4869. ** Arguments: ha - handle to array
  4870. ** aiDelete - index of element to be deleted
  4871. **
  4872. ** Returns: TWINRESULT
  4873. **
  4874. ** Side Effects: none
  4875. */
  4876. void DeletePtr(HPTRARRAY hpa, ARRAYINDEX aiDelete)
  4877. {
  4878. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4879. ASSERT(aiDelete >= 0);
  4880. ASSERT(aiDelete < ((PCPTRARRAY)hpa)->aicPtrsUsed);
  4881. /*
  4882. * Compact the element array by moving down all elements past the one being
  4883. * deleted.
  4884. */
  4885. MoveMemory((PVOID)& ARRAY_ELEMENT((PPTRARRAY)hpa, aiDelete),
  4886. & ARRAY_ELEMENT((PPTRARRAY)hpa, aiDelete + 1),
  4887. (((PCPTRARRAY)hpa)->aicPtrsUsed - aiDelete - 1) * sizeof(ARRAY_ELEMENT((PCPTRARRAY)hpa, 0)));
  4888. /* One less element used. */
  4889. ((PPTRARRAY)hpa)->aicPtrsUsed--;
  4890. }
  4891. void DeleteAllPtrs(HPTRARRAY hpa)
  4892. {
  4893. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4894. ((PPTRARRAY)hpa)->aicPtrsUsed = 0;
  4895. }
  4896. /*
  4897. ** GetPtrCount()
  4898. **
  4899. ** Retrieves the number of elements in an element array.
  4900. **
  4901. ** Arguments: hpa - handle to array
  4902. **
  4903. ** Returns: TWINRESULT
  4904. **
  4905. ** Side Effects: none
  4906. */
  4907. ARRAYINDEX GetPtrCount(HPTRARRAY hpa)
  4908. {
  4909. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4910. return(((PCPTRARRAY)hpa)->aicPtrsUsed);
  4911. }
  4912. /*
  4913. ** GetPtr()
  4914. **
  4915. ** Retrieves an element from an array.
  4916. **
  4917. ** Arguments: hpa - handle to array
  4918. ** ai - index of element to be retrieved
  4919. **
  4920. ** Returns: TWINRESULT
  4921. **
  4922. ** Side Effects: none
  4923. */
  4924. PVOID GetPtr(HPTRARRAY hpa, ARRAYINDEX ai)
  4925. {
  4926. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4927. ASSERT(ai >= 0);
  4928. ASSERT(ai < ((PCPTRARRAY)hpa)->aicPtrsUsed);
  4929. return((PVOID)ARRAY_ELEMENT((PCPTRARRAY)hpa, ai));
  4930. }
  4931. /*
  4932. ** SortPtrArray()
  4933. **
  4934. ** Sorts an array.
  4935. **
  4936. ** Arguments: hpa - handle to element list to be sorted
  4937. ** cspp - pointer comparison callback function
  4938. **
  4939. ** Returns: void
  4940. **
  4941. ** Side Effects: none
  4942. **
  4943. ** Uses heap sort.
  4944. */
  4945. void SortPtrArray(HPTRARRAY hpa, COMPARESORTEDPTRSPROC cspp)
  4946. {
  4947. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  4948. /* Are there any elements to sort (2 or more)? */
  4949. if (((PCPTRARRAY)hpa)->aicPtrsUsed > 1)
  4950. {
  4951. ARRAYINDEX ai;
  4952. ARRAYINDEX aiLastUsed = ((PCPTRARRAY)hpa)->aicPtrsUsed - 1;
  4953. /* Yes. Create partially ordered tree. */
  4954. for (ai = aiLastUsed / 2; ai >= 0; ai--)
  4955. PtrHeapSift((PPTRARRAY)hpa, ai, aiLastUsed, cspp);
  4956. for (ai = aiLastUsed; ai >= 1; ai--)
  4957. {
  4958. /* Remove minimum from front of heap. */
  4959. PtrHeapSwap((PPTRARRAY)hpa, 0, ai);
  4960. /* Reestablish partially ordered tree. */
  4961. PtrHeapSift((PPTRARRAY)hpa, 0, ai - 1, cspp);
  4962. }
  4963. }
  4964. ASSERT(IsPtrArrayInSortedOrder((PCPTRARRAY)hpa, cspp));
  4965. }
  4966. /*
  4967. ** SearchSortedArray()
  4968. **
  4969. ** Searches an array for a target element using binary search. If several
  4970. ** adjacent elements match the target element, the index of the first matching
  4971. ** element is returned.
  4972. **
  4973. ** Arguments: hpa - handle to array to be searched
  4974. ** cspp - element comparison callback function to be called to
  4975. ** compare the target element with an element from the
  4976. ** array, the callback function is called as:
  4977. **
  4978. ** (*cspp)(pcvTarget, pcvPtrFromList)
  4979. **
  4980. ** pcvTarget - pointer to target element to search for
  4981. ** pbFound - pointer to BOOL to be filled in with TRUE if the
  4982. ** target element is found, or FALSE if not
  4983. ** paiTarget - pointer to ARRAYINDEX to be filled in with the
  4984. ** index of the first element matching the target
  4985. ** element if found, otherwise filled in with the
  4986. ** index where the target element should be
  4987. ** inserted
  4988. **
  4989. ** Returns: TRUE if target element is found. FALSE if not.
  4990. **
  4991. ** Side Effects: none
  4992. **
  4993. ** We use a private version of SearchSortedArray() instead of the CRT bsearch()
  4994. ** function since we want it to return the insertion index of the target
  4995. ** element if the target element is not found.
  4996. */
  4997. BOOL SearchSortedArray(HPTRARRAY hpa, COMPARESORTEDPTRSPROC cspp,
  4998. PCVOID pcvTarget, PARRAYINDEX paiTarget)
  4999. {
  5000. BOOL bFound;
  5001. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  5002. ASSERT(IS_VALID_CODE_PTR(cspp, COMPARESORTEDPTRSPROC));
  5003. ASSERT(IS_VALID_WRITE_PTR(paiTarget, ARRAYINDEX));
  5004. ASSERT(ADD_PTRS_IN_SORTED_ORDER((PCPTRARRAY)hpa));
  5005. #if 0
  5006. ASSERT(IsPtrArrayInSortedOrder((PCPTRARRAY)hpa, ((PCPTRARRAY)hpa)->cspp));
  5007. #endif
  5008. bFound = FALSE;
  5009. /* Are there any elements to search through? */
  5010. if (((PCPTRARRAY)hpa)->aicPtrsUsed > 0)
  5011. {
  5012. ARRAYINDEX aiLow = 0;
  5013. ARRAYINDEX aiMiddle = 0;
  5014. ARRAYINDEX aiHigh = ((PCPTRARRAY)hpa)->aicPtrsUsed - 1;
  5015. COMPARISONRESULT cr = CR_EQUAL;
  5016. /* Yes. Search for the target element. */
  5017. /*
  5018. * At the end of the penultimate iteration of this loop:
  5019. *
  5020. * aiLow == aiMiddle == aiHigh.
  5021. */
  5022. ASSERT(aiHigh <= ARRAYINDEX_MAX);
  5023. while (aiLow <= aiHigh)
  5024. {
  5025. aiMiddle = (aiLow + aiHigh) / 2;
  5026. cr = (*cspp)(pcvTarget, ARRAY_ELEMENT((PCPTRARRAY)hpa, aiMiddle));
  5027. if (cr == CR_FIRST_SMALLER)
  5028. aiHigh = aiMiddle - 1;
  5029. else if (cr == CR_FIRST_LARGER)
  5030. aiLow = aiMiddle + 1;
  5031. else
  5032. {
  5033. /*
  5034. * Found a match at index aiMiddle. Search back for first match.
  5035. */
  5036. bFound = TRUE;
  5037. while (aiMiddle > 0)
  5038. {
  5039. if ((*cspp)(pcvTarget, ARRAY_ELEMENT((PCPTRARRAY)hpa, aiMiddle - 1)) != CR_EQUAL)
  5040. break;
  5041. else
  5042. aiMiddle--;
  5043. }
  5044. break;
  5045. }
  5046. }
  5047. /*
  5048. * Return the index of the target if found, or the index where the target
  5049. * should be inserted if not found.
  5050. */
  5051. /*
  5052. * If (cr == CR_FIRST_LARGER), the insertion index is aiLow.
  5053. *
  5054. * If (cr == CR_FIRST_SMALLER), the insertion index is aiMiddle.
  5055. *
  5056. * If (cr == CR_EQUAL), the insertion index is aiMiddle.
  5057. */
  5058. if (cr == CR_FIRST_LARGER)
  5059. *paiTarget = aiLow;
  5060. else
  5061. *paiTarget = aiMiddle;
  5062. }
  5063. else
  5064. /*
  5065. * No. The target element cannot be found in an empty array. It should
  5066. * be inserted as the first element.
  5067. */
  5068. *paiTarget = 0;
  5069. ASSERT(*paiTarget <= ((PCPTRARRAY)hpa)->aicPtrsUsed);
  5070. return(bFound);
  5071. }
  5072. /*
  5073. ** LinearSearchArray()
  5074. **
  5075. ** Searches an array for a target element using binary search. If several
  5076. ** adjacent elements match the target element, the index of the first matching
  5077. ** element is returned.
  5078. **
  5079. ** Arguments: hpa - handle to array to be searched
  5080. ** cupp - element comparison callback function to be called to
  5081. ** compare the target element with an element from the
  5082. ** array, the callback function is called as:
  5083. **
  5084. ** (*cupp)(pvTarget, pvPtrFromList)
  5085. **
  5086. ** the callback function should return a value based upon
  5087. ** the result of the element comparison as follows:
  5088. **
  5089. ** FALSE, pvTarget == pvPtrFromList
  5090. ** TRUE, pvTarget != pvPtrFromList
  5091. **
  5092. ** pvTarget - far element to target element to search for
  5093. ** paiTarget - far element to ARRAYINDEX to be filled in with
  5094. ** the index of the first matching element if
  5095. ** found, otherwise filled in with index where
  5096. ** element should be inserted
  5097. **
  5098. ** Returns: TRUE if target element is found. FALSE if not.
  5099. **
  5100. ** Side Effects: none
  5101. **
  5102. ** We use a private version of LinearSearchForPtr() instead of the CRT _lfind()
  5103. ** function since we want it to return the insertion index of the target
  5104. ** element if the target element is not found.
  5105. **
  5106. ** If the target element is not found the insertion index returned is the first
  5107. ** element after the last used element in the array.
  5108. */
  5109. BOOL LinearSearchArray(HPTRARRAY hpa, COMPAREUNSORTEDPTRSPROC cupp,
  5110. PCVOID pcvTarget, PARRAYINDEX paiTarget)
  5111. {
  5112. BOOL bFound;
  5113. ARRAYINDEX ai;
  5114. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY) &&
  5115. (! cupp || IS_VALID_CODE_PTR(cupp, COMPPTRSPROC)) &&
  5116. IS_VALID_WRITE_PTR(paiTarget, ARRAYINDEX));
  5117. bFound = FALSE;
  5118. for (ai = 0; ai < ((PCPTRARRAY)hpa)->aicPtrsUsed; ai++)
  5119. {
  5120. if (! (*cupp)(pcvTarget, ARRAY_ELEMENT((PCPTRARRAY)hpa, ai)))
  5121. {
  5122. bFound = TRUE;
  5123. break;
  5124. }
  5125. }
  5126. if (bFound)
  5127. *paiTarget = ai;
  5128. return(bFound);
  5129. }
  5130. /* Macros
  5131. *********/
  5132. #define ARRAY_ELEMENT_SIZE(hpa, ai, es) (((PBYTE)hpa)[(ai) * (es)])
  5133. /* Module Prototypes
  5134. ********************/
  5135. void HeapSwap(PVOID, LONG, LONG, size_t, PVOID);
  5136. void HeapSift(PVOID, LONG, LONG, size_t, COMPARESORTEDELEMSPROC, PVOID);
  5137. /*
  5138. ** HeapSwap()
  5139. **
  5140. ** Swaps two elements of an array.
  5141. **
  5142. ** Arguments: pvArray - pointer to array
  5143. ** li1 - index of first element
  5144. ** li2 - index of second element
  5145. ** stElemSize - length of element in bytes
  5146. ** pvTemp - pointer to temporary buffer of at least stElemSize
  5147. ** bytes used for swapping
  5148. **
  5149. ** Returns: void
  5150. **
  5151. ** Side Effects: none
  5152. */
  5153. void HeapSwap(PVOID pvArray, LONG li1, LONG li2,
  5154. size_t stElemSize, PVOID pvTemp)
  5155. {
  5156. ASSERT(li1 >= 0);
  5157. ASSERT(li2 >= 0);
  5158. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pvArray, VOID, (max(li1, li2) + 1) * stElemSize));
  5159. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pvTemp, VOID, stElemSize));
  5160. CopyMemory(pvTemp, & ARRAY_ELEMENT_SIZE(pvArray, li1, stElemSize), stElemSize);
  5161. CopyMemory(& ARRAY_ELEMENT_SIZE(pvArray, li1, stElemSize), & ARRAY_ELEMENT_SIZE(pvArray, li2, stElemSize), stElemSize);
  5162. CopyMemory(& ARRAY_ELEMENT_SIZE(pvArray, li2, stElemSize), pvTemp, stElemSize);
  5163. }
  5164. /*
  5165. ** HeapSift()
  5166. **
  5167. ** Sifts an element down in an array until the partially ordered tree property
  5168. ** is restored.
  5169. **
  5170. ** Arguments: hppTable - pointer to array
  5171. ** liFirst - index of first element to sift down
  5172. ** liLast - index of last element in subtree
  5173. ** cep - pointer comparison callback function to be called to
  5174. ** compare elements
  5175. **
  5176. ** Returns: void
  5177. **
  5178. ** Side Effects: none
  5179. */
  5180. void HeapSift(PVOID pvArray, LONG liFirst, LONG liLast,
  5181. size_t stElemSize, COMPARESORTEDELEMSPROC cep, PVOID pvTemp)
  5182. {
  5183. LONG li;
  5184. ASSERT(liFirst >= 0);
  5185. ASSERT(liLast >= 0);
  5186. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pvArray, VOID, (max(liFirst, liLast) + 1) * stElemSize));
  5187. ASSERT(IS_VALID_CODE_PTR(cep, COMPARESORTEDELEMSPROC));
  5188. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pvTemp, VOID, stElemSize));
  5189. li = liFirst * 2;
  5190. CopyMemory(pvTemp, & ARRAY_ELEMENT_SIZE(pvArray, liFirst, stElemSize), stElemSize);
  5191. while (li <= liLast)
  5192. {
  5193. if (li < liLast &&
  5194. (*cep)(& ARRAY_ELEMENT_SIZE(pvArray, li, stElemSize), & ARRAY_ELEMENT_SIZE(pvArray, li + 1, stElemSize)) == CR_FIRST_SMALLER)
  5195. li++;
  5196. if ((*cep)(pvTemp, & ARRAY_ELEMENT_SIZE(pvArray, li, stElemSize)) != CR_FIRST_SMALLER)
  5197. break;
  5198. CopyMemory(& ARRAY_ELEMENT_SIZE(pvArray, liFirst, stElemSize), & ARRAY_ELEMENT_SIZE(pvArray, li, stElemSize), stElemSize);
  5199. liFirst = li;
  5200. li *= 2;
  5201. }
  5202. CopyMemory(& ARRAY_ELEMENT_SIZE(pvArray, liFirst, stElemSize), pvTemp, stElemSize);
  5203. }
  5204. #ifdef DEBUG
  5205. /*
  5206. ** InSortedOrder()
  5207. **
  5208. **
  5209. **
  5210. ** Arguments:
  5211. **
  5212. ** Returns:
  5213. **
  5214. ** Side Effects: none
  5215. */
  5216. BOOL InSortedOrder(PVOID pvArray, LONG lcElements,
  5217. size_t stElemSize, COMPARESORTEDELEMSPROC cep)
  5218. {
  5219. BOOL bResult = TRUE;
  5220. ASSERT(lcElements >= 0);
  5221. ASSERT(IS_VALID_READ_BUFFER_PTR(pvArray, VOID, lcElements * stElemSize));
  5222. ASSERT(IS_VALID_CODE_PTR(cep, COMPARESORTEDELEMSPROC));
  5223. if (lcElements > 1)
  5224. {
  5225. LONG li;
  5226. for (li = 0; li < lcElements - 1; li++)
  5227. {
  5228. if ((*cep)(& ARRAY_ELEMENT_SIZE(pvArray, li, stElemSize),
  5229. & ARRAY_ELEMENT_SIZE(pvArray, li + 1, stElemSize))
  5230. == CR_FIRST_LARGER)
  5231. {
  5232. bResult = FALSE;
  5233. ERROR_OUT((TEXT("InSortedOrder(): Element [%ld] %#lx > following element [%ld] %#lx."),
  5234. li,
  5235. & ARRAY_ELEMENT_SIZE(pvArray, li, stElemSize),
  5236. li + 1,
  5237. & ARRAY_ELEMENT_SIZE(pvArray, li + 1, stElemSize)));
  5238. break;
  5239. }
  5240. }
  5241. }
  5242. return(bResult);
  5243. }
  5244. #endif
  5245. /*
  5246. ** HeapSort()
  5247. **
  5248. ** Sorts an array. Thanks to Rob's Dad for the cool heap sort algorithm.
  5249. **
  5250. ** Arguments: pvArray - pointer to base of array
  5251. ** lcElements - number of elements in array
  5252. ** stElemSize - length of element in bytes
  5253. ** cep - element comparison callback function
  5254. ** pvTemp - pointer to temporary buffer of at least stElemSize
  5255. ** bytes used for swapping
  5256. **
  5257. ** Returns: void
  5258. **
  5259. ** Side Effects: none
  5260. */
  5261. void HeapSort(PVOID pvArray, LONG lcElements, size_t stElemSize,
  5262. COMPARESORTEDELEMSPROC cep, PVOID pvTemp)
  5263. {
  5264. ASSERT(lcElements >= 0);
  5265. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pvArray, VOID, lcElements * stElemSize));
  5266. ASSERT(IS_VALID_CODE_PTR(cep, COMPARESORTEDELEMSPROC));
  5267. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pvTemp, VOID, stElemSize));
  5268. /* Are there any elements to sort (2 or more)? */
  5269. if (lcElements > 1)
  5270. {
  5271. LONG li;
  5272. LONG liLastUsed = lcElements - 1;
  5273. /* Yes. Create partially ordered tree. */
  5274. for (li = liLastUsed / 2; li >= 0; li--)
  5275. HeapSift(pvArray, li, liLastUsed, stElemSize, cep, pvTemp);
  5276. for (li = liLastUsed; li >= 1; li--)
  5277. {
  5278. /* Remove minimum from front of heap. */
  5279. HeapSwap(pvArray, 0, li, stElemSize, pvTemp);
  5280. /* Reestablish partially ordered tree. */
  5281. HeapSift(pvArray, 0, li - 1, stElemSize, cep, pvTemp);
  5282. }
  5283. }
  5284. ASSERT(InSortedOrder(pvArray, lcElements, stElemSize, cep));
  5285. }
  5286. /*
  5287. ** BinarySearch()
  5288. **
  5289. ** Searches an array for a given element.
  5290. **
  5291. ** Arguments: pvArray - pointer to base of array
  5292. ** lcElements - number of elements in array
  5293. ** stElemSize - length of element in bytes
  5294. ** cep - element comparison callback function
  5295. ** pvTarget - pointer to target element to search for
  5296. ** pliTarget - pointer to LONG to be filled in with index of
  5297. ** target element if found
  5298. **
  5299. ** Returns: TRUE if target element found, or FALSE if not.
  5300. **
  5301. ** Side Effects: none
  5302. */
  5303. BOOL BinarySearch(PVOID pvArray, LONG lcElements,
  5304. size_t stElemSize, COMPARESORTEDELEMSPROC cep,
  5305. PCVOID pcvTarget, PLONG pliTarget)
  5306. {
  5307. BOOL bFound = FALSE;
  5308. ASSERT(lcElements >= 0);
  5309. ASSERT(IS_VALID_READ_BUFFER_PTR(pvArray, VOID, lcElements * stElemSize));
  5310. ASSERT(IS_VALID_CODE_PTR(cep, COMPARESORTEDELEMSPROC));
  5311. ASSERT(IS_VALID_READ_BUFFER_PTR(pcvTarget, VOID, stElemSize));
  5312. ASSERT(IS_VALID_WRITE_PTR(pliTarget, LONG));
  5313. /* Are there any elements to search through? */
  5314. if (lcElements > 0)
  5315. {
  5316. LONG liLow = 0;
  5317. LONG liMiddle = 0;
  5318. LONG liHigh = lcElements - 1;
  5319. COMPARISONRESULT cr = CR_EQUAL;
  5320. /* Yes. Search for the target element. */
  5321. /*
  5322. * At the end of the penultimate iteration of this loop:
  5323. *
  5324. * liLow == liMiddle == liHigh.
  5325. */
  5326. while (liLow <= liHigh)
  5327. {
  5328. liMiddle = (liLow + liHigh) / 2;
  5329. cr = (*cep)(pcvTarget, & ARRAY_ELEMENT_SIZE(pvArray, liMiddle, stElemSize));
  5330. if (cr == CR_FIRST_SMALLER)
  5331. liHigh = liMiddle - 1;
  5332. else if (cr == CR_FIRST_LARGER)
  5333. liLow = liMiddle + 1;
  5334. else
  5335. {
  5336. *pliTarget = liMiddle;
  5337. bFound = TRUE;
  5338. break;
  5339. }
  5340. }
  5341. }
  5342. return(bFound);
  5343. }
  5344. /* Types
  5345. ********/
  5346. /* string table */
  5347. typedef struct _stringtable
  5348. {
  5349. /* number of hash buckets in string table */
  5350. HASHBUCKETCOUNT hbc;
  5351. /* pointer to array of hash buckets (HLISTs) */
  5352. PHLIST phlistHashBuckets;
  5353. }
  5354. STRINGTABLE;
  5355. DECLARE_STANDARD_TYPES(STRINGTABLE);
  5356. /* string heap structure */
  5357. typedef struct _string
  5358. {
  5359. /* lock count of string */
  5360. ULONG ulcLock;
  5361. /* actual string */
  5362. TCHAR string[1];
  5363. }
  5364. BFCSTRING;
  5365. DECLARE_STANDARD_TYPES(BFCSTRING);
  5366. /* string table database structure header */
  5367. typedef struct _stringtabledbheader
  5368. {
  5369. /*
  5370. * length of longest string in string table, not including null terminator
  5371. */
  5372. DWORD dwcbMaxStringLen;
  5373. /* number of strings in string table */
  5374. LONG lcStrings;
  5375. }
  5376. STRINGTABLEDBHEADER;
  5377. DECLARE_STANDARD_TYPES(STRINGTABLEDBHEADER);
  5378. /* database string header */
  5379. typedef struct _dbstringheader
  5380. {
  5381. /* old handle to this string */
  5382. HSTRING hsOld;
  5383. }
  5384. DBSTRINGHEADER;
  5385. DECLARE_STANDARD_TYPES(DBSTRINGHEADER);
  5386. /* Module Prototypes
  5387. ********************/
  5388. COMPARISONRESULT StringSearchCmp(PCVOID, PCVOID);
  5389. COMPARISONRESULT StringSortCmp(PCVOID, PCVOID);
  5390. BOOL UnlockString(PBFCSTRING);
  5391. BOOL FreeStringWalker(PVOID, PVOID);
  5392. void FreeHashBucket(HLIST);
  5393. TWINRESULT WriteHashBucket(HCACHEDFILE, HLIST, PLONG, PDWORD);
  5394. TWINRESULT WriteString(HCACHEDFILE, HNODE, PBFCSTRING, PDWORD);
  5395. TWINRESULT ReadString(HCACHEDFILE, HSTRINGTABLE, HHANDLETRANS, LPTSTR, DWORD);
  5396. TWINRESULT SlowReadString(HCACHEDFILE, LPTSTR, DWORD);
  5397. /*
  5398. ** StringSearchCmp()
  5399. **
  5400. **
  5401. **
  5402. ** Arguments:
  5403. **
  5404. ** Returns:
  5405. **
  5406. ** Side Effects: none
  5407. */
  5408. COMPARISONRESULT StringSearchCmp(PCVOID pcszPath, PCVOID pcstring)
  5409. {
  5410. ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
  5411. ASSERT(IS_VALID_STRUCT_PTR(pcstring, PCBFCSTRING));
  5412. return(MapIntToComparisonResult(lstrcmp((LPCTSTR)pcszPath,
  5413. (LPCTSTR)&(((PCBFCSTRING)pcstring)->string))));
  5414. }
  5415. COMPARISONRESULT StringSortCmp(PCVOID pcstring1, PCVOID pcstring2)
  5416. {
  5417. ASSERT(IS_VALID_STRUCT_PTR(pcstring1, PCBFCSTRING));
  5418. ASSERT(IS_VALID_STRUCT_PTR(pcstring2, PCBFCSTRING));
  5419. return(MapIntToComparisonResult(lstrcmp((LPCTSTR)&(((PCBFCSTRING)pcstring1)->string),
  5420. (LPCTSTR)&(((PCBFCSTRING)pcstring2)->string))));
  5421. }
  5422. BOOL UnlockString(PBFCSTRING pstring)
  5423. {
  5424. ASSERT(IS_VALID_STRUCT_PTR(pstring, PCBFCSTRING));
  5425. /* Is the lock count going to underflow? */
  5426. if (EVAL(pstring->ulcLock > 0))
  5427. pstring->ulcLock--;
  5428. return(pstring->ulcLock > 0);
  5429. }
  5430. BOOL FreeStringWalker(PVOID pstring, PVOID pvUnused)
  5431. {
  5432. ASSERT(IS_VALID_STRUCT_PTR(pstring, PCBFCSTRING));
  5433. ASSERT(! pvUnused);
  5434. FreeMemory(pstring);
  5435. return(TRUE);
  5436. }
  5437. /*
  5438. ** FreeHashBucket()
  5439. **
  5440. ** Frees the strings in a hash bucket, and the hash bucket's string list.
  5441. **
  5442. ** Arguments: hlistHashBucket - handle to hash bucket's list of strings
  5443. **
  5444. ** Returns: void
  5445. **
  5446. ** Side Effects: none
  5447. **
  5448. ** N.b., this function ignores the lock counts of the strings in the hash
  5449. ** bucket. All strings in the hash bucket are freed.
  5450. */
  5451. void FreeHashBucket(HLIST hlistHashBucket)
  5452. {
  5453. ASSERT(! hlistHashBucket || IS_VALID_HANDLE(hlistHashBucket, LIST));
  5454. /* Are there any strings in this hash bucket to delete? */
  5455. if (hlistHashBucket)
  5456. {
  5457. /* Yes. Delete all strings in list. */
  5458. EVAL(WalkList(hlistHashBucket, &FreeStringWalker, NULL));
  5459. /* Delete hash bucket string list. */
  5460. DestroyList(hlistHashBucket);
  5461. }
  5462. }
  5463. /*
  5464. ** MyGetStringLen()
  5465. **
  5466. ** Retrieves the length of a string in a string table.
  5467. **
  5468. ** Arguments: pcstring - pointer to string whose length is to be
  5469. ** determined
  5470. **
  5471. ** Returns: Length of string in bytes, not including null terminator.
  5472. **
  5473. ** Side Effects: none
  5474. */
  5475. int MyGetStringLen(PCBFCSTRING pcstring)
  5476. {
  5477. ASSERT(IS_VALID_STRUCT_PTR(pcstring, PCBFCSTRING));
  5478. return(lstrlen(pcstring->string) * sizeof(TCHAR));
  5479. }
  5480. TWINRESULT WriteHashBucket(HCACHEDFILE hcf,
  5481. HLIST hlistHashBucket,
  5482. PLONG plcStrings,
  5483. PDWORD pdwcbMaxStringLen)
  5484. {
  5485. TWINRESULT tr = TR_SUCCESS;
  5486. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  5487. ASSERT(! hlistHashBucket || IS_VALID_HANDLE(hlistHashBucket, LIST));
  5488. ASSERT(IS_VALID_WRITE_PTR(plcStrings, LONG));
  5489. ASSERT(IS_VALID_WRITE_PTR(pdwcbMaxStringLen, DWORD));
  5490. /* Any strings in this hash bucket? */
  5491. *plcStrings = 0;
  5492. *pdwcbMaxStringLen = 0;
  5493. if (hlistHashBucket)
  5494. {
  5495. BOOL bContinue;
  5496. HNODE hnode;
  5497. /* Yes. Walk hash bucket, saving each string. */
  5498. for (bContinue = GetFirstNode(hlistHashBucket, &hnode);
  5499. bContinue;
  5500. bContinue = GetNextNode(hnode, &hnode))
  5501. {
  5502. PBFCSTRING pstring;
  5503. pstring = (PBFCSTRING)GetNodeData(hnode);
  5504. ASSERT(IS_VALID_STRUCT_PTR(pstring, PCBFCSTRING));
  5505. /*
  5506. * As a sanity check, don't save any string with a lock count of 0. A
  5507. * 0 lock count implies that the string has not been referenced since
  5508. * it was restored from the database, or something is broken.
  5509. */
  5510. if (pstring->ulcLock > 0)
  5511. {
  5512. DWORD dwcbStringLen;
  5513. tr = WriteString(hcf, hnode, pstring, &dwcbStringLen);
  5514. if (tr == TR_SUCCESS)
  5515. {
  5516. if (dwcbStringLen > *pdwcbMaxStringLen)
  5517. *pdwcbMaxStringLen = dwcbStringLen;
  5518. ASSERT(*plcStrings < LONG_MAX);
  5519. (*plcStrings)++;
  5520. }
  5521. else
  5522. break;
  5523. }
  5524. else
  5525. ERROR_OUT((TEXT("WriteHashBucket(): String \"%s\" has 0 lock count and will not be saved."),
  5526. pstring->string));
  5527. }
  5528. }
  5529. return(tr);
  5530. }
  5531. TWINRESULT WriteString(HCACHEDFILE hcf, HNODE hnodeOld,
  5532. PBFCSTRING pstring, PDWORD pdwcbStringLen)
  5533. {
  5534. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  5535. DBSTRINGHEADER dbsh;
  5536. /* (+ 1) for null terminator. */
  5537. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  5538. ASSERT(IS_VALID_HANDLE(hnodeOld, NODE));
  5539. ASSERT(IS_VALID_STRUCT_PTR(pstring, PCBFCSTRING));
  5540. ASSERT(IS_VALID_READ_BUFFER_PTR(pstring, BFCSTRING, sizeof(BFCSTRING) + MyGetStringLen(pstring) + sizeof(TCHAR) - sizeof(pstring->string)));
  5541. ASSERT(IS_VALID_WRITE_PTR(pdwcbStringLen, DWORD));
  5542. /* Create string header. */
  5543. dbsh.hsOld = (HSTRING)hnodeOld;
  5544. /* Save string header and string. */
  5545. if (WriteToCachedFile(hcf, (PCVOID)&dbsh, sizeof(dbsh), NULL))
  5546. {
  5547. LPSTR pszAnsi;
  5548. /* (+ 1) for null terminator. */
  5549. *pdwcbStringLen = MyGetStringLen(pstring) + SIZEOF(TCHAR);
  5550. // If its unicode, convert the string to ansi before writing it out
  5551. {
  5552. pszAnsi = LocalAlloc(LPTR, *pdwcbStringLen);
  5553. if (NULL == pszAnsi)
  5554. {
  5555. return tr;
  5556. }
  5557. WideCharToMultiByte( OurGetACP(), 0, pstring->string, -1, pszAnsi, *pdwcbStringLen, NULL, NULL);
  5558. // We should always have a string at this point that can be converted losslessly
  5559. #ifdef DEBUG
  5560. {
  5561. WCHAR szUnicode[MAX_PATH*2];
  5562. MultiByteToWideChar( OurGetACP(), 0, pszAnsi, -1, szUnicode, ARRAYSIZE(szUnicode));
  5563. ASSERT(0 == lstrcmp(szUnicode, pstring->string));
  5564. }
  5565. #endif
  5566. if (WriteToCachedFile(hcf, (PCVOID) pszAnsi, lstrlenA(pszAnsi) + 1, NULL))
  5567. tr = TR_SUCCESS;
  5568. LocalFree(pszAnsi);
  5569. }
  5570. }
  5571. return(tr);
  5572. }
  5573. TWINRESULT ReadString(HCACHEDFILE hcf, HSTRINGTABLE hst,
  5574. HHANDLETRANS hht, LPTSTR pszStringBuf,
  5575. DWORD dwcbStringBufLen)
  5576. {
  5577. TWINRESULT tr;
  5578. DBSTRINGHEADER dbsh;
  5579. DWORD dwcbRead;
  5580. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  5581. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5582. ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  5583. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszStringBuf, STR, (UINT)dwcbStringBufLen));
  5584. if (ReadFromCachedFile(hcf, &dbsh, sizeof(dbsh), &dwcbRead) &&
  5585. dwcbRead == sizeof(dbsh))
  5586. {
  5587. tr = SlowReadString(hcf, pszStringBuf, dwcbStringBufLen);
  5588. if (tr == TR_SUCCESS)
  5589. {
  5590. HSTRING hsNew;
  5591. if (AddString(pszStringBuf, hst, GetHashBucketIndex, &hsNew))
  5592. {
  5593. /*
  5594. * We must undo the LockString() performed by AddString() to
  5595. * maintain the correct string lock count. N.b., the lock count of
  5596. * a string may be > 0 even after unlocking since the client may
  5597. * already have added the string to the given string table.
  5598. */
  5599. UnlockString((PBFCSTRING)GetNodeData((HNODE)hsNew));
  5600. if (! AddHandleToHandleTranslator(hht, (HGENERIC)(dbsh.hsOld), (HGENERIC)hsNew))
  5601. {
  5602. DeleteNode((HNODE)hsNew);
  5603. tr = TR_CORRUPT_BRIEFCASE;
  5604. }
  5605. }
  5606. else
  5607. tr = TR_OUT_OF_MEMORY;
  5608. }
  5609. }
  5610. else
  5611. tr = TR_CORRUPT_BRIEFCASE;
  5612. return(tr);
  5613. }
  5614. TWINRESULT SlowReadString(HCACHEDFILE hcf, LPTSTR pszStringBuf,
  5615. DWORD dwcbStringBufLen)
  5616. {
  5617. TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  5618. LPTSTR pszStringBufEnd;
  5619. DWORD dwcbRead;
  5620. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  5621. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszStringBuf, STR, (UINT)dwcbStringBufLen));
  5622. pszStringBufEnd = pszStringBuf + dwcbStringBufLen;
  5623. // The database strings are always written ANSI, so if we are running unicode,
  5624. // we need to convert as we go
  5625. {
  5626. LPSTR pszAnsiEnd;
  5627. LPSTR pszAnsiStart;
  5628. LPSTR pszAnsi = LocalAlloc(LPTR, dwcbStringBufLen);
  5629. pszAnsiStart = pszAnsi;
  5630. pszAnsiEnd = pszAnsi + dwcbStringBufLen;
  5631. if (NULL == pszAnsi)
  5632. {
  5633. return tr;
  5634. }
  5635. while (pszAnsi < pszAnsiEnd &&
  5636. ReadFromCachedFile(hcf, pszAnsi, sizeof(*pszAnsi), &dwcbRead) &&
  5637. dwcbRead == sizeof(*pszAnsi))
  5638. {
  5639. if (*pszAnsi)
  5640. pszAnsi++;
  5641. else
  5642. {
  5643. tr = TR_SUCCESS;
  5644. break;
  5645. }
  5646. }
  5647. if (tr == TR_SUCCESS)
  5648. {
  5649. MultiByteToWideChar( OurGetACP(), 0, pszAnsiStart, -1, pszStringBuf, dwcbStringBufLen / sizeof(TCHAR));
  5650. }
  5651. LocalFree(pszAnsiStart);
  5652. }
  5653. return(tr);
  5654. }
  5655. /*
  5656. ** CreateStringTable()
  5657. **
  5658. ** Creates a new string table.
  5659. **
  5660. ** Arguments: pcnszt - pointer to NEWSTRINGTABLE descibing string table to
  5661. ** be created
  5662. **
  5663. ** Returns: Handle to new string table if successful, or NULL if
  5664. ** unsuccessful.
  5665. **
  5666. ** Side Effects: none
  5667. */
  5668. BOOL CreateStringTable(PCNEWSTRINGTABLE pcnszt,
  5669. PHSTRINGTABLE phst)
  5670. {
  5671. PSTRINGTABLE pst;
  5672. ASSERT(IS_VALID_STRUCT_PTR(pcnszt, CNEWSTRINGTABLE));
  5673. ASSERT(IS_VALID_WRITE_PTR(phst, HSTRINGTABLE));
  5674. /* Try to allocate new string table structure. */
  5675. *phst = NULL;
  5676. if (AllocateMemory(sizeof(*pst), &pst))
  5677. {
  5678. PHLIST phlistHashBuckets;
  5679. /* Try to allocate hash bucket array. */
  5680. if (AllocateMemory(pcnszt->hbc * sizeof(*phlistHashBuckets), (PVOID *)(&phlistHashBuckets)))
  5681. {
  5682. HASHBUCKETCOUNT bc;
  5683. /* Successs! Initialize STRINGTABLE fields. */
  5684. pst->phlistHashBuckets = phlistHashBuckets;
  5685. pst->hbc = pcnszt->hbc;
  5686. /* Initialize all hash buckets to NULL. */
  5687. for (bc = 0; bc < pcnszt->hbc; bc++)
  5688. phlistHashBuckets[bc] = NULL;
  5689. *phst = (HSTRINGTABLE)pst;
  5690. ASSERT(IS_VALID_HANDLE(*phst, STRINGTABLE));
  5691. }
  5692. else
  5693. /* Free string table structure. */
  5694. FreeMemory(pst);
  5695. }
  5696. return(*phst != NULL);
  5697. }
  5698. void DestroyStringTable(HSTRINGTABLE hst)
  5699. {
  5700. HASHBUCKETCOUNT bc;
  5701. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5702. /* Traverse array of hash bucket heads, freeing hash bucket strings. */
  5703. for (bc = 0; bc < ((PSTRINGTABLE)hst)->hbc; bc++)
  5704. FreeHashBucket(((PSTRINGTABLE)hst)->phlistHashBuckets[bc]);
  5705. /* Free array of hash buckets. */
  5706. FreeMemory(((PSTRINGTABLE)hst)->phlistHashBuckets);
  5707. /* Free string table structure. */
  5708. FreeMemory((PSTRINGTABLE)hst);
  5709. }
  5710. /*
  5711. ** AddString()
  5712. **
  5713. ** Adds a string to a string table.
  5714. **
  5715. ** Arguments: pcsz - pointer to string to be added
  5716. ** hst - handle to string table that string is to be added to
  5717. **
  5718. ** Returns: Handle to new string if successful, or NULL if unsuccessful.
  5719. **
  5720. ** Side Effects: none
  5721. */
  5722. BOOL AddString(LPCTSTR pcsz, HSTRINGTABLE hst,
  5723. STRINGTABLEHASHFUNC pfnHashFunc, PHSTRING phs)
  5724. {
  5725. BOOL bResult;
  5726. HASHBUCKETCOUNT hbcNew;
  5727. BOOL bFound;
  5728. HNODE hnode;
  5729. PHLIST phlistHashBucket;
  5730. ASSERT(IS_VALID_STRING_PTR(pcsz, CSTR));
  5731. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5732. ASSERT(IS_VALID_CODE_PTR(pfnHashFunc, STRINGTABLEHASHFUNC));
  5733. ASSERT(IS_VALID_WRITE_PTR(phs, HSTRING));
  5734. /* Find appropriate hash bucket. */
  5735. hbcNew = pfnHashFunc(pcsz, ((PSTRINGTABLE)hst)->hbc);
  5736. ASSERT(hbcNew < ((PSTRINGTABLE)hst)->hbc);
  5737. phlistHashBucket = &(((PSTRINGTABLE)hst)->phlistHashBuckets[hbcNew]);
  5738. if (*phlistHashBucket)
  5739. {
  5740. /* Search the hash bucket for the string. */
  5741. bFound = SearchSortedList(*phlistHashBucket, &StringSearchCmp, pcsz,
  5742. &hnode);
  5743. bResult = TRUE;
  5744. }
  5745. else
  5746. {
  5747. NEWLIST nl;
  5748. /* Create a string list for this hash bucket. */
  5749. bFound = FALSE;
  5750. nl.dwFlags = NL_FL_SORTED_ADD;
  5751. bResult = CreateList(&nl, phlistHashBucket);
  5752. }
  5753. /* Do we have a hash bucket for the string? */
  5754. if (bResult)
  5755. {
  5756. /* Yes. Is the string already in the hash bucket? */
  5757. if (bFound)
  5758. {
  5759. /* Yes. */
  5760. LockString((HSTRING)hnode);
  5761. *phs = (HSTRING)hnode;
  5762. }
  5763. else
  5764. {
  5765. /* No. Create it. */
  5766. PBFCSTRING pstringNew;
  5767. /* (+ 1) for null terminator. */
  5768. bResult = AllocateMemory(sizeof(*pstringNew) - sizeof(pstringNew->string)
  5769. + (lstrlen(pcsz) + 1) * sizeof(TCHAR), &pstringNew);
  5770. if (bResult)
  5771. {
  5772. HNODE hnodeNew;
  5773. /* Set up BFCSTRING fields. */
  5774. pstringNew->ulcLock = 1;
  5775. lstrcpy(pstringNew->string, pcsz);
  5776. /* What's up with this string, Doc? */
  5777. bResult = AddNode(*phlistHashBucket, StringSortCmp, pstringNew, &hnodeNew);
  5778. /* Was the new string added to the hash bucket successfully? */
  5779. if (bResult)
  5780. /* Yes. */
  5781. *phs = (HSTRING)hnodeNew;
  5782. else
  5783. /* No. */
  5784. FreeMemory(pstringNew);
  5785. }
  5786. }
  5787. }
  5788. ASSERT(! bResult ||
  5789. IS_VALID_HANDLE(*phs, BFCSTRING));
  5790. return(bResult);
  5791. }
  5792. /*
  5793. ** DeleteString()
  5794. **
  5795. ** Decrements a string's lock count. If the lock count goes to 0, the string
  5796. ** is deleted from its string table.
  5797. **
  5798. ** Arguments: hs - handle to the string to be deleted
  5799. **
  5800. ** Returns: void
  5801. **
  5802. ** Side Effects: none
  5803. */
  5804. void DeleteString(HSTRING hs)
  5805. {
  5806. PBFCSTRING pstring;
  5807. ASSERT(IS_VALID_HANDLE(hs, BFCSTRING));
  5808. pstring = (PBFCSTRING)GetNodeData((HNODE)hs);
  5809. /* Delete string completely? */
  5810. if (! UnlockString(pstring))
  5811. {
  5812. /* Yes. Remove the string node from the hash bucket's list. */
  5813. DeleteNode((HNODE)hs);
  5814. FreeMemory(pstring);
  5815. }
  5816. }
  5817. /*
  5818. ** LockString()
  5819. **
  5820. ** Increments a string's lock count.
  5821. **
  5822. ** Arguments: hs - handle to string whose lock count is to be incremented
  5823. **
  5824. ** Returns: void
  5825. **
  5826. ** Side Effects: none
  5827. */
  5828. void LockString(HSTRING hs)
  5829. {
  5830. PBFCSTRING pstring;
  5831. ASSERT(IS_VALID_HANDLE(hs, BFCSTRING));
  5832. /* Increment lock count. */
  5833. pstring = (PBFCSTRING)GetNodeData((HNODE)hs);
  5834. ASSERT(pstring->ulcLock < ULONG_MAX);
  5835. pstring->ulcLock++;
  5836. }
  5837. COMPARISONRESULT CompareStringsI(HSTRING hs1, HSTRING hs2)
  5838. {
  5839. ASSERT(IS_VALID_HANDLE(hs1, BFCSTRING));
  5840. ASSERT(IS_VALID_HANDLE(hs2, BFCSTRING));
  5841. /* This comparison works across string tables. */
  5842. return(MapIntToComparisonResult(lstrcmpi(((PCBFCSTRING)GetNodeData((HNODE)hs1))->string,
  5843. ((PCBFCSTRING)GetNodeData((HNODE)hs2))->string)));
  5844. }
  5845. /*
  5846. ** GetBfcString()
  5847. **
  5848. ** Retrieves a pointer to a string in a string table.
  5849. **
  5850. ** Arguments: hs - handle to the string to be retrieved
  5851. **
  5852. ** Returns: Pointer to string.
  5853. **
  5854. ** Side Effects: none
  5855. */
  5856. LPCTSTR GetBfcString(HSTRING hs)
  5857. {
  5858. PBFCSTRING pstring;
  5859. ASSERT(IS_VALID_HANDLE(hs, BFCSTRING));
  5860. pstring = (PBFCSTRING)GetNodeData((HNODE)hs);
  5861. return((LPCTSTR)&(pstring->string));
  5862. }
  5863. TWINRESULT WriteStringTable(HCACHEDFILE hcf, HSTRINGTABLE hst)
  5864. {
  5865. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  5866. DWORD dwcbStringTableDBHeaderOffset;
  5867. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  5868. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5869. /* Save initial file poisition. */
  5870. dwcbStringTableDBHeaderOffset = GetCachedFilePointerPosition(hcf);
  5871. if (dwcbStringTableDBHeaderOffset != INVALID_SEEK_POSITION)
  5872. {
  5873. STRINGTABLEDBHEADER stdbh;
  5874. /* Leave space for the string table header. */
  5875. ZeroMemory(&stdbh, sizeof(stdbh));
  5876. if (WriteToCachedFile(hcf, (PCVOID)&stdbh, sizeof(stdbh), NULL))
  5877. {
  5878. HASHBUCKETCOUNT hbc;
  5879. /* Save strings in each hash bucket. */
  5880. stdbh.dwcbMaxStringLen = 0;
  5881. stdbh.lcStrings = 0;
  5882. tr = TR_SUCCESS;
  5883. for (hbc = 0; hbc < ((PSTRINGTABLE)hst)->hbc; hbc++)
  5884. {
  5885. LONG lcStringsInHashBucket;
  5886. DWORD dwcbStringLen;
  5887. tr = WriteHashBucket(hcf,
  5888. (((PSTRINGTABLE)hst)->phlistHashBuckets)[hbc],
  5889. &lcStringsInHashBucket, &dwcbStringLen);
  5890. if (tr == TR_SUCCESS)
  5891. {
  5892. /* Watch out for overflow. */
  5893. ASSERT(stdbh.lcStrings <= LONG_MAX - lcStringsInHashBucket);
  5894. stdbh.lcStrings += lcStringsInHashBucket;
  5895. if (dwcbStringLen > stdbh.dwcbMaxStringLen)
  5896. stdbh.dwcbMaxStringLen = dwcbStringLen;
  5897. }
  5898. else
  5899. break;
  5900. }
  5901. if (tr == TR_SUCCESS)
  5902. {
  5903. /* Save string table header. */
  5904. // The on-disk dwCBMaxString len always refers to ANSI chars,
  5905. // whereas in memory it is for the TCHAR type, we adjust it
  5906. // around the save
  5907. stdbh.dwcbMaxStringLen /= sizeof(TCHAR);
  5908. tr = WriteDBSegmentHeader(hcf, dwcbStringTableDBHeaderOffset,
  5909. &stdbh, sizeof(stdbh));
  5910. stdbh.dwcbMaxStringLen *= sizeof(TCHAR);
  5911. TRACE_OUT((TEXT("WriteStringTable(): Wrote %ld strings."),
  5912. stdbh.lcStrings));
  5913. }
  5914. }
  5915. }
  5916. return(tr);
  5917. }
  5918. TWINRESULT ReadStringTable(HCACHEDFILE hcf, HSTRINGTABLE hst,
  5919. PHHANDLETRANS phhtTrans)
  5920. {
  5921. TWINRESULT tr;
  5922. STRINGTABLEDBHEADER stdbh;
  5923. DWORD dwcbRead;
  5924. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  5925. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5926. ASSERT(IS_VALID_WRITE_PTR(phhtTrans, HHANDLETRANS));
  5927. if (ReadFromCachedFile(hcf, &stdbh, sizeof(stdbh), &dwcbRead) &&
  5928. dwcbRead == sizeof(stdbh))
  5929. {
  5930. LPTSTR pszStringBuf;
  5931. // The string header will have the ANSI cb max, whereas inmemory
  5932. // we need the cb max based on the current character size
  5933. stdbh.dwcbMaxStringLen *= sizeof(TCHAR);
  5934. if (AllocateMemory(stdbh.dwcbMaxStringLen, &pszStringBuf))
  5935. {
  5936. HHANDLETRANS hht;
  5937. if (CreateHandleTranslator(stdbh.lcStrings, &hht))
  5938. {
  5939. LONG lcStrings;
  5940. tr = TR_SUCCESS;
  5941. TRACE_OUT((TEXT("ReadStringTable(): Reading %ld strings, maximum length %lu."),
  5942. stdbh.lcStrings,
  5943. stdbh.dwcbMaxStringLen));
  5944. for (lcStrings = 0;
  5945. lcStrings < stdbh.lcStrings && tr == TR_SUCCESS;
  5946. lcStrings++)
  5947. tr = ReadString(hcf, hst, hht, pszStringBuf, stdbh.dwcbMaxStringLen);
  5948. if (tr == TR_SUCCESS)
  5949. {
  5950. PrepareForHandleTranslation(hht);
  5951. *phhtTrans = hht;
  5952. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5953. ASSERT(IS_VALID_HANDLE(*phhtTrans, HANDLETRANS));
  5954. }
  5955. else
  5956. DestroyHandleTranslator(hht);
  5957. }
  5958. else
  5959. tr = TR_OUT_OF_MEMORY;
  5960. FreeMemory(pszStringBuf);
  5961. }
  5962. else
  5963. tr = TR_OUT_OF_MEMORY;
  5964. }
  5965. else
  5966. tr = TR_CORRUPT_BRIEFCASE;
  5967. return(tr);
  5968. }
  5969. #ifdef DEBUG
  5970. ULONG GetStringCount(HSTRINGTABLE hst)
  5971. {
  5972. ULONG ulcStrings = 0;
  5973. HASHBUCKETCOUNT hbc;
  5974. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  5975. for (hbc = 0; hbc < ((PCSTRINGTABLE)hst)->hbc; hbc++)
  5976. {
  5977. HLIST hlistHashBucket;
  5978. hlistHashBucket = (((PCSTRINGTABLE)hst)->phlistHashBuckets)[hbc];
  5979. if (hlistHashBucket)
  5980. {
  5981. ASSERT(ulcStrings <= ULONG_MAX - GetNodeCount(hlistHashBucket));
  5982. ulcStrings += GetNodeCount(hlistHashBucket);
  5983. }
  5984. }
  5985. return(ulcStrings);
  5986. }
  5987. #endif
  5988. /* Macros
  5989. *********/
  5990. /* get a pointer to the stub type descriptor for a STUB */
  5991. #define GetStubTypeDescriptor(pcs) (&(Mrgcstd[pcs->st]))
  5992. /* Types
  5993. ********/
  5994. /* stub functions */
  5995. typedef TWINRESULT (*UNLINKSTUBPROC)(PSTUB);
  5996. typedef void (*DESTROYSTUBPROC)(PSTUB);
  5997. typedef void (*LOCKSTUBPROC)(PSTUB);
  5998. typedef void (*UNLOCKSTUBPROC)(PSTUB);
  5999. /* stub type descriptor */
  6000. typedef struct _stubtypedescriptor
  6001. {
  6002. UNLINKSTUBPROC UnlinkStub;
  6003. DESTROYSTUBPROC DestroyStub;
  6004. LOCKSTUBPROC LockStub;
  6005. UNLOCKSTUBPROC UnlockStub;
  6006. }
  6007. STUBTYPEDESCRIPTOR;
  6008. DECLARE_STANDARD_TYPES(STUBTYPEDESCRIPTOR);
  6009. /* Module Prototypes
  6010. ********************/
  6011. void LockSingleStub(PSTUB);
  6012. void UnlockSingleStub(PSTUB);
  6013. #ifdef DEBUG
  6014. LPCTSTR GetStubName(PCSTUB);
  6015. #endif
  6016. /* Module Variables
  6017. *******************/
  6018. /* stub type descriptors */
  6019. /* Cast off compiler complaints about pointer argument mismatch. */
  6020. CONST STUBTYPEDESCRIPTOR Mrgcstd[] =
  6021. {
  6022. /* object twin STUB descriptor */
  6023. {
  6024. (UNLINKSTUBPROC)UnlinkObjectTwin,
  6025. (DESTROYSTUBPROC)DestroyObjectTwin,
  6026. LockSingleStub,
  6027. UnlockSingleStub
  6028. },
  6029. /* twin family STUB descriptor */
  6030. {
  6031. (UNLINKSTUBPROC)UnlinkTwinFamily,
  6032. (DESTROYSTUBPROC)DestroyTwinFamily,
  6033. LockSingleStub,
  6034. UnlockSingleStub
  6035. },
  6036. /* folder pair STUB descriptor */
  6037. {
  6038. (UNLINKSTUBPROC)UnlinkFolderPair,
  6039. (DESTROYSTUBPROC)DestroyFolderPair,
  6040. (LOCKSTUBPROC)LockFolderPair,
  6041. (UNLOCKSTUBPROC)UnlockFolderPair
  6042. }
  6043. };
  6044. void LockSingleStub(PSTUB ps)
  6045. {
  6046. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6047. ASSERT(IsStubFlagClear(ps, STUB_FL_UNLINKED));
  6048. ASSERT(ps->ulcLock < ULONG_MAX);
  6049. ps->ulcLock++;
  6050. }
  6051. void UnlockSingleStub(PSTUB ps)
  6052. {
  6053. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6054. if (EVAL(ps->ulcLock > 0))
  6055. {
  6056. ps->ulcLock--;
  6057. if (! ps->ulcLock &&
  6058. IsStubFlagSet(ps, STUB_FL_UNLINKED))
  6059. DestroyStub(ps);
  6060. }
  6061. }
  6062. #ifdef DEBUG
  6063. LPCTSTR GetStubName(PCSTUB pcs)
  6064. {
  6065. LPCTSTR pcszStubName;
  6066. ASSERT(IS_VALID_STRUCT_PTR(pcs, CSTUB));
  6067. switch (pcs->st)
  6068. {
  6069. case ST_OBJECTTWIN:
  6070. pcszStubName = TEXT("object twin");
  6071. break;
  6072. case ST_TWINFAMILY:
  6073. pcszStubName = TEXT("twin family");
  6074. break;
  6075. case ST_FOLDERPAIR:
  6076. pcszStubName = TEXT("folder twin");
  6077. break;
  6078. default:
  6079. ERROR_OUT((TEXT("GetStubName() called on unrecognized stub type %d."),
  6080. pcs->st));
  6081. pcszStubName = TEXT("UNKNOWN");
  6082. break;
  6083. }
  6084. ASSERT(IS_VALID_STRING_PTR(pcszStubName, CSTR));
  6085. return(pcszStubName);
  6086. }
  6087. #endif
  6088. /*
  6089. ** InitStub()
  6090. **
  6091. ** Initializes a stub.
  6092. **
  6093. ** Arguments: ps - pointer to stub to be initialized
  6094. ** st - type of stub
  6095. **
  6096. ** Returns: void
  6097. **
  6098. ** Side Effects: none
  6099. */
  6100. void InitStub(PSTUB ps, STUBTYPE st)
  6101. {
  6102. ASSERT(IS_VALID_WRITE_PTR(ps, STUB));
  6103. ps->st = st;
  6104. ps->ulcLock = 0;
  6105. ps->dwFlags = 0;
  6106. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6107. }
  6108. /*
  6109. ** DestroyStub()
  6110. **
  6111. ** Destroys a stub.
  6112. **
  6113. ** Arguments: ps - pointer to stub to be destroyed
  6114. **
  6115. ** Returns: TWINRESULT
  6116. **
  6117. ** Side Effects: Depends upon stub type.
  6118. */
  6119. TWINRESULT DestroyStub(PSTUB ps)
  6120. {
  6121. TWINRESULT tr;
  6122. PCSTUBTYPEDESCRIPTOR pcstd;
  6123. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6124. #ifdef DEBUG
  6125. if (IsStubFlagSet(ps, STUB_FL_UNLINKED) &&
  6126. ps->ulcLock > 0)
  6127. WARNING_OUT((TEXT("DestroyStub() called on unlinked locked %s stub %#lx."),
  6128. GetStubName(ps),
  6129. ps));
  6130. #endif
  6131. pcstd = GetStubTypeDescriptor(ps);
  6132. /* Is the stub already unlinked? */
  6133. if (IsStubFlagSet(ps, STUB_FL_UNLINKED))
  6134. /* Yes. */
  6135. tr = TR_SUCCESS;
  6136. else
  6137. /* No. Unlink it. */
  6138. tr = (*(pcstd->UnlinkStub))(ps);
  6139. /* Is the stub still locked? */
  6140. if (tr == TR_SUCCESS && ! ps->ulcLock)
  6141. /* No. Wipe it out. */
  6142. (*(pcstd->DestroyStub))(ps);
  6143. return(tr);
  6144. }
  6145. void LockStub(PSTUB ps)
  6146. {
  6147. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6148. (*(GetStubTypeDescriptor(ps)->LockStub))(ps);
  6149. }
  6150. /*
  6151. ** UnlockStub()
  6152. **
  6153. ** Unlocks a stub. Carries out any pending deletion on the stub.
  6154. **
  6155. ** Arguments: ps - pointer to stub to be unlocked
  6156. **
  6157. ** Returns: void
  6158. **
  6159. ** Side Effects: If the stub is unlinked and the lock count decreases to 0
  6160. ** after unlocking, the stub is deleted.
  6161. */
  6162. void UnlockStub(PSTUB ps)
  6163. {
  6164. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6165. (*(GetStubTypeDescriptor(ps)->UnlockStub))(ps);
  6166. }
  6167. DWORD GetStubFlags(PCSTUB pcs)
  6168. {
  6169. ASSERT(IS_VALID_STRUCT_PTR(pcs, CSTUB));
  6170. return(pcs->dwFlags);
  6171. }
  6172. /*
  6173. ** SetStubFlag()
  6174. **
  6175. ** Sets given flag in a stub. Other flags in stub are not affected.
  6176. **
  6177. ** Arguments: ps - pointer to stub whose flags are to be set
  6178. **
  6179. ** Returns: void
  6180. **
  6181. ** Side Effects: none
  6182. */
  6183. void SetStubFlag(PSTUB ps, DWORD dwFlags)
  6184. {
  6185. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6186. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_STUB_FLAGS));
  6187. SET_FLAG(ps->dwFlags, dwFlags);
  6188. }
  6189. /*
  6190. ** ClearStubFlag()
  6191. **
  6192. ** Clears given flag in a stub. Other flags in stub are not affected.
  6193. **
  6194. ** Arguments: ps - pointer to stub whose flags are to be set
  6195. **
  6196. ** Returns: void
  6197. **
  6198. ** Side Effects: none
  6199. */
  6200. void ClearStubFlag(PSTUB ps, DWORD dwFlags)
  6201. {
  6202. ASSERT(IS_VALID_STRUCT_PTR(ps, CSTUB));
  6203. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_STUB_FLAGS));
  6204. CLEAR_FLAG(ps->dwFlags, dwFlags);
  6205. }
  6206. BOOL IsStubFlagSet(PCSTUB pcs, DWORD dwFlags)
  6207. {
  6208. ASSERT(IS_VALID_STRUCT_PTR(pcs, CSTUB));
  6209. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_STUB_FLAGS));
  6210. return(IS_FLAG_SET(pcs->dwFlags, dwFlags));
  6211. }
  6212. BOOL IsStubFlagClear(PCSTUB pcs, DWORD dwFlags)
  6213. {
  6214. ASSERT(IS_VALID_STRUCT_PTR(pcs, CSTUB));
  6215. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_STUB_FLAGS));
  6216. return(IS_FLAG_CLEAR(pcs->dwFlags, dwFlags));
  6217. }
  6218. /* Constants
  6219. ************/
  6220. /* twin family pointer array allocation constants */
  6221. #define NUM_START_TWIN_FAMILY_PTRS (16)
  6222. #define NUM_TWIN_FAMILY_PTRS_TO_ADD (16)
  6223. /* Types
  6224. ********/
  6225. /* twin families database structure header */
  6226. typedef struct _twinfamiliesdbheader
  6227. {
  6228. /* number of twin families */
  6229. LONG lcTwinFamilies;
  6230. }
  6231. TWINFAMILIESDBHEADER;
  6232. DECLARE_STANDARD_TYPES(TWINFAMILIESDBHEADER);
  6233. /* individual twin family database structure header */
  6234. typedef struct _twinfamilydbheader
  6235. {
  6236. /* stub flags */
  6237. DWORD dwStubFlags;
  6238. /* old string handle of name */
  6239. HSTRING hsName;
  6240. /* number of object twins in family */
  6241. LONG lcObjectTwins;
  6242. }
  6243. TWINFAMILYDBHEADER;
  6244. DECLARE_STANDARD_TYPES(TWINFAMILYDBHEADER);
  6245. /* object twin database structure */
  6246. typedef struct _dbobjecttwin
  6247. {
  6248. /* stub flags */
  6249. DWORD dwStubFlags;
  6250. /* old handle to folder string */
  6251. HPATH hpath;
  6252. /* time stamp at last reconciliation */
  6253. FILESTAMP fsLastRec;
  6254. }
  6255. DBOBJECTTWIN;
  6256. DECLARE_STANDARD_TYPES(DBOBJECTTWIN);
  6257. /* GenerateSpinOffObjectTwin() callback structure */
  6258. typedef struct _spinoffobjecttwininfo
  6259. {
  6260. PCFOLDERPAIR pcfp;
  6261. HLIST hlistNewObjectTwins;
  6262. }
  6263. SPINOFFOBJECTTWININFO;
  6264. DECLARE_STANDARD_TYPES(SPINOFFOBJECTTWININFO);
  6265. typedef void (CALLBACK *COPYOBJECTTWINPROC)(POBJECTTWIN, PCDBOBJECTTWIN);
  6266. /* Module Prototypes
  6267. ********************/
  6268. TWINRESULT TwinJustTheseTwoObjects(HBRFCASE, HPATH, HPATH, LPCTSTR, POBJECTTWIN *, POBJECTTWIN *, HLIST);
  6269. BOOL CreateTwinFamily(HBRFCASE, LPCTSTR, PTWINFAMILY *);
  6270. void CollapseTwinFamilies(PTWINFAMILY, PTWINFAMILY);
  6271. BOOL GenerateSpinOffObjectTwin(PVOID, PVOID);
  6272. BOOL BuildBradyBunch(PVOID, PVOID);
  6273. BOOL CreateObjectTwinAndAddToList(PTWINFAMILY, HPATH, HLIST, POBJECTTWIN *, PHNODE);
  6274. BOOL CreateListOfGeneratedObjectTwins(PCFOLDERPAIR, PHLIST);
  6275. COMPARISONRESULT TwinFamilySortCmp(PCVOID, PCVOID);
  6276. COMPARISONRESULT TwinFamilySearchCmp(PCVOID, PCVOID);
  6277. BOOL ObjectTwinSearchCmp(PCVOID, PCVOID);
  6278. TWINRESULT WriteTwinFamily(HCACHEDFILE, PCTWINFAMILY);
  6279. TWINRESULT WriteObjectTwin(HCACHEDFILE, PCOBJECTTWIN);
  6280. TWINRESULT ReadTwinFamily(HCACHEDFILE, HBRFCASE, PCDBVERSION, HHANDLETRANS, HHANDLETRANS);
  6281. TWINRESULT ReadObjectTwin(HCACHEDFILE, PCDBVERSION, PTWINFAMILY, HHANDLETRANS);
  6282. void CopyTwinFamilyInfo(PTWINFAMILY, PCTWINFAMILYDBHEADER);
  6283. void CopyObjectTwinInfo(POBJECTTWIN, PCDBOBJECTTWIN);
  6284. void CopyM8ObjectTwinInfo(POBJECTTWIN, PCDBOBJECTTWIN);
  6285. BOOL DestroyObjectTwinStubWalker(PVOID, PVOID);
  6286. BOOL MarkObjectTwinNeverReconciledWalker(PVOID, PVOID);
  6287. BOOL LookForSrcFolderTwinsWalker(PVOID, PVOID);
  6288. BOOL IncrementSrcFolderTwinsWalker(PVOID, PVOID);
  6289. BOOL ClearSrcFolderTwinsWalker(PVOID, PVOID);
  6290. BOOL SetTwinFamilyWalker(PVOID, PVOID);
  6291. BOOL InsertNodeAtFrontWalker(POBJECTTWIN, PVOID);
  6292. BOOL IsReconciledFileStamp(PCFILESTAMP pcfs)
  6293. {
  6294. ASSERT(IS_VALID_STRUCT_PTR(pcfs, CFILESTAMP));
  6295. return(pcfs->fscond != FS_COND_UNAVAILABLE);
  6296. }
  6297. TWINRESULT TwinJustTheseTwoObjects(HBRFCASE hbr, HPATH hpathFolder1,
  6298. HPATH hpathFolder2, LPCTSTR pcszName,
  6299. POBJECTTWIN *ppot1,
  6300. POBJECTTWIN *ppot2,
  6301. HLIST hlistNewObjectTwins)
  6302. {
  6303. TWINRESULT tr = TR_OUT_OF_MEMORY;
  6304. HNODE hnodeSearch;
  6305. BOOL bFound1;
  6306. BOOL bFound2;
  6307. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  6308. ASSERT(IS_VALID_HANDLE(hpathFolder1, PATH));
  6309. ASSERT(IS_VALID_HANDLE(hpathFolder2, PATH));
  6310. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  6311. ASSERT(IS_VALID_WRITE_PTR(ppot1, POBJECTTWIN));
  6312. ASSERT(IS_VALID_WRITE_PTR(ppot2, POBJECTTWIN));
  6313. ASSERT(IS_VALID_HANDLE(hlistNewObjectTwins, LIST));
  6314. /* Determine twin families of existing object twins. */
  6315. bFound1 = FindObjectTwin(hbr, hpathFolder1, pcszName, &hnodeSearch);
  6316. if (bFound1)
  6317. *ppot1 = (POBJECTTWIN)GetNodeData(hnodeSearch);
  6318. bFound2 = FindObjectTwin(hbr, hpathFolder2, pcszName, &hnodeSearch);
  6319. if (bFound2)
  6320. *ppot2 = (POBJECTTWIN)GetNodeData(hnodeSearch);
  6321. /* Take action based upon existence of two object twins. */
  6322. if (! bFound1 && ! bFound2)
  6323. {
  6324. PTWINFAMILY ptfNew;
  6325. /* Neither object is already present. Create a new twin family. */
  6326. if (CreateTwinFamily(hbr, pcszName, &ptfNew))
  6327. {
  6328. HNODE hnodeNew1;
  6329. if (CreateObjectTwinAndAddToList(ptfNew, hpathFolder1,
  6330. hlistNewObjectTwins, ppot1,
  6331. &hnodeNew1))
  6332. {
  6333. HNODE hnodeNew2;
  6334. if (CreateObjectTwinAndAddToList(ptfNew, hpathFolder2,
  6335. hlistNewObjectTwins, ppot2,
  6336. &hnodeNew2))
  6337. {
  6338. TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Created a twin family for object %s in folders %s and %s."),
  6339. pcszName,
  6340. DebugGetPathString(hpathFolder1),
  6341. DebugGetPathString(hpathFolder2)));
  6342. ASSERT(IsStubFlagClear(&(ptfNew->stub), STUB_FL_DELETION_PENDING));
  6343. tr = TR_SUCCESS;
  6344. }
  6345. else
  6346. {
  6347. DeleteNode(hnodeNew1);
  6348. DestroyStub(&((*ppot1)->stub));
  6349. TWINJUSTTHESETWOOBJECTS_BAIL:
  6350. DestroyStub(&(ptfNew->stub));
  6351. }
  6352. }
  6353. else
  6354. goto TWINJUSTTHESETWOOBJECTS_BAIL;
  6355. }
  6356. }
  6357. else if (bFound1 && bFound2)
  6358. {
  6359. /*
  6360. * Both objects are already present. Are they members of the same twin
  6361. * family?
  6362. */
  6363. if ((*ppot1)->ptfParent == (*ppot2)->ptfParent)
  6364. {
  6365. /* Yes, same twin family. Complain that these twins already exist. */
  6366. TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Object %s is already twinned in folders %s and %s."),
  6367. pcszName,
  6368. DebugGetPathString(hpathFolder1),
  6369. DebugGetPathString(hpathFolder2)));
  6370. tr = TR_DUPLICATE_TWIN;
  6371. }
  6372. else
  6373. {
  6374. /*
  6375. * No, different twin families. Collapse the two families.
  6376. *
  6377. * "That's the way they became the Brady bunch..."
  6378. *
  6379. * *ppot1 and *ppot2 remain valid across this call.
  6380. */
  6381. TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Collapsing separate twin families for object %s in folders %s and %s."),
  6382. pcszName,
  6383. DebugGetPathString(hpathFolder1),
  6384. DebugGetPathString(hpathFolder2)));
  6385. CollapseTwinFamilies((*ppot1)->ptfParent, (*ppot2)->ptfParent);
  6386. tr = TR_SUCCESS;
  6387. }
  6388. }
  6389. else
  6390. {
  6391. PTWINFAMILY ptfParent;
  6392. HNODE hnodeUnused;
  6393. /*
  6394. * Only one of the two objects is present. Add the new object twin
  6395. * to the existing object twin's family.
  6396. */
  6397. if (bFound1)
  6398. {
  6399. /* First object is already a twin. */
  6400. ptfParent = (*ppot1)->ptfParent;
  6401. if (CreateObjectTwinAndAddToList(ptfParent, hpathFolder2,
  6402. hlistNewObjectTwins, ppot2,
  6403. &hnodeUnused))
  6404. {
  6405. TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Adding twin of object %s\\%s to existing twin family including %s\\%s."),
  6406. DebugGetPathString(hpathFolder2),
  6407. pcszName,
  6408. DebugGetPathString(hpathFolder1),
  6409. pcszName));
  6410. tr = TR_SUCCESS;
  6411. }
  6412. }
  6413. else
  6414. {
  6415. /* Second object is already a twin. */
  6416. ptfParent = (*ppot2)->ptfParent;
  6417. if (CreateObjectTwinAndAddToList(ptfParent, hpathFolder1,
  6418. hlistNewObjectTwins, ppot1,
  6419. &hnodeUnused))
  6420. {
  6421. TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Adding twin of object %s\\%s to existing twin family including %s\\%s."),
  6422. DebugGetPathString(hpathFolder1),
  6423. pcszName,
  6424. DebugGetPathString(hpathFolder2),
  6425. pcszName));
  6426. tr = TR_SUCCESS;
  6427. }
  6428. }
  6429. }
  6430. ASSERT((tr != TR_SUCCESS && tr != TR_DUPLICATE_TWIN) ||
  6431. IS_VALID_STRUCT_PTR(*ppot1, COBJECTTWIN) && IS_VALID_STRUCT_PTR(*ppot2, COBJECTTWIN));
  6432. return(tr);
  6433. }
  6434. BOOL CreateTwinFamily(HBRFCASE hbr, LPCTSTR pcszName, PTWINFAMILY *pptf)
  6435. {
  6436. BOOL bResult = FALSE;
  6437. PTWINFAMILY ptfNew;
  6438. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  6439. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  6440. ASSERT(IS_VALID_WRITE_PTR(pptf, PTWINFAMILY));
  6441. /* Try to create a new TWINFAMILY structure. */
  6442. if (AllocateMemory(sizeof(*ptfNew), &ptfNew))
  6443. {
  6444. NEWLIST nl;
  6445. HLIST hlistObjectTwins;
  6446. /* Create a list of object twins for the new twin family. */
  6447. nl.dwFlags = 0;
  6448. if (CreateList(&nl, &hlistObjectTwins))
  6449. {
  6450. HSTRING hsName;
  6451. /* Add the object name to the name string table. */
  6452. if (AddString(pcszName, GetBriefcaseNameStringTable(hbr),
  6453. GetHashBucketIndex, &hsName))
  6454. {
  6455. ARRAYINDEX aiUnused;
  6456. /* Fill in TWINFAMILY fields. */
  6457. InitStub(&(ptfNew->stub), ST_TWINFAMILY);
  6458. ptfNew->hsName = hsName;
  6459. ptfNew->hlistObjectTwins = hlistObjectTwins;
  6460. ptfNew->hbr = hbr;
  6461. MarkTwinFamilyNeverReconciled(ptfNew);
  6462. /* Add the twin family to the briefcase's list of twin families. */
  6463. if (AddPtr(GetBriefcaseTwinFamilyPtrArray(hbr), TwinFamilySortCmp, ptfNew, &aiUnused))
  6464. {
  6465. *pptf = ptfNew;
  6466. bResult = TRUE;
  6467. ASSERT(IS_VALID_STRUCT_PTR(*pptf, CTWINFAMILY));
  6468. }
  6469. else
  6470. {
  6471. DeleteString(hsName);
  6472. CREATETWINFAMILY_BAIL1:
  6473. DestroyList(hlistObjectTwins);
  6474. CREATETWINFAMILY_BAIL2:
  6475. FreeMemory(ptfNew);
  6476. }
  6477. }
  6478. else
  6479. goto CREATETWINFAMILY_BAIL1;
  6480. }
  6481. else
  6482. goto CREATETWINFAMILY_BAIL2;
  6483. }
  6484. return(bResult);
  6485. }
  6486. /*
  6487. ** CollapseTwinFamilies()
  6488. **
  6489. ** Collapses two twin families into one. N.b., this function should only be
  6490. ** called on two twin families with the same object name!
  6491. **
  6492. ** Arguments: ptf1 - pointer to destination twin family
  6493. ** ptf2 - pointer to source twin family
  6494. **
  6495. ** Returns: void
  6496. **
  6497. ** Side Effects: Twin family *ptf2 is destroyed.
  6498. */
  6499. void CollapseTwinFamilies(PTWINFAMILY ptf1, PTWINFAMILY ptf2)
  6500. {
  6501. ASSERT(IS_VALID_STRUCT_PTR(ptf1, CTWINFAMILY));
  6502. ASSERT(IS_VALID_STRUCT_PTR(ptf2, CTWINFAMILY));
  6503. ASSERT(CompareNameStringsByHandle(ptf1->hsName, ptf2->hsName) == CR_EQUAL);
  6504. /* Use the first twin family as the collapsed twin family. */
  6505. /*
  6506. * Change the parent twin family of the object twins in the second twin
  6507. * family to the first twin family.
  6508. */
  6509. EVAL(WalkList(ptf2->hlistObjectTwins, &SetTwinFamilyWalker, ptf1));
  6510. /* Append object list from second twin family on to first. */
  6511. AppendList(ptf1->hlistObjectTwins, ptf2->hlistObjectTwins);
  6512. MarkTwinFamilyNeverReconciled(ptf1);
  6513. /* Wipe out the old twin family. */
  6514. DestroyStub(&(ptf2->stub));
  6515. ASSERT(IS_VALID_STRUCT_PTR(ptf1, CTWINFAMILY));
  6516. }
  6517. BOOL GenerateSpinOffObjectTwin(PVOID pot, PVOID pcsooti)
  6518. {
  6519. BOOL bResult;
  6520. HPATH hpathMatchingFolder;
  6521. HNODE hnodeUnused;
  6522. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6523. ASSERT(IS_VALID_STRUCT_PTR(pcsooti, CSPINOFFOBJECTTWININFO));
  6524. /*
  6525. * Append the generated object twin's subpath to the matching folder twin's
  6526. * base path for subtree twins.
  6527. */
  6528. if (BuildPathForMatchingObjectTwin(
  6529. ((PCSPINOFFOBJECTTWININFO)pcsooti)->pcfp, pot,
  6530. GetBriefcasePathList(((POBJECTTWIN)pot)->ptfParent->hbr),
  6531. &hpathMatchingFolder))
  6532. {
  6533. /*
  6534. * Does this generated object twin's twin family already contain an
  6535. * object twin generated by the other half of the pair of folder twins?
  6536. */
  6537. if (! SearchUnsortedList(((POBJECTTWIN)pot)->ptfParent->hlistObjectTwins,
  6538. &ObjectTwinSearchCmp, hpathMatchingFolder,
  6539. &hnodeUnused))
  6540. {
  6541. /*
  6542. * No. Does the other object twin already exist in a different twin
  6543. * family?
  6544. */
  6545. if (FindObjectTwin(((POBJECTTWIN)pot)->ptfParent->hbr,
  6546. hpathMatchingFolder,
  6547. GetBfcString(((POBJECTTWIN)pot)->ptfParent->hsName),
  6548. &hnodeUnused))
  6549. {
  6550. /* Yes. */
  6551. ASSERT(((PCOBJECTTWIN)GetNodeData(hnodeUnused))->ptfParent != ((POBJECTTWIN)pot)->ptfParent);
  6552. bResult = TRUE;
  6553. }
  6554. else
  6555. {
  6556. POBJECTTWIN potNew;
  6557. /*
  6558. * No. Create a new object twin, and add it to the bookkeeping
  6559. * list of new object twins.
  6560. */
  6561. bResult = CreateObjectTwinAndAddToList(
  6562. ((POBJECTTWIN)pot)->ptfParent, hpathMatchingFolder,
  6563. ((PCSPINOFFOBJECTTWININFO)pcsooti)->hlistNewObjectTwins,
  6564. &potNew, &hnodeUnused);
  6565. }
  6566. }
  6567. else
  6568. bResult = TRUE;
  6569. DeletePath(hpathMatchingFolder);
  6570. }
  6571. else
  6572. bResult = FALSE;
  6573. return(bResult);
  6574. }
  6575. BOOL BuildBradyBunch(PVOID pot, PVOID pcfp)
  6576. {
  6577. BOOL bResult;
  6578. HPATH hpathMatchingFolder;
  6579. HNODE hnodeOther;
  6580. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6581. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  6582. /*
  6583. * Append the generated object twin's subpath to the matching folder twin's
  6584. * base path for subtree twins.
  6585. */
  6586. bResult = BuildPathForMatchingObjectTwin(
  6587. pcfp, pot,
  6588. GetBriefcasePathList(((POBJECTTWIN)pot)->ptfParent->hbr),
  6589. &hpathMatchingFolder);
  6590. if (bResult)
  6591. {
  6592. /*
  6593. * Does this generated object twin's twin family already contain an object
  6594. * twin generated by the other half of the pair of folder twins?
  6595. */
  6596. if (! SearchUnsortedList(((POBJECTTWIN)pot)->ptfParent->hlistObjectTwins,
  6597. &ObjectTwinSearchCmp, hpathMatchingFolder,
  6598. &hnodeOther))
  6599. {
  6600. /*
  6601. * The other object twin should already exist in a different twin family.
  6602. */
  6603. if (EVAL(FindObjectTwin(((POBJECTTWIN)pot)->ptfParent->hbr,
  6604. hpathMatchingFolder,
  6605. GetBfcString(((POBJECTTWIN)pot)->ptfParent->hsName),
  6606. &hnodeOther)))
  6607. {
  6608. PCOBJECTTWIN pcotOther;
  6609. pcotOther = (PCOBJECTTWIN)GetNodeData(hnodeOther);
  6610. if (EVAL(pcotOther->ptfParent != ((POBJECTTWIN)pot)->ptfParent))
  6611. {
  6612. /* It does. Crush them. */
  6613. CollapseTwinFamilies(((POBJECTTWIN)pot)->ptfParent,
  6614. pcotOther->ptfParent);
  6615. TRACE_OUT((TEXT("BuildBradyBunch(): Collapsed separate twin families for object %s\\%s and %s\\%s."),
  6616. DebugGetPathString(((POBJECTTWIN)pot)->hpath),
  6617. GetBfcString(((POBJECTTWIN)pot)->ptfParent->hsName),
  6618. DebugGetPathString(pcotOther->hpath),
  6619. GetBfcString(pcotOther->ptfParent->hsName)));
  6620. }
  6621. }
  6622. }
  6623. DeletePath(hpathMatchingFolder);
  6624. }
  6625. return(bResult);
  6626. }
  6627. BOOL CreateObjectTwinAndAddToList(PTWINFAMILY ptf, HPATH hpathFolder,
  6628. HLIST hlistObjectTwins,
  6629. POBJECTTWIN *ppot, PHNODE phnode)
  6630. {
  6631. BOOL bResult = FALSE;
  6632. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  6633. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  6634. ASSERT(IS_VALID_HANDLE(hlistObjectTwins, LIST));
  6635. ASSERT(IS_VALID_WRITE_PTR(ppot, POBJECTTWIN));
  6636. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  6637. if (CreateObjectTwin(ptf, hpathFolder, ppot))
  6638. {
  6639. if (InsertNodeAtFront(hlistObjectTwins, NULL, *ppot, phnode))
  6640. bResult = TRUE;
  6641. else
  6642. DestroyStub(&((*ppot)->stub));
  6643. }
  6644. return(bResult);
  6645. }
  6646. BOOL CreateListOfGeneratedObjectTwins(PCFOLDERPAIR pcfp,
  6647. PHLIST phlistGeneratedObjectTwins)
  6648. {
  6649. NEWLIST nl;
  6650. BOOL bResult = FALSE;
  6651. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  6652. ASSERT(IS_VALID_WRITE_PTR(phlistGeneratedObjectTwins, HLIST));
  6653. nl.dwFlags = 0;
  6654. if (CreateList(&nl, phlistGeneratedObjectTwins))
  6655. {
  6656. if (EnumGeneratedObjectTwins(pcfp, &InsertNodeAtFrontWalker, *phlistGeneratedObjectTwins))
  6657. bResult = TRUE;
  6658. else
  6659. DestroyList(*phlistGeneratedObjectTwins);
  6660. }
  6661. return(bResult);
  6662. }
  6663. /*
  6664. ** TwinFamilySortCmp()
  6665. **
  6666. ** Pointer comparison function used to sort the global array of twin families.
  6667. **
  6668. ** Arguments: pctf1 - pointer to TWINFAMILY describing first twin family
  6669. ** pctf2 - pointer to TWINFAMILY describing second twin family
  6670. **
  6671. ** Returns:
  6672. **
  6673. ** Side Effects: none
  6674. **
  6675. ** Twin families are sorted by:
  6676. ** 1) name string
  6677. ** 2) pointer value
  6678. */
  6679. COMPARISONRESULT TwinFamilySortCmp(PCVOID pctf1, PCVOID pctf2)
  6680. {
  6681. COMPARISONRESULT cr;
  6682. ASSERT(IS_VALID_STRUCT_PTR(pctf1, CTWINFAMILY));
  6683. ASSERT(IS_VALID_STRUCT_PTR(pctf2, CTWINFAMILY));
  6684. cr = CompareNameStringsByHandle(((PCTWINFAMILY)pctf1)->hsName, ((PCTWINFAMILY)pctf2)->hsName);
  6685. if (cr == CR_EQUAL)
  6686. /* Same name strings. Now sort by pointer value. */
  6687. cr = ComparePointers(pctf1, pctf2);
  6688. return(cr);
  6689. }
  6690. /*
  6691. ** TwinFamilySearchCmp()
  6692. **
  6693. ** Pointer comparison function used to search the global array of twin families
  6694. ** for the first twin family for a given name.
  6695. **
  6696. ** Arguments: pcszName - name string to search for
  6697. ** pctf - pointer to TWINFAMILY to examine
  6698. **
  6699. ** Returns:
  6700. **
  6701. ** Side Effects: none
  6702. **
  6703. ** Twin families are searched by:
  6704. ** 1) name string
  6705. */
  6706. COMPARISONRESULT TwinFamilySearchCmp(PCVOID pcszName, PCVOID pctf)
  6707. {
  6708. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  6709. ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
  6710. return(CompareNameStrings(pcszName, GetBfcString(((PCTWINFAMILY)pctf)->hsName)));
  6711. }
  6712. BOOL ObjectTwinSearchCmp(PCVOID hpath, PCVOID pcot)
  6713. {
  6714. ASSERT(IS_VALID_HANDLE((HPATH)hpath, PATH));
  6715. ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
  6716. return(ComparePaths((HPATH)hpath, ((PCOBJECTTWIN)pcot)->hpath));
  6717. }
  6718. TWINRESULT WriteTwinFamily(HCACHEDFILE hcf, PCTWINFAMILY pctf)
  6719. {
  6720. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  6721. DWORD dwcbTwinFamilyDBHeaderOffset;
  6722. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  6723. ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
  6724. /* Save initial file poisition. */
  6725. dwcbTwinFamilyDBHeaderOffset = GetCachedFilePointerPosition(hcf);
  6726. if (dwcbTwinFamilyDBHeaderOffset != INVALID_SEEK_POSITION)
  6727. {
  6728. TWINFAMILYDBHEADER tfdbh;
  6729. /* Leave space for the twin family's header. */
  6730. ZeroMemory(&tfdbh, sizeof(tfdbh));
  6731. if (WriteToCachedFile(hcf, (PCVOID)&tfdbh, sizeof(tfdbh), NULL))
  6732. {
  6733. BOOL bContinue;
  6734. HNODE hnode;
  6735. LONG lcObjectTwins = 0;
  6736. /* Save twin family's object twins. */
  6737. ASSERT(GetNodeCount(pctf->hlistObjectTwins) >= 2);
  6738. tr = TR_SUCCESS;
  6739. for (bContinue = GetFirstNode(pctf->hlistObjectTwins, &hnode);
  6740. bContinue;
  6741. bContinue = GetNextNode(hnode, &hnode))
  6742. {
  6743. POBJECTTWIN pot;
  6744. pot = (POBJECTTWIN)GetNodeData(hnode);
  6745. tr = WriteObjectTwin(hcf, pot);
  6746. if (tr == TR_SUCCESS)
  6747. {
  6748. ASSERT(lcObjectTwins < LONG_MAX);
  6749. lcObjectTwins++;
  6750. }
  6751. else
  6752. break;
  6753. }
  6754. /* Save twin family's database header. */
  6755. if (tr == TR_SUCCESS)
  6756. {
  6757. ASSERT(lcObjectTwins >= 2);
  6758. tfdbh.dwStubFlags = (pctf->stub.dwFlags & DB_STUB_FLAGS_MASK);
  6759. tfdbh.hsName = pctf->hsName;
  6760. tfdbh.lcObjectTwins = lcObjectTwins;
  6761. tr = WriteDBSegmentHeader(hcf, dwcbTwinFamilyDBHeaderOffset, &tfdbh, sizeof(tfdbh));
  6762. }
  6763. }
  6764. }
  6765. return(tr);
  6766. }
  6767. TWINRESULT WriteObjectTwin(HCACHEDFILE hcf, PCOBJECTTWIN pcot)
  6768. {
  6769. TWINRESULT tr;
  6770. DBOBJECTTWIN dbot;
  6771. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  6772. ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
  6773. /* Set up object twin database structure. */
  6774. dbot.dwStubFlags = (pcot->stub.dwFlags & DB_STUB_FLAGS_MASK);
  6775. dbot.hpath = pcot->hpath;
  6776. dbot.hpath = pcot->hpath;
  6777. dbot.fsLastRec = pcot->fsLastRec;
  6778. /* Save object twin database structure. */
  6779. if (WriteToCachedFile(hcf, (PCVOID)&dbot, sizeof(dbot), NULL))
  6780. tr = TR_SUCCESS;
  6781. else
  6782. tr = TR_BRIEFCASE_WRITE_FAILED;
  6783. return(tr);
  6784. }
  6785. TWINRESULT ReadTwinFamily(HCACHEDFILE hcf, HBRFCASE hbr,
  6786. PCDBVERSION pcdbver,
  6787. HHANDLETRANS hhtFolderTrans,
  6788. HHANDLETRANS hhtNameTrans)
  6789. {
  6790. TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  6791. TWINFAMILYDBHEADER tfdbh;
  6792. DWORD dwcbRead;
  6793. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  6794. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  6795. ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION));
  6796. ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
  6797. ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
  6798. if (ReadFromCachedFile(hcf, &tfdbh, sizeof(tfdbh), &dwcbRead) &&
  6799. dwcbRead == sizeof(tfdbh))
  6800. {
  6801. if (tfdbh.lcObjectTwins >= 2)
  6802. {
  6803. HSTRING hsName;
  6804. if (TranslateHandle(hhtNameTrans, (HGENERIC)(tfdbh.hsName), (PHGENERIC)&hsName))
  6805. {
  6806. PTWINFAMILY ptfParent;
  6807. if (CreateTwinFamily(hbr, GetBfcString(hsName), &ptfParent))
  6808. {
  6809. LONG l;
  6810. CopyTwinFamilyInfo(ptfParent, &tfdbh);
  6811. tr = TR_SUCCESS;
  6812. for (l = tfdbh.lcObjectTwins;
  6813. l > 0 && tr == TR_SUCCESS;
  6814. l--)
  6815. tr = ReadObjectTwin(hcf, pcdbver, ptfParent, hhtFolderTrans);
  6816. if (tr != TR_SUCCESS)
  6817. DestroyStub(&(ptfParent->stub));
  6818. }
  6819. else
  6820. tr = TR_OUT_OF_MEMORY;
  6821. }
  6822. }
  6823. }
  6824. return(tr);
  6825. }
  6826. TWINRESULT ReadObjectTwin(HCACHEDFILE hcf,
  6827. PCDBVERSION pcdbver,
  6828. PTWINFAMILY ptfParent,
  6829. HHANDLETRANS hhtFolderTrans)
  6830. {
  6831. TWINRESULT tr;
  6832. DBOBJECTTWIN dbot;
  6833. DWORD dwcbRead;
  6834. HPATH hpath;
  6835. DWORD dwcbSize;
  6836. COPYOBJECTTWINPROC pfnCopy;
  6837. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  6838. ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION));
  6839. ASSERT(IS_VALID_STRUCT_PTR(ptfParent, CTWINFAMILY));
  6840. ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
  6841. if (HEADER_M8_MINOR_VER == pcdbver->dwMinorVer)
  6842. {
  6843. /* The M8 database does not have the ftModLocal in the FILESTAMP
  6844. ** structure.
  6845. */
  6846. dwcbSize = sizeof(dbot) - sizeof(FILETIME);
  6847. pfnCopy = CopyM8ObjectTwinInfo;
  6848. }
  6849. else
  6850. {
  6851. ASSERT(HEADER_MINOR_VER == pcdbver->dwMinorVer);
  6852. dwcbSize = sizeof(dbot);
  6853. pfnCopy = CopyObjectTwinInfo;
  6854. }
  6855. if ((ReadFromCachedFile(hcf, &dbot, dwcbSize, &dwcbRead) &&
  6856. dwcbRead == dwcbSize) &&
  6857. TranslateHandle(hhtFolderTrans, (HGENERIC)(dbot.hpath), (PHGENERIC)&hpath))
  6858. {
  6859. POBJECTTWIN pot;
  6860. /* Create the new object twin and add it to the twin family. */
  6861. if (CreateObjectTwin(ptfParent, hpath, &pot))
  6862. {
  6863. pfnCopy(pot, &dbot);
  6864. tr = TR_SUCCESS;
  6865. }
  6866. else
  6867. tr = TR_OUT_OF_MEMORY;
  6868. }
  6869. else
  6870. tr = TR_CORRUPT_BRIEFCASE;
  6871. return(tr);
  6872. }
  6873. void CopyTwinFamilyInfo(PTWINFAMILY ptf,
  6874. PCTWINFAMILYDBHEADER pctfdbh)
  6875. {
  6876. ASSERT(IS_VALID_WRITE_PTR(ptf, TWINFAMILY));
  6877. ASSERT(IS_VALID_READ_PTR(pctfdbh, CTWINFAMILYDBHEADER));
  6878. ptf->stub.dwFlags = pctfdbh->dwStubFlags;
  6879. }
  6880. void CopyObjectTwinInfo(POBJECTTWIN pot, PCDBOBJECTTWIN pcdbot)
  6881. {
  6882. ASSERT(IS_VALID_WRITE_PTR(pot, OBJECTTWIN));
  6883. ASSERT(IS_VALID_READ_PTR(pcdbot, CDBOBJECTTWIN));
  6884. pot->stub.dwFlags = pcdbot->dwStubFlags;
  6885. pot->fsLastRec = pcdbot->fsLastRec;
  6886. }
  6887. void CopyM8ObjectTwinInfo(POBJECTTWIN pot, PCDBOBJECTTWIN pcdbot)
  6888. {
  6889. ASSERT(IS_VALID_WRITE_PTR(pot, OBJECTTWIN));
  6890. ASSERT(IS_VALID_READ_PTR(pcdbot, CDBOBJECTTWIN));
  6891. pot->stub.dwFlags = pcdbot->dwStubFlags;
  6892. pot->fsLastRec = pcdbot->fsLastRec;
  6893. /* The pot->fsLastRec.ftModLocal field is invalid, so fill it in */
  6894. if ( !FileTimeToLocalFileTime(&pot->fsLastRec.ftMod, &pot->fsLastRec.ftModLocal) )
  6895. {
  6896. /* Just copy the time if FileTimeToLocalFileTime failed */
  6897. pot->fsLastRec.ftModLocal = pot->fsLastRec.ftMod;
  6898. }
  6899. }
  6900. BOOL DestroyObjectTwinStubWalker(PVOID pot, PVOID pvUnused)
  6901. {
  6902. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6903. ASSERT(! pvUnused);
  6904. /*
  6905. * Set ulcSrcFolderTwins to 0 so UnlinkObjectTwin() succeeds.
  6906. * DestroyStub() will unlink and destroy any new twin family created.
  6907. */
  6908. ((POBJECTTWIN)pot)->ulcSrcFolderTwins = 0;
  6909. DestroyStub(&(((POBJECTTWIN)pot)->stub));
  6910. return(TRUE);
  6911. }
  6912. BOOL MarkObjectTwinNeverReconciledWalker(PVOID pot, PVOID pvUnused)
  6913. {
  6914. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6915. ASSERT(! pvUnused);
  6916. MarkObjectTwinNeverReconciled(pot);
  6917. return(TRUE);
  6918. }
  6919. BOOL LookForSrcFolderTwinsWalker(PVOID pot, PVOID pvUnused)
  6920. {
  6921. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6922. ASSERT(! pvUnused);
  6923. return(! ((POBJECTTWIN)pot)->ulcSrcFolderTwins);
  6924. }
  6925. BOOL IncrementSrcFolderTwinsWalker(PVOID pot, PVOID pvUnused)
  6926. {
  6927. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6928. ASSERT(! pvUnused);
  6929. ASSERT(((POBJECTTWIN)pot)->ulcSrcFolderTwins < ULONG_MAX);
  6930. ((POBJECTTWIN)pot)->ulcSrcFolderTwins++;
  6931. return(TRUE);
  6932. }
  6933. BOOL ClearSrcFolderTwinsWalker(PVOID pot, PVOID pvUnused)
  6934. {
  6935. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6936. ASSERT(! pvUnused);
  6937. ((POBJECTTWIN)pot)->ulcSrcFolderTwins = 0;
  6938. return(TRUE);
  6939. }
  6940. BOOL SetTwinFamilyWalker(PVOID pot, PVOID ptfParent)
  6941. {
  6942. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6943. ASSERT(IS_VALID_STRUCT_PTR(ptfParent, CTWINFAMILY));
  6944. ((POBJECTTWIN)pot)->ptfParent = ptfParent;
  6945. return(TRUE);
  6946. }
  6947. BOOL InsertNodeAtFrontWalker(POBJECTTWIN pot, PVOID hlist)
  6948. {
  6949. HNODE hnodeUnused;
  6950. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  6951. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  6952. return(InsertNodeAtFront(hlist, NULL, pot, &hnodeUnused));
  6953. }
  6954. COMPARISONRESULT CompareNameStrings(LPCTSTR pcszFirst, LPCTSTR pcszSecond)
  6955. {
  6956. ASSERT(IS_VALID_STRING_PTR(pcszFirst, CSTR));
  6957. ASSERT(IS_VALID_STRING_PTR(pcszSecond, CSTR));
  6958. return(MapIntToComparisonResult(lstrcmpi(pcszFirst, pcszSecond)));
  6959. }
  6960. COMPARISONRESULT CompareNameStringsByHandle(HSTRING hsFirst,
  6961. HSTRING hsSecond)
  6962. {
  6963. ASSERT(IS_VALID_HANDLE(hsFirst, BFCSTRING));
  6964. ASSERT(IS_VALID_HANDLE(hsSecond, BFCSTRING));
  6965. return(CompareStringsI(hsFirst, hsSecond));
  6966. }
  6967. TWINRESULT TranslatePATHRESULTToTWINRESULT(PATHRESULT pr)
  6968. {
  6969. TWINRESULT tr;
  6970. switch (pr)
  6971. {
  6972. case PR_SUCCESS:
  6973. tr = TR_SUCCESS;
  6974. break;
  6975. case PR_UNAVAILABLE_VOLUME:
  6976. tr = TR_UNAVAILABLE_VOLUME;
  6977. break;
  6978. case PR_OUT_OF_MEMORY:
  6979. tr = TR_OUT_OF_MEMORY;
  6980. break;
  6981. default:
  6982. ASSERT(pr == PR_INVALID_PATH);
  6983. tr = TR_INVALID_PARAMETER;
  6984. break;
  6985. }
  6986. return(tr);
  6987. }
  6988. BOOL CreateTwinFamilyPtrArray(PHPTRARRAY phpa)
  6989. {
  6990. NEWPTRARRAY npa;
  6991. ASSERT(IS_VALID_WRITE_PTR(phpa, HPTRARRAY));
  6992. /* Try to create a twin family pointer array. */
  6993. npa.aicInitialPtrs = NUM_START_TWIN_FAMILY_PTRS;
  6994. npa.aicAllocGranularity = NUM_TWIN_FAMILY_PTRS_TO_ADD;
  6995. npa.dwFlags = NPA_FL_SORTED_ADD;
  6996. return(CreatePtrArray(&npa, phpa));
  6997. }
  6998. void DestroyTwinFamilyPtrArray(HPTRARRAY hpa)
  6999. {
  7000. ARRAYINDEX aicPtrs;
  7001. ARRAYINDEX ai;
  7002. ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  7003. /* First free all twin families in pointer array. */
  7004. aicPtrs = GetPtrCount(hpa);
  7005. for (ai = 0; ai < aicPtrs; ai++)
  7006. DestroyTwinFamily(GetPtr(hpa, ai));
  7007. /* Now wipe out the pointer array. */
  7008. DestroyPtrArray(hpa);
  7009. }
  7010. HBRFCASE GetTwinBriefcase(HTWIN htwin)
  7011. {
  7012. HBRFCASE hbr;
  7013. ASSERT(IS_VALID_HANDLE(htwin, TWIN));
  7014. switch (((PSTUB)htwin)->st)
  7015. {
  7016. case ST_OBJECTTWIN:
  7017. hbr = ((PCOBJECTTWIN)htwin)->ptfParent->hbr;
  7018. break;
  7019. case ST_TWINFAMILY:
  7020. hbr = ((PCTWINFAMILY)htwin)->hbr;
  7021. break;
  7022. case ST_FOLDERPAIR:
  7023. hbr = ((PCFOLDERPAIR)htwin)->pfpd->hbr;
  7024. break;
  7025. default:
  7026. ERROR_OUT((TEXT("GetTwinBriefcase() called on unrecognized stub type %d."),
  7027. ((PSTUB)htwin)->st));
  7028. hbr = NULL;
  7029. break;
  7030. }
  7031. return(hbr);
  7032. }
  7033. BOOL FindObjectTwinInList(HLIST hlist, HPATH hpath, PHNODE phnode)
  7034. {
  7035. ASSERT(IS_VALID_HANDLE(hlist, LIST));
  7036. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  7037. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  7038. return(SearchUnsortedList(hlist, &ObjectTwinSearchCmp, hpath, phnode));
  7039. }
  7040. /*
  7041. ** EnumTwins()
  7042. **
  7043. ** Enumerates folder twins and twin families in a briefcase.
  7044. **
  7045. ** Arguments:
  7046. **
  7047. ** Returns: TRUE if halted. FALSE if not.
  7048. **
  7049. ** Side Effects: none
  7050. */
  7051. BOOL EnumTwins(HBRFCASE hbr, ENUMTWINSPROC etp, LPARAM lpData,
  7052. PHTWIN phtwinStop)
  7053. {
  7054. HPTRARRAY hpa;
  7055. ARRAYINDEX aicPtrs;
  7056. ARRAYINDEX ai;
  7057. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  7058. ASSERT(IS_VALID_CODE_PTR(etp, ENUMTWINSPROC));
  7059. ASSERT(IS_VALID_WRITE_PTR(phtwinStop, HTWIN));
  7060. /* Enumerate folder pairs. */
  7061. *phtwinStop = NULL;
  7062. hpa = GetBriefcaseFolderPairPtrArray(hbr);
  7063. aicPtrs = GetPtrCount(hpa);
  7064. for (ai = 0; ai < aicPtrs; ai++)
  7065. {
  7066. PCFOLDERPAIR pcfp;
  7067. pcfp = GetPtr(hpa, ai);
  7068. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  7069. if (! (*etp)((HTWIN)pcfp, lpData))
  7070. {
  7071. *phtwinStop = (HTWIN)pcfp;
  7072. break;
  7073. }
  7074. }
  7075. if (! *phtwinStop)
  7076. {
  7077. /* Enumerate twin families. */
  7078. hpa = GetBriefcaseTwinFamilyPtrArray(hbr);
  7079. aicPtrs = GetPtrCount(hpa);
  7080. for (ai = 0; ai < aicPtrs; ai++)
  7081. {
  7082. PCTWINFAMILY pctf;
  7083. pctf = GetPtr(hpa, ai);
  7084. ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
  7085. if (! (*etp)((HTWIN)pctf, lpData))
  7086. {
  7087. *phtwinStop = (HTWIN)pctf;
  7088. break;
  7089. }
  7090. }
  7091. }
  7092. return(*phtwinStop != NULL);
  7093. }
  7094. /*
  7095. ** FindObjectTwin()
  7096. **
  7097. ** Looks for a twin family containing a specified object twin.
  7098. **
  7099. ** Arguments: hpathFolder - folder containing object
  7100. ** pcszName - name of object
  7101. **
  7102. ** Returns: Handle to list node containing pointer to object twin if
  7103. ** found, or NULL if not found.
  7104. **
  7105. ** Side Effects: none
  7106. */
  7107. BOOL FindObjectTwin(HBRFCASE hbr, HPATH hpathFolder,
  7108. LPCTSTR pcszName, PHNODE phnode)
  7109. {
  7110. BOOL bFound = FALSE;
  7111. HPTRARRAY hpaTwinFamilies;
  7112. ARRAYINDEX aiFirst;
  7113. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  7114. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  7115. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  7116. ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
  7117. /* Search for a matching twin family. */
  7118. *phnode = NULL;
  7119. hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
  7120. if (SearchSortedArray(hpaTwinFamilies, &TwinFamilySearchCmp, pcszName,
  7121. &aiFirst))
  7122. {
  7123. ARRAYINDEX aicPtrs;
  7124. ARRAYINDEX ai;
  7125. PTWINFAMILY ptf;
  7126. /*
  7127. * aiFirst holds the index of the first twin family with a common object
  7128. * name matching pcszName.
  7129. */
  7130. /*
  7131. * Now search each of these twin families for a folder matching
  7132. * pcszFolder.
  7133. */
  7134. aicPtrs = GetPtrCount(hpaTwinFamilies);
  7135. ASSERT(aicPtrs > 0);
  7136. ASSERT(aiFirst >= 0);
  7137. ASSERT(aiFirst < aicPtrs);
  7138. for (ai = aiFirst; ai < aicPtrs; ai++)
  7139. {
  7140. ptf = GetPtr(hpaTwinFamilies, ai);
  7141. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7142. /* Is this a twin family of objects of the given name? */
  7143. if (CompareNameStrings(GetBfcString(ptf->hsName), pcszName) == CR_EQUAL)
  7144. {
  7145. bFound = SearchUnsortedList(ptf->hlistObjectTwins,
  7146. &ObjectTwinSearchCmp, hpathFolder,
  7147. phnode);
  7148. if (bFound)
  7149. break;
  7150. }
  7151. else
  7152. /* No. Stop searching. */
  7153. break;
  7154. }
  7155. }
  7156. return(bFound);
  7157. }
  7158. /*
  7159. ** CreateObjectTwin()
  7160. **
  7161. ** Creates a new object twin, and adds it to a twin family.
  7162. **
  7163. ** Arguments: ptf - pointer to parent twin family
  7164. ** hpathFolder - folder of new object twin
  7165. **
  7166. ** Returns: Pointer to new object twin if successful, or NULL if
  7167. ** unsuccessful.
  7168. **
  7169. ** Side Effects: none
  7170. **
  7171. ** N.b., this function does not first check to see if the object twin already
  7172. ** exists in the family.
  7173. */
  7174. BOOL CreateObjectTwin(PTWINFAMILY ptf, HPATH hpathFolder,
  7175. POBJECTTWIN *ppot)
  7176. {
  7177. BOOL bResult = FALSE;
  7178. POBJECTTWIN potNew;
  7179. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7180. ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
  7181. ASSERT(IS_VALID_WRITE_PTR(ppot, POBJECTTWIN));
  7182. /* Create a new OBJECTTWIN structure. */
  7183. if (AllocateMemory(sizeof(*potNew), &potNew))
  7184. {
  7185. if (CopyPath(hpathFolder, GetBriefcasePathList(ptf->hbr), &(potNew->hpath)))
  7186. {
  7187. HNODE hnodeUnused;
  7188. /* Fill in new OBJECTTWIN fields. */
  7189. InitStub(&(potNew->stub), ST_OBJECTTWIN);
  7190. potNew->ptfParent = ptf;
  7191. potNew->ulcSrcFolderTwins = 0;
  7192. MarkObjectTwinNeverReconciled(potNew);
  7193. /* Add the object twin to the twin family's list of object twins. */
  7194. if (InsertNodeAtFront(ptf->hlistObjectTwins, NULL, potNew, &hnodeUnused))
  7195. {
  7196. *ppot = potNew;
  7197. bResult = TRUE;
  7198. ASSERT(IS_VALID_STRUCT_PTR(*ppot, COBJECTTWIN));
  7199. }
  7200. else
  7201. {
  7202. DeletePath(potNew->hpath);
  7203. CREATEOBJECTTWIN_BAIL:
  7204. FreeMemory(potNew);
  7205. }
  7206. }
  7207. else
  7208. goto CREATEOBJECTTWIN_BAIL;
  7209. }
  7210. return(bResult);
  7211. }
  7212. /*
  7213. ** UnlinkObjectTwin()
  7214. **
  7215. ** Unlinks an object twin.
  7216. **
  7217. ** Arguments: pot - pointer to object twin to unlink
  7218. **
  7219. ** Returns: TWINRESULT
  7220. **
  7221. ** Side Effects: none
  7222. */
  7223. TWINRESULT UnlinkObjectTwin(POBJECTTWIN pot)
  7224. {
  7225. TWINRESULT tr;
  7226. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  7227. ASSERT(IsStubFlagClear(&(pot->stub), STUB_FL_UNLINKED));
  7228. TRACE_OUT((TEXT("UnlinkObjectTwin(): Unlinking object twin for folder %s."),
  7229. DebugGetPathString(pot->hpath)));
  7230. /* Is the object twin's twin family being deleted? */
  7231. if (IsStubFlagSet(&(pot->ptfParent->stub), STUB_FL_BEING_DELETED))
  7232. /* Yes. No need to unlink the object twin. */
  7233. tr = TR_SUCCESS;
  7234. else
  7235. {
  7236. /* Are there any folder twin sources left for this object twin? */
  7237. if (! pot->ulcSrcFolderTwins)
  7238. {
  7239. HNODE hnode;
  7240. /*
  7241. * Search the object twin's parent's list of object twins for the
  7242. * object twin to be unlinked.
  7243. */
  7244. if (EVAL(FindObjectTwinInList(pot->ptfParent->hlistObjectTwins, pot->hpath, &hnode)) &&
  7245. EVAL(GetNodeData(hnode) == pot))
  7246. {
  7247. ULONG ulcRemainingObjectTwins;
  7248. /* Unlink the object twin. */
  7249. DeleteNode(hnode);
  7250. SetStubFlag(&(pot->stub), STUB_FL_UNLINKED);
  7251. /*
  7252. * If we have just unlinked the second last object twin in a twin
  7253. * family, destroy the twin family.
  7254. */
  7255. ulcRemainingObjectTwins = GetNodeCount(pot->ptfParent->hlistObjectTwins);
  7256. if (ulcRemainingObjectTwins < 2)
  7257. {
  7258. /* It's the end of the family line. */
  7259. tr = DestroyStub(&(pot->ptfParent->stub));
  7260. if (ulcRemainingObjectTwins == 1 &&
  7261. tr == TR_HAS_FOLDER_TWIN_SRC)
  7262. tr = TR_SUCCESS;
  7263. }
  7264. else
  7265. tr = TR_SUCCESS;
  7266. }
  7267. else
  7268. tr = TR_INVALID_PARAMETER;
  7269. ASSERT(tr == TR_SUCCESS);
  7270. }
  7271. else
  7272. tr = TR_HAS_FOLDER_TWIN_SRC;
  7273. }
  7274. return(tr);
  7275. }
  7276. /*
  7277. ** DestroyObjectTwin()
  7278. **
  7279. ** Destroys an object twin.
  7280. **
  7281. ** Arguments: pot - pointer to object twin to destroy
  7282. **
  7283. ** Returns: void
  7284. **
  7285. ** Side Effects: none
  7286. */
  7287. void DestroyObjectTwin(POBJECTTWIN pot)
  7288. {
  7289. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  7290. TRACE_OUT((TEXT("DestroyObjectTwin(): Destroying object twin for folder %s."),
  7291. DebugGetPathString(pot->hpath)));
  7292. DeletePath(pot->hpath);
  7293. FreeMemory(pot);
  7294. }
  7295. /*
  7296. ** UnlinkTwinFamily()
  7297. **
  7298. ** Unlinks a twin family.
  7299. **
  7300. ** Arguments: ptf - pointer to twin family to unlink
  7301. **
  7302. ** Returns: TWINRESULT
  7303. **
  7304. ** Side Effects: none
  7305. */
  7306. TWINRESULT UnlinkTwinFamily(PTWINFAMILY ptf)
  7307. {
  7308. TWINRESULT tr;
  7309. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7310. ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED));
  7311. ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_BEING_DELETED));
  7312. /*
  7313. * A twin family containing object twins generated by folder twins may not
  7314. * be deleted, since those object twins may not be directly deleted.
  7315. */
  7316. if (WalkList(ptf->hlistObjectTwins, &LookForSrcFolderTwinsWalker, NULL))
  7317. {
  7318. HPTRARRAY hpaTwinFamilies;
  7319. ARRAYINDEX aiUnlink;
  7320. TRACE_OUT((TEXT("UnlinkTwinFamily(): Unlinking twin family for object %s."),
  7321. GetBfcString(ptf->hsName)));
  7322. /* Search for the twin family to be unlinked. */
  7323. hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(ptf->hbr);
  7324. if (EVAL(SearchSortedArray(hpaTwinFamilies, &TwinFamilySortCmp, ptf,
  7325. &aiUnlink)))
  7326. {
  7327. /* Unlink the twin family. */
  7328. ASSERT(GetPtr(hpaTwinFamilies, aiUnlink) == ptf);
  7329. DeletePtr(hpaTwinFamilies, aiUnlink);
  7330. SetStubFlag(&(ptf->stub), STUB_FL_UNLINKED);
  7331. }
  7332. tr = TR_SUCCESS;
  7333. }
  7334. else
  7335. tr = TR_HAS_FOLDER_TWIN_SRC;
  7336. return(tr);
  7337. }
  7338. /*
  7339. ** DestroyTwinFamily()
  7340. **
  7341. ** Destroys a twin family.
  7342. **
  7343. ** Arguments: ptf - pointer to twin family to destroy
  7344. **
  7345. ** Returns: void
  7346. **
  7347. ** Side Effects: none
  7348. */
  7349. void DestroyTwinFamily(PTWINFAMILY ptf)
  7350. {
  7351. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7352. ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_BEING_DELETED));
  7353. TRACE_OUT((TEXT("DestroyTwinFamily(): Destroying twin family for object %s."),
  7354. GetBfcString(ptf->hsName)));
  7355. SetStubFlag(&(ptf->stub), STUB_FL_BEING_DELETED);
  7356. /*
  7357. * Destroy the object twins in the family one by one. Be careful not to use
  7358. * an object twin after it has been destroyed.
  7359. */
  7360. EVAL(WalkList(ptf->hlistObjectTwins, &DestroyObjectTwinStubWalker, NULL));
  7361. /* Destroy TWINFAMILY fields. */
  7362. DestroyList(ptf->hlistObjectTwins);
  7363. DeleteString(ptf->hsName);
  7364. FreeMemory(ptf);
  7365. }
  7366. /*
  7367. ** MarkTwinFamilyNeverReconciled()
  7368. **
  7369. ** Marks a twin family as never reconciled.
  7370. **
  7371. ** Arguments: ptf - pointer to twin family to be marked never reconciled
  7372. **
  7373. ** Returns: void
  7374. **
  7375. ** Side Effects: Clears the twin family's last reconciliation time stamp.
  7376. ** Marks all the object twins in the family never reconciled.
  7377. */
  7378. void MarkTwinFamilyNeverReconciled(PTWINFAMILY ptf)
  7379. {
  7380. /*
  7381. * If we're being called from CreateTwinFamily(), the fields we're about to
  7382. * set may currently be invalid. Don't fully verify the TWINFAMILY
  7383. * structure.
  7384. */
  7385. ASSERT(IS_VALID_WRITE_PTR(ptf, TWINFAMILY));
  7386. /* Mark all object twins in twin family as never reconciled. */
  7387. EVAL(WalkList(ptf->hlistObjectTwins, MarkObjectTwinNeverReconciledWalker, NULL));
  7388. }
  7389. void MarkObjectTwinNeverReconciled(PVOID pot)
  7390. {
  7391. /*
  7392. * If we're being called from CreateObjectTwin(), the fields we're about to
  7393. * set may currently be invalid. Don't fully verify the OBJECTTWIN
  7394. * structure.
  7395. */
  7396. ASSERT(IS_VALID_WRITE_PTR((PCOBJECTTWIN)pot, COBJECTTWIN));
  7397. ASSERT(IsStubFlagClear(&(((PCOBJECTTWIN)pot)->stub), STUB_FL_NOT_RECONCILED));
  7398. ZeroMemory(&(((POBJECTTWIN)pot)->fsLastRec),
  7399. sizeof(((POBJECTTWIN)pot)->fsLastRec));
  7400. ((POBJECTTWIN)pot)->fsLastRec.fscond = FS_COND_UNAVAILABLE;
  7401. }
  7402. void MarkTwinFamilyDeletionPending(PTWINFAMILY ptf)
  7403. {
  7404. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7405. if (IsStubFlagClear(&(ptf->stub), STUB_FL_DELETION_PENDING))
  7406. TRACE_OUT((TEXT("MarkTwinFamilyDeletionPending(): Deletion now pending for twin family for %s."),
  7407. GetBfcString(ptf->hsName)));
  7408. SetStubFlag(&(ptf->stub), STUB_FL_DELETION_PENDING);
  7409. }
  7410. void UnmarkTwinFamilyDeletionPending(PTWINFAMILY ptf)
  7411. {
  7412. BOOL bContinue;
  7413. HNODE hnode;
  7414. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7415. if (IsStubFlagSet(&(ptf->stub), STUB_FL_DELETION_PENDING))
  7416. {
  7417. for (bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnode);
  7418. bContinue;
  7419. bContinue = GetNextNode(hnode, &hnode))
  7420. {
  7421. POBJECTTWIN pot;
  7422. pot = GetNodeData(hnode);
  7423. ClearStubFlag(&(pot->stub), STUB_FL_KEEP);
  7424. }
  7425. ClearStubFlag(&(ptf->stub), STUB_FL_DELETION_PENDING);
  7426. TRACE_OUT((TEXT("UnmarkTwinFamilyDeletionPending(): Deletion no longer pending for twin family for %s."),
  7427. GetBfcString(ptf->hsName)));
  7428. }
  7429. }
  7430. BOOL IsTwinFamilyDeletionPending(PCTWINFAMILY pctf)
  7431. {
  7432. ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
  7433. return(IsStubFlagSet(&(pctf->stub), STUB_FL_DELETION_PENDING));
  7434. }
  7435. void ClearTwinFamilySrcFolderTwinCount(PTWINFAMILY ptf)
  7436. {
  7437. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7438. EVAL(WalkList(ptf->hlistObjectTwins, &ClearSrcFolderTwinsWalker, NULL));
  7439. }
  7440. BOOL EnumObjectTwins(HBRFCASE hbr,
  7441. ENUMGENERATEDOBJECTTWINSPROC egotp,
  7442. PVOID pvRefData)
  7443. {
  7444. BOOL bResult = TRUE;
  7445. HPTRARRAY hpaTwinFamilies;
  7446. ARRAYINDEX aicPtrs;
  7447. ARRAYINDEX ai;
  7448. /* pvRefData may be any value. */
  7449. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  7450. ASSERT(IS_VALID_CODE_PTR(egotp, ENUMGENERATEDOBJECTTWINPROC));
  7451. /* Walk the array of twin families. */
  7452. hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
  7453. aicPtrs = GetPtrCount(hpaTwinFamilies);
  7454. ai = 0;
  7455. while (ai < aicPtrs)
  7456. {
  7457. PTWINFAMILY ptf;
  7458. BOOL bContinue;
  7459. HNODE hnodePrev;
  7460. ptf = GetPtr(hpaTwinFamilies, ai);
  7461. ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  7462. ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED));
  7463. /* Lock the twin family so it isn't deleted out from under us. */
  7464. LockStub(&(ptf->stub));
  7465. /*
  7466. * Walk each twin family's list of object twins, calling the callback
  7467. * function with each object twin.
  7468. */
  7469. bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnodePrev);
  7470. while (bContinue)
  7471. {
  7472. HNODE hnodeNext;
  7473. POBJECTTWIN pot;
  7474. bContinue = GetNextNode(hnodePrev, &hnodeNext);
  7475. pot = (POBJECTTWIN)GetNodeData(hnodePrev);
  7476. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  7477. bResult = (*egotp)(pot, pvRefData);
  7478. if (! bResult)
  7479. break;
  7480. hnodePrev = hnodeNext;
  7481. }
  7482. /* Was the twin family unlinked? */
  7483. if (IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED))
  7484. /* No. */
  7485. ai++;
  7486. else
  7487. {
  7488. /* Yes. */
  7489. aicPtrs--;
  7490. ASSERT(aicPtrs == GetPtrCount(hpaTwinFamilies));
  7491. TRACE_OUT((TEXT("EnumObjectTwins(): Twin family for object %s unlinked by callback."),
  7492. GetBfcString(ptf->hsName)));
  7493. }
  7494. UnlockStub(&(ptf->stub));
  7495. if (! bResult)
  7496. break;
  7497. }
  7498. return(bResult);
  7499. }
  7500. /*
  7501. ** ApplyNewFolderTwinsToTwinFamilies()
  7502. **
  7503. **
  7504. **
  7505. ** Arguments:
  7506. **
  7507. ** Returns:
  7508. **
  7509. ** Side Effects: none
  7510. **
  7511. ** If FALSE is returned, the array of twin families is in the same state it was
  7512. ** in before ApplyNewFolderTwinsToTwinFamilies() was called. No clean-up is
  7513. ** required by the caller in case of failure.
  7514. **
  7515. ** This function collapses a pair of separate twin families when an object twin
  7516. ** in one twin family intersects one of the folder twins in the pair of new
  7517. ** folder twins and an object twin in the other twin family intersects the
  7518. ** other folder twin in the pair of new folder twins.
  7519. **
  7520. ** This function generates a spinoff object twin when an existing object twin
  7521. ** intersects one of the folder twins in the pair of new folder twins, and no
  7522. ** corresponding object twin for the other folder twin in the pair of new
  7523. ** folder twins exists in the briefcase. The spinoff object twin is added to
  7524. ** the generating object twin's twin family. A spinoff object twins cannot
  7525. ** cause any existing pairs of twin families to be collapsed because the
  7526. ** spinoff object twin did not previously exist in a twin family.
  7527. **
  7528. */
  7529. BOOL ApplyNewFolderTwinsToTwinFamilies(PCFOLDERPAIR pcfp)
  7530. {
  7531. BOOL bResult = FALSE;
  7532. HLIST hlistGeneratedObjectTwins;
  7533. ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
  7534. /*
  7535. * Create lists to contain existing object twins generated by both folder
  7536. * twins.
  7537. */
  7538. if (CreateListOfGeneratedObjectTwins(pcfp, &hlistGeneratedObjectTwins))
  7539. {
  7540. HLIST hlistOtherGeneratedObjectTwins;
  7541. if (CreateListOfGeneratedObjectTwins(pcfp->pfpOther,
  7542. &hlistOtherGeneratedObjectTwins))
  7543. {
  7544. NEWLIST nl;
  7545. HLIST hlistNewObjectTwins;
  7546. /* Create list to contain spin-off object twins. */
  7547. nl.dwFlags = 0;
  7548. if (CreateList(&nl, &hlistNewObjectTwins))
  7549. {
  7550. SPINOFFOBJECTTWININFO sooti;
  7551. /*
  7552. * Generate list of new object twins generated by new folder twins
  7553. * to seed ApplyNewObjectTwinToFolderTwins().
  7554. */
  7555. sooti.pcfp = pcfp;
  7556. sooti.hlistNewObjectTwins = hlistNewObjectTwins;
  7557. if (WalkList(hlistGeneratedObjectTwins, &GenerateSpinOffObjectTwin,
  7558. &sooti))
  7559. {
  7560. sooti.pcfp = pcfp->pfpOther;
  7561. ASSERT(sooti.hlistNewObjectTwins == hlistNewObjectTwins);
  7562. if (WalkList(hlistOtherGeneratedObjectTwins,
  7563. &GenerateSpinOffObjectTwin, &sooti))
  7564. {
  7565. /*
  7566. * ApplyNewObjectTwinsToFolderTwins() sets ulcSrcFolderTwins
  7567. * for all object twins in hlistNewObjectTwins.
  7568. */
  7569. if (ApplyNewObjectTwinsToFolderTwins(hlistNewObjectTwins))
  7570. {
  7571. /*
  7572. * Collapse separate twin families joined by new folder
  7573. * twin.
  7574. */
  7575. EVAL(WalkList(hlistGeneratedObjectTwins, &BuildBradyBunch,
  7576. (PVOID)pcfp));
  7577. /*
  7578. * We don't need to call BuildBradyBunch() for
  7579. * pcfp->pfpOther and hlistOtherGeneratedObjectTwins since
  7580. * one twin family from each collapsed pair of twin
  7581. * families must come from each list of generated object
  7582. * twins.
  7583. */
  7584. /*
  7585. * Increment source folder twin count for all pre-existing
  7586. * object twins generated by the new folder twins.
  7587. */
  7588. EVAL(WalkList(hlistGeneratedObjectTwins,
  7589. &IncrementSrcFolderTwinsWalker, NULL));
  7590. EVAL(WalkList(hlistOtherGeneratedObjectTwins,
  7591. &IncrementSrcFolderTwinsWalker, NULL));
  7592. bResult = TRUE;
  7593. }
  7594. }
  7595. }
  7596. /* Wipe out any new object twins on failure. */
  7597. if (! bResult)
  7598. EVAL(WalkList(hlistNewObjectTwins, &DestroyObjectTwinStubWalker,
  7599. NULL));
  7600. DestroyList(hlistNewObjectTwins);
  7601. }
  7602. DestroyList(hlistOtherGeneratedObjectTwins);
  7603. }
  7604. DestroyList(hlistGeneratedObjectTwins);
  7605. }
  7606. return(bResult);
  7607. }
  7608. TWINRESULT TransplantObjectTwin(POBJECTTWIN pot,
  7609. HPATH hpathOldFolder,
  7610. HPATH hpathNewFolder)
  7611. {
  7612. TWINRESULT tr;
  7613. ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  7614. ASSERT(IS_VALID_HANDLE(hpathOldFolder, PATH));
  7615. ASSERT(IS_VALID_HANDLE(hpathNewFolder, PATH));
  7616. /* Is this object twin rooted in the renamed folder's subtree? */
  7617. if (IsPathPrefix(pot->hpath, hpathOldFolder))
  7618. {
  7619. TCHAR rgchPathSuffix[MAX_PATH_LEN];
  7620. LPCTSTR pcszSubPath;
  7621. HPATH hpathNew;
  7622. /* Yes. Change the object twin's root. */
  7623. pcszSubPath = FindChildPathSuffix(hpathOldFolder, pot->hpath,
  7624. rgchPathSuffix);
  7625. if (AddChildPath(GetBriefcasePathList(pot->ptfParent->hbr),
  7626. hpathNewFolder, pcszSubPath, &hpathNew))
  7627. {
  7628. TRACE_OUT((TEXT("TransplantObjectTwin(): Transplanted object twin %s\\%s to %s\\%s."),
  7629. DebugGetPathString(pot->hpath),
  7630. GetBfcString(pot->ptfParent->hsName),
  7631. DebugGetPathString(hpathNew),
  7632. GetBfcString(pot->ptfParent->hsName)));
  7633. DeletePath(pot->hpath);
  7634. pot->hpath = hpathNew;
  7635. tr = TR_SUCCESS;
  7636. }
  7637. else
  7638. tr = TR_OUT_OF_MEMORY;
  7639. }
  7640. else
  7641. tr = TR_SUCCESS;
  7642. return(tr);
  7643. }
  7644. BOOL IsFolderObjectTwinName(LPCTSTR pcszName)
  7645. {
  7646. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  7647. return(! *pcszName);
  7648. }
  7649. TWINRESULT WriteTwinFamilies(HCACHEDFILE hcf, HPTRARRAY hpaTwinFamilies)
  7650. {
  7651. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  7652. DWORD dwcbTwinFamiliesDBHeaderOffset;
  7653. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  7654. ASSERT(IS_VALID_HANDLE(hpaTwinFamilies, PTRARRAY));
  7655. /* Save initial file poisition. */
  7656. dwcbTwinFamiliesDBHeaderOffset = GetCachedFilePointerPosition(hcf);
  7657. if (dwcbTwinFamiliesDBHeaderOffset != INVALID_SEEK_POSITION)
  7658. {
  7659. TWINFAMILIESDBHEADER tfdbh;
  7660. /* Leave space for the twin families' header. */
  7661. ZeroMemory(&tfdbh, sizeof(tfdbh));
  7662. if (WriteToCachedFile(hcf, (PCVOID)&tfdbh, sizeof(tfdbh), NULL))
  7663. {
  7664. ARRAYINDEX aicPtrs;
  7665. ARRAYINDEX ai;
  7666. tr = TR_SUCCESS;
  7667. aicPtrs = GetPtrCount(hpaTwinFamilies);
  7668. for (ai = 0;
  7669. ai < aicPtrs && tr == TR_SUCCESS;
  7670. ai++)
  7671. tr = WriteTwinFamily(hcf, GetPtr(hpaTwinFamilies, ai));
  7672. if (tr == TR_SUCCESS)
  7673. {
  7674. /* Save twin families' header. */
  7675. tfdbh.lcTwinFamilies = aicPtrs;
  7676. tr = WriteDBSegmentHeader(hcf, dwcbTwinFamiliesDBHeaderOffset,
  7677. &tfdbh, sizeof(tfdbh));
  7678. if (tr == TR_SUCCESS)
  7679. TRACE_OUT((TEXT("WriteTwinFamilies(): Wrote %ld twin families."),
  7680. tfdbh.lcTwinFamilies));
  7681. }
  7682. }
  7683. }
  7684. return(tr);
  7685. }
  7686. TWINRESULT ReadTwinFamilies(HCACHEDFILE hcf, HBRFCASE hbr,
  7687. PCDBVERSION pcdbver,
  7688. HHANDLETRANS hhtFolderTrans,
  7689. HHANDLETRANS hhtNameTrans)
  7690. {
  7691. TWINRESULT tr;
  7692. TWINFAMILIESDBHEADER tfdbh;
  7693. DWORD dwcbRead;
  7694. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  7695. ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
  7696. ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION));
  7697. ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
  7698. ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
  7699. if (ReadFromCachedFile(hcf, &tfdbh, sizeof(tfdbh), &dwcbRead) &&
  7700. dwcbRead == sizeof(tfdbh))
  7701. {
  7702. LONG l;
  7703. tr = TR_SUCCESS;
  7704. TRACE_OUT((TEXT("ReadTwinFamilies(): Reading %ld twin families."),
  7705. tfdbh.lcTwinFamilies));
  7706. for (l = 0;
  7707. l < tfdbh.lcTwinFamilies && tr == TR_SUCCESS;
  7708. l++)
  7709. tr = ReadTwinFamily(hcf, hbr, pcdbver, hhtFolderTrans, hhtNameTrans);
  7710. }
  7711. else
  7712. tr = TR_CORRUPT_BRIEFCASE;
  7713. return(tr);
  7714. }
  7715. COMPARISONRESULT ComparePathStringsByHandle(HSTRING hsFirst,
  7716. HSTRING hsSecond)
  7717. {
  7718. ASSERT(IS_VALID_HANDLE(hsFirst, BFCSTRING));
  7719. ASSERT(IS_VALID_HANDLE(hsSecond, BFCSTRING));
  7720. return(CompareStringsI(hsFirst, hsSecond));
  7721. }
  7722. COMPARISONRESULT MyLStrCmpNI(LPCTSTR pcsz1, LPCTSTR pcsz2, int ncbLen)
  7723. {
  7724. int n = 0;
  7725. ASSERT(IS_VALID_STRING_PTR(pcsz1, CSTR));
  7726. ASSERT(IS_VALID_STRING_PTR(pcsz2, CSTR));
  7727. ASSERT(ncbLen >= 0);
  7728. while (ncbLen > 0 &&
  7729. ! (n = PtrToUlong(CharLower((LPTSTR)(ULONG)*pcsz1))
  7730. - PtrToUlong(CharLower((LPTSTR)(ULONG)*pcsz2))) &&
  7731. *pcsz1)
  7732. {
  7733. pcsz1++;
  7734. pcsz2++;
  7735. ncbLen--;
  7736. }
  7737. return(MapIntToComparisonResult(n));
  7738. }
  7739. /*
  7740. ** ComposePath()
  7741. **
  7742. ** Composes a path string given a folder and a filename.
  7743. **
  7744. ** Arguments: pszBuffer - path string that is created
  7745. ** pcszFolder - path string of the folder
  7746. ** pcszName - path to append
  7747. **
  7748. ** Returns: void
  7749. **
  7750. ** Side Effects: none
  7751. **
  7752. ** N.b., truncates path to MAX_PATH_LEN bytes in length.
  7753. */
  7754. void ComposePath(LPTSTR pszBuffer, LPCTSTR pcszFolder, LPCTSTR pcszName)
  7755. {
  7756. ASSERT(IS_VALID_STRING_PTR(pszBuffer, STR));
  7757. ASSERT(IS_VALID_STRING_PTR(pcszFolder, CSTR));
  7758. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  7759. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszBuffer, STR, MAX_PATH_LEN));
  7760. MyLStrCpyN(pszBuffer, pcszFolder, MAX_PATH_LEN);
  7761. CatPath(pszBuffer, pcszName);
  7762. ASSERT(IS_VALID_STRING_PTR(pszBuffer, STR));
  7763. }
  7764. /*
  7765. ** ExtractFileName()
  7766. **
  7767. ** Extracts the file name from a path name.
  7768. **
  7769. ** Arguments: pcszPathName - path string from which to extract file name
  7770. **
  7771. ** Returns: Pointer to file name in path string.
  7772. **
  7773. ** Side Effects: none
  7774. */
  7775. LPCTSTR ExtractFileName(LPCTSTR pcszPathName)
  7776. {
  7777. LPCTSTR pcszLastComponent;
  7778. LPCTSTR pcsz;
  7779. ASSERT(IS_VALID_STRING_PTR(pcszPathName, CSTR));
  7780. for (pcszLastComponent = pcsz = pcszPathName;
  7781. *pcsz;
  7782. pcsz = CharNext(pcsz))
  7783. {
  7784. if (IS_SLASH(*pcsz) || *pcsz == COLON)
  7785. pcszLastComponent = CharNext(pcsz);
  7786. }
  7787. ASSERT(IS_VALID_STRING_PTR(pcszLastComponent, CSTR));
  7788. return(pcszLastComponent);
  7789. }
  7790. /*
  7791. ** ExtractExtension()
  7792. **
  7793. ** Extracts the extension from a file.
  7794. **
  7795. ** Arguments: pcszName - name whose extension is to be extracted
  7796. **
  7797. ** Returns: If the name contains an extension, a pointer to the period at
  7798. ** the beginning of the extension is returned. If the name has
  7799. ** no extension, a pointer to the name's null terminator is
  7800. ** returned.
  7801. **
  7802. ** Side Effects: none
  7803. */
  7804. LPCTSTR ExtractExtension(LPCTSTR pcszName)
  7805. {
  7806. LPCTSTR pcszLastPeriod;
  7807. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  7808. /* Make sure we have an isolated file name. */
  7809. pcszName = ExtractFileName(pcszName);
  7810. pcszLastPeriod = NULL;
  7811. while (*pcszName)
  7812. {
  7813. if (*pcszName == PERIOD)
  7814. pcszLastPeriod = pcszName;
  7815. pcszName = CharNext(pcszName);
  7816. }
  7817. if (! pcszLastPeriod)
  7818. {
  7819. /* Point at null terminator. */
  7820. pcszLastPeriod = pcszName;
  7821. ASSERT(! *pcszLastPeriod);
  7822. }
  7823. else
  7824. /* Point at period at beginning of extension. */
  7825. ASSERT(*pcszLastPeriod == PERIOD);
  7826. ASSERT(IS_VALID_STRING_PTR(pcszLastPeriod, CSTR));
  7827. return(pcszLastPeriod);
  7828. }
  7829. /*
  7830. ** GetHashBucketIndex()
  7831. **
  7832. ** Calculates the hash bucket index for a string.
  7833. **
  7834. ** Arguments: pcsz - pointer to string whose hash bucket index is to be
  7835. ** calculated
  7836. ** hbc - number of hash buckets in string table
  7837. **
  7838. ** Returns: Hash bucket index for string.
  7839. **
  7840. ** Side Effects: none
  7841. **
  7842. ** The hashing function used is the sum of the byte values in the string modulo
  7843. ** the number of buckets in the hash table.
  7844. */
  7845. HASHBUCKETCOUNT GetHashBucketIndex(LPCTSTR pcsz, HASHBUCKETCOUNT hbc)
  7846. {
  7847. ULONG ulSum;
  7848. ASSERT(IS_VALID_STRING_PTR(pcsz, CSTR));
  7849. ASSERT(hbc > 0);
  7850. /* Don't worry about overflow here. */
  7851. for (ulSum = 0; *pcsz; pcsz++)
  7852. ulSum += *pcsz;
  7853. return((HASHBUCKETCOUNT)(ulSum % hbc));
  7854. }
  7855. /*
  7856. ** CopyLinkInfo()
  7857. **
  7858. ** Copies LinkInfo into local memory.
  7859. **
  7860. ** Arguments: pcliSrc - source LinkInfo
  7861. ** ppliDest - pointer to PLINKINFO to be filled in with pointer
  7862. ** to local copy
  7863. **
  7864. ** Returns: TRUE if successful. FALSE if not.
  7865. **
  7866. ** Side Effects: none
  7867. */
  7868. BOOL CopyLinkInfo(PCLINKINFO pcliSrc, PLINKINFO *ppliDest)
  7869. {
  7870. BOOL bResult;
  7871. DWORD dwcbSize;
  7872. ASSERT(IS_VALID_STRUCT_PTR(pcliSrc, CLINKINFO));
  7873. ASSERT(IS_VALID_WRITE_PTR(ppliDest, PLINKINFO));
  7874. dwcbSize = *(PDWORD)pcliSrc;
  7875. bResult = AllocateMemory(dwcbSize, ppliDest);
  7876. if (bResult)
  7877. CopyMemory(*ppliDest, pcliSrc, dwcbSize);
  7878. ASSERT(! bResult ||
  7879. IS_VALID_STRUCT_PTR(*ppliDest, CLINKINFO));
  7880. return(bResult);
  7881. }
  7882. /* Constants
  7883. ************/
  7884. /* VOLUMELIST PTRARRAY allocation parameters */
  7885. #define NUM_START_VOLUMES (16)
  7886. #define NUM_VOLUMES_TO_ADD (16)
  7887. /* VOLUMELIST string table allocation parameters */
  7888. #define NUM_VOLUME_HASH_BUCKETS (31)
  7889. /* Types
  7890. ********/
  7891. /* volume list */
  7892. typedef struct _volumelist
  7893. {
  7894. /* array of pointers to VOLUMEs */
  7895. HPTRARRAY hpa;
  7896. /* table of volume root path strings */
  7897. HSTRINGTABLE hst;
  7898. /* flags from RESOLVELINKINFOINFLAGS */
  7899. DWORD dwFlags;
  7900. /*
  7901. * handle to parent window, only valid if RLI_IFL_ALLOW_UI is set in dwFlags
  7902. * field
  7903. */
  7904. HWND hwndOwner;
  7905. }
  7906. VOLUMELIST;
  7907. DECLARE_STANDARD_TYPES(VOLUMELIST);
  7908. /* VOLUME flags */
  7909. typedef enum _volumeflags
  7910. {
  7911. /* The volume root path string indicated by hsRootPath is valid. */
  7912. VOLUME_FL_ROOT_PATH_VALID = 0x0001,
  7913. /*
  7914. * The net resource should be disconnected by calling DisconnectLinkInfo()
  7915. * when finished.
  7916. */
  7917. VOLUME_FL_DISCONNECT = 0x0002,
  7918. /* Any cached volume information should be verified before use. */
  7919. VOLUME_FL_VERIFY_VOLUME = 0x0004,
  7920. /* flag combinations */
  7921. ALL_VOLUME_FLAGS = (VOLUME_FL_ROOT_PATH_VALID |
  7922. VOLUME_FL_DISCONNECT |
  7923. VOLUME_FL_VERIFY_VOLUME)
  7924. }
  7925. VOLUMEFLAGS;
  7926. /* VOLUME states */
  7927. typedef enum _volumestate
  7928. {
  7929. VS_UNKNOWN,
  7930. VS_AVAILABLE,
  7931. VS_UNAVAILABLE
  7932. }
  7933. VOLUMESTATE;
  7934. DECLARE_STANDARD_TYPES(VOLUMESTATE);
  7935. /* volume structure */
  7936. typedef struct _volume
  7937. {
  7938. /* reference count */
  7939. ULONG ulcLock;
  7940. /* bit mask of flags from VOLUMEFLAGS */
  7941. DWORD dwFlags;
  7942. /* volume state */
  7943. VOLUMESTATE vs;
  7944. /* pointer to LinkInfo structure indentifying volume */
  7945. PLINKINFO pli;
  7946. /*
  7947. * handle to volume root path string, only valid if
  7948. * VOLUME_FL_ROOT_PATH_VALID is set in dwFlags field
  7949. */
  7950. HSTRING hsRootPath;
  7951. /* pointer to parent volume list */
  7952. PVOLUMELIST pvlParent;
  7953. }
  7954. VOLUME;
  7955. DECLARE_STANDARD_TYPES(VOLUME);
  7956. /* database volume list header */
  7957. typedef struct _dbvolumelistheader
  7958. {
  7959. /* number of volumes in list */
  7960. LONG lcVolumes;
  7961. /* length of longest LinkInfo structure in volume list in bytes */
  7962. UINT ucbMaxLinkInfoLen;
  7963. }
  7964. DBVOLUMELISTHEADER;
  7965. DECLARE_STANDARD_TYPES(DBVOLUMELISTHEADER);
  7966. /* database volume structure */
  7967. typedef struct _dbvolume
  7968. {
  7969. /* old handle to volume */
  7970. HVOLUME hvol;
  7971. /* old LinkInfo structure follows */
  7972. /* first DWORD of LinkInfo structure is total size in bytes */
  7973. }
  7974. DBVOLUME;
  7975. DECLARE_STANDARD_TYPES(DBVOLUME);
  7976. /* Module Prototypes
  7977. ********************/
  7978. COMPARISONRESULT VolumeSortCmp(PCVOID, PCVOID);
  7979. COMPARISONRESULT VolumeSearchCmp(PCVOID, PCVOID);
  7980. BOOL SearchForVolumeByRootPathCmp(PCVOID, PCVOID);
  7981. BOOL UnifyVolume(PVOLUMELIST, PLINKINFO, PVOLUME *);
  7982. BOOL CreateVolume(PVOLUMELIST, PLINKINFO, PVOLUME *);
  7983. void UnlinkVolume(PCVOLUME);
  7984. BOOL DisconnectVolume(PVOLUME);
  7985. void DestroyVolume(PVOLUME);
  7986. void LockVolume(PVOLUME);
  7987. BOOL UnlockVolume(PVOLUME);
  7988. void InvalidateVolumeInfo(PVOLUME);
  7989. void ClearVolumeInfo(PVOLUME);
  7990. void GetUnavailableVolumeRootPath(PCLINKINFO, LPTSTR);
  7991. BOOL VerifyAvailableVolume(PVOLUME);
  7992. void ExpensiveResolveVolumeRootPath(PVOLUME, LPTSTR);
  7993. void ResolveVolumeRootPath(PVOLUME, LPTSTR);
  7994. VOLUMERESULT VOLUMERESULTFromLastError(VOLUMERESULT);
  7995. TWINRESULT WriteVolume(HCACHEDFILE, PVOLUME);
  7996. TWINRESULT ReadVolume(HCACHEDFILE, PVOLUMELIST, PLINKINFO, UINT, HHANDLETRANS);
  7997. /*
  7998. ** VolumeSortCmp()
  7999. **
  8000. **
  8001. **
  8002. ** Arguments:
  8003. **
  8004. ** Returns:
  8005. **
  8006. ** Side Effects: none
  8007. **
  8008. ** Volumes are sorted by:
  8009. ** 1) LinkInfo volume
  8010. ** 2) pointer
  8011. */
  8012. COMPARISONRESULT VolumeSortCmp(PCVOID pcvol1, PCVOID pcvol2)
  8013. {
  8014. COMPARISONRESULT cr;
  8015. ASSERT(IS_VALID_STRUCT_PTR(pcvol1, CVOLUME));
  8016. ASSERT(IS_VALID_STRUCT_PTR(pcvol2, CVOLUME));
  8017. cr = CompareLinkInfoVolumes(((PCVOLUME)pcvol1)->pli,
  8018. ((PCVOLUME)pcvol2)->pli);
  8019. if (cr == CR_EQUAL)
  8020. cr = ComparePointers(pcvol1, pcvol1);
  8021. return(cr);
  8022. }
  8023. /*
  8024. ** VolumeSearchCmp()
  8025. **
  8026. **
  8027. **
  8028. ** Arguments:
  8029. **
  8030. ** Returns:
  8031. **
  8032. ** Side Effects: none
  8033. **
  8034. ** Volumes are searched by:
  8035. ** 1) LinkInfo volume
  8036. */
  8037. COMPARISONRESULT VolumeSearchCmp(PCVOID pcli, PCVOID pcvol)
  8038. {
  8039. ASSERT(IS_VALID_STRUCT_PTR(pcli, CLINKINFO));
  8040. ASSERT(IS_VALID_STRUCT_PTR(pcvol, CVOLUME));
  8041. return(CompareLinkInfoVolumes(pcli, ((PCVOLUME)pcvol)->pli));
  8042. }
  8043. /*
  8044. ** SearchForVolumeByRootPathCmp()
  8045. **
  8046. **
  8047. **
  8048. ** Arguments:
  8049. **
  8050. ** Returns:
  8051. **
  8052. ** Side Effects: none
  8053. **
  8054. ** Volumes are searched by:
  8055. ** 1) available volume root path
  8056. */
  8057. BOOL SearchForVolumeByRootPathCmp(PCVOID pcszFullPath,
  8058. PCVOID pcvol)
  8059. {
  8060. BOOL bDifferent;
  8061. ASSERT(IsFullPath(pcszFullPath));
  8062. ASSERT(IS_VALID_STRUCT_PTR(pcvol, CVOLUME));
  8063. if (((PCVOLUME)pcvol)->vs == VS_AVAILABLE &&
  8064. IS_FLAG_SET(((PCVOLUME)pcvol)->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
  8065. {
  8066. LPCTSTR pcszVolumeRootPath;
  8067. pcszVolumeRootPath = GetBfcString(((PCVOLUME)pcvol)->hsRootPath);
  8068. bDifferent = MyLStrCmpNI(pcszFullPath, pcszVolumeRootPath,
  8069. lstrlen(pcszVolumeRootPath));
  8070. }
  8071. else
  8072. bDifferent = TRUE;
  8073. return(bDifferent);
  8074. }
  8075. BOOL UnifyVolume(PVOLUMELIST pvl, PLINKINFO pliRoot,
  8076. PVOLUME *ppvol)
  8077. {
  8078. BOOL bResult = FALSE;
  8079. ASSERT(IS_VALID_STRUCT_PTR(pvl, CVOLUMELIST));
  8080. ASSERT(IS_VALID_STRUCT_PTR(pliRoot, CLINKINFO));
  8081. ASSERT(IS_VALID_WRITE_PTR(ppvol, PVOLUME));
  8082. if (AllocateMemory(sizeof(**ppvol), ppvol))
  8083. {
  8084. if (CopyLinkInfo(pliRoot, &((*ppvol)->pli)))
  8085. {
  8086. ARRAYINDEX aiUnused;
  8087. (*ppvol)->ulcLock = 0;
  8088. (*ppvol)->dwFlags = 0;
  8089. (*ppvol)->vs = VS_UNKNOWN;
  8090. (*ppvol)->hsRootPath = NULL;
  8091. (*ppvol)->pvlParent = pvl;
  8092. if (AddPtr(pvl->hpa, VolumeSortCmp, *ppvol, &aiUnused))
  8093. bResult = TRUE;
  8094. else
  8095. {
  8096. FreeMemory((*ppvol)->pli);
  8097. UNIFYVOLUME_BAIL:
  8098. FreeMemory(*ppvol);
  8099. }
  8100. }
  8101. else
  8102. goto UNIFYVOLUME_BAIL;
  8103. }
  8104. ASSERT(! bResult ||
  8105. IS_VALID_STRUCT_PTR(*ppvol, CVOLUME));
  8106. return(bResult);
  8107. }
  8108. BOOL CreateVolume(PVOLUMELIST pvl, PLINKINFO pliRoot,
  8109. PVOLUME *ppvol)
  8110. {
  8111. BOOL bResult;
  8112. PVOLUME pvol;
  8113. ARRAYINDEX aiFound;
  8114. ASSERT(IS_VALID_STRUCT_PTR(pvl, CVOLUMELIST));
  8115. ASSERT(IS_VALID_STRUCT_PTR(pliRoot, CLINKINFO));
  8116. ASSERT(IS_VALID_WRITE_PTR(ppvol, PVOLUME));
  8117. /* Does a volume for the given root path already exist? */
  8118. if (SearchSortedArray(pvl->hpa, &VolumeSearchCmp, pliRoot, &aiFound))
  8119. {
  8120. pvol = GetPtr(pvl->hpa, aiFound);
  8121. bResult = TRUE;
  8122. }
  8123. else
  8124. bResult = UnifyVolume(pvl, pliRoot, &pvol);
  8125. if (bResult)
  8126. {
  8127. LockVolume(pvol);
  8128. *ppvol = pvol;
  8129. }
  8130. ASSERT(! bResult ||
  8131. IS_VALID_STRUCT_PTR(*ppvol, CVOLUME));
  8132. return(bResult);
  8133. }
  8134. void UnlinkVolume(PCVOLUME pcvol)
  8135. {
  8136. HPTRARRAY hpa;
  8137. ARRAYINDEX aiFound;
  8138. ASSERT(IS_VALID_STRUCT_PTR(pcvol, CVOLUME));
  8139. hpa = pcvol->pvlParent->hpa;
  8140. if (EVAL(SearchSortedArray(hpa, &VolumeSortCmp, pcvol, &aiFound)))
  8141. {
  8142. ASSERT(GetPtr(hpa, aiFound) == pcvol);
  8143. DeletePtr(hpa, aiFound);
  8144. }
  8145. }
  8146. BOOL DisconnectVolume(PVOLUME pvol)
  8147. {
  8148. BOOL bResult;
  8149. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8150. if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_DISCONNECT))
  8151. {
  8152. bResult = DisconnectLinkInfo(pvol->pli);
  8153. CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_DISCONNECT);
  8154. }
  8155. else
  8156. bResult = TRUE;
  8157. return(bResult);
  8158. }
  8159. void DestroyVolume(PVOLUME pvol)
  8160. {
  8161. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8162. ClearVolumeInfo(pvol);
  8163. FreeMemory(pvol->pli);
  8164. FreeMemory(pvol);
  8165. }
  8166. void LockVolume(PVOLUME pvol)
  8167. {
  8168. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8169. ASSERT(pvol->ulcLock < ULONG_MAX);
  8170. pvol->ulcLock++;
  8171. }
  8172. BOOL UnlockVolume(PVOLUME pvol)
  8173. {
  8174. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8175. if (EVAL(pvol->ulcLock > 0))
  8176. pvol->ulcLock--;
  8177. return(pvol->ulcLock > 0);
  8178. }
  8179. void InvalidateVolumeInfo(PVOLUME pvol)
  8180. {
  8181. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8182. SET_FLAG(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME);
  8183. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8184. }
  8185. void ClearVolumeInfo(PVOLUME pvol)
  8186. {
  8187. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8188. DisconnectVolume(pvol);
  8189. if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
  8190. {
  8191. DeleteString(pvol->hsRootPath);
  8192. CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID);
  8193. }
  8194. CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME);
  8195. pvol->vs = VS_UNKNOWN;
  8196. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8197. }
  8198. void GetUnavailableVolumeRootPath(PCLINKINFO pcli,
  8199. LPTSTR pszRootPathBuf)
  8200. {
  8201. LPCSTR pcszLinkInfoData;
  8202. TCHAR szTmp[MAX_PATH] = TEXT("");
  8203. ASSERT(IS_VALID_STRUCT_PTR(pcli, CLINKINFO));
  8204. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
  8205. /*
  8206. * Try unavailable volume root paths in the following order:
  8207. * 1) last redirected device
  8208. * 2) net resource name
  8209. * 3) local path ...and take the _last_ good one!
  8210. */
  8211. if (GetLinkInfoData(pcli, LIDT_REDIRECTED_DEVICE, &pcszLinkInfoData) ||
  8212. GetLinkInfoData(pcli, LIDT_NET_RESOURCE, &pcszLinkInfoData) ||
  8213. GetLinkInfoData(pcli, LIDT_LOCAL_BASE_PATH, &pcszLinkInfoData))
  8214. {
  8215. ASSERT(lstrlenA(pcszLinkInfoData) < MAX_PATH_LEN);
  8216. MultiByteToWideChar( OurGetACP(), 0, pcszLinkInfoData, -1, szTmp, MAX_PATH);
  8217. ComposePath(pszRootPathBuf, szTmp, TEXT("\\"));
  8218. }
  8219. else
  8220. {
  8221. pszRootPathBuf[0] = TEXT('\0');
  8222. ERROR_OUT((TEXT("GetUnavailableVolumeRootPath(): Net resource name and local base path unavailable. Using empty string as unavailable root path.")));
  8223. }
  8224. ASSERT(IsRootPath(pszRootPathBuf) &&
  8225. EVAL(lstrlen(pszRootPathBuf) < MAX_PATH_LEN));
  8226. }
  8227. BOOL VerifyAvailableVolume(PVOLUME pvol)
  8228. {
  8229. BOOL bResult = FALSE;
  8230. PLINKINFO pli;
  8231. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8232. ASSERT(pvol->vs == VS_AVAILABLE);
  8233. ASSERT(IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID));
  8234. WARNING_OUT((TEXT("VerifyAvailableVolume(): Calling CreateLinkInfo() to verify volume on %s."),
  8235. GetBfcString(pvol->hsRootPath)));
  8236. if (CreateLinkInfo(GetBfcString(pvol->hsRootPath), &pli))
  8237. {
  8238. bResult = (CompareLinkInfoReferents(pvol->pli, pli) == CR_EQUAL);
  8239. DestroyLinkInfo(pli);
  8240. if (bResult)
  8241. TRACE_OUT((TEXT("VerifyAvailableVolume(): Volume %s has not changed."),
  8242. GetBfcString(pvol->hsRootPath)));
  8243. else
  8244. WARNING_OUT((TEXT("VerifyAvailableVolume(): Volume %s has changed."),
  8245. GetBfcString(pvol->hsRootPath)));
  8246. }
  8247. else
  8248. WARNING_OUT((TEXT("VerifyAvailableVolume(): CreateLinkInfo() failed for %s."),
  8249. GetBfcString(pvol->hsRootPath)));
  8250. return(bResult);
  8251. }
  8252. void ExpensiveResolveVolumeRootPath(PVOLUME pvol, LPTSTR pszVolumeRootPathBuf)
  8253. {
  8254. BOOL bResult;
  8255. DWORD dwOutFlags;
  8256. PLINKINFO pliUpdated;
  8257. HSTRING hsRootPath;
  8258. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8259. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszVolumeRootPathBuf, STR, MAX_PATH_LEN));
  8260. if (pvol->vs == VS_UNKNOWN ||
  8261. pvol->vs == VS_AVAILABLE)
  8262. {
  8263. /*
  8264. * Only request a connection if connections are still permitted in this
  8265. * volume list.
  8266. */
  8267. WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Calling ResolveLinkInfo() to determine volume availability and root path.")));
  8268. bResult = ResolveLinkInfo(pvol->pli, pszVolumeRootPathBuf,
  8269. pvol->pvlParent->dwFlags,
  8270. pvol->pvlParent->hwndOwner, &dwOutFlags,
  8271. &pliUpdated);
  8272. if (bResult)
  8273. {
  8274. pvol->vs = VS_AVAILABLE;
  8275. if (IS_FLAG_SET(dwOutFlags, RLI_OFL_UPDATED))
  8276. {
  8277. PLINKINFO pliUpdatedCopy;
  8278. ASSERT(IS_FLAG_SET(pvol->pvlParent->dwFlags, RLI_IFL_UPDATE));
  8279. if (CopyLinkInfo(pliUpdated, &pliUpdatedCopy))
  8280. {
  8281. FreeMemory(pvol->pli);
  8282. pvol->pli = pliUpdatedCopy;
  8283. }
  8284. DestroyLinkInfo(pliUpdated);
  8285. WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Updating LinkInfo for volume %s."),
  8286. pszVolumeRootPathBuf));
  8287. }
  8288. if (IS_FLAG_SET(dwOutFlags, RLI_OFL_DISCONNECT))
  8289. {
  8290. SET_FLAG(pvol->dwFlags, VOLUME_FL_DISCONNECT);
  8291. WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Volume %s must be disconnected when finished."),
  8292. pszVolumeRootPathBuf));
  8293. }
  8294. TRACE_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Volume %s is available."),
  8295. pszVolumeRootPathBuf));
  8296. }
  8297. else
  8298. ASSERT(GetLastError() != ERROR_INVALID_PARAMETER);
  8299. }
  8300. else
  8301. {
  8302. ASSERT(pvol->vs == VS_UNAVAILABLE);
  8303. bResult = FALSE;
  8304. }
  8305. if (! bResult)
  8306. {
  8307. pvol->vs = VS_UNAVAILABLE;
  8308. if (GetLastError() == ERROR_CANCELLED)
  8309. {
  8310. ASSERT(IS_FLAG_SET(pvol->pvlParent->dwFlags, RLI_IFL_CONNECT));
  8311. CLEAR_FLAG(pvol->pvlParent->dwFlags, RLI_IFL_CONNECT);
  8312. WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Connection attempt cancelled. No subsequent connections will be attempted.")));
  8313. }
  8314. GetUnavailableVolumeRootPath(pvol->pli, pszVolumeRootPathBuf);
  8315. WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Using %s as unavailable volume root path."),
  8316. pszVolumeRootPathBuf));
  8317. }
  8318. /* Add volume root path string to volume list's string table. */
  8319. if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
  8320. {
  8321. CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID);
  8322. DeleteString(pvol->hsRootPath);
  8323. }
  8324. if (AddString(pszVolumeRootPathBuf, pvol->pvlParent->hst, GetHashBucketIndex, &hsRootPath))
  8325. {
  8326. SET_FLAG(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID);
  8327. pvol->hsRootPath = hsRootPath;
  8328. }
  8329. else
  8330. WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Unable to save %s as volume root path."),
  8331. pszVolumeRootPathBuf));
  8332. }
  8333. void ResolveVolumeRootPath(PVOLUME pvol,
  8334. LPTSTR pszVolumeRootPathBuf)
  8335. {
  8336. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8337. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszVolumeRootPathBuf, STR, MAX_PATH_LEN));
  8338. /* Do we have a cached volume root path to use? */
  8339. if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID) &&
  8340. (IS_FLAG_CLEAR(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME) ||
  8341. (pvol->vs == VS_AVAILABLE &&
  8342. VerifyAvailableVolume(pvol))))
  8343. {
  8344. /* Yes. */
  8345. MyLStrCpyN(pszVolumeRootPathBuf, GetBfcString(pvol->hsRootPath), MAX_PATH_LEN);
  8346. ASSERT(lstrlen(pszVolumeRootPathBuf) < MAX_PATH_LEN);
  8347. ASSERT(pvol->vs != VS_UNKNOWN);
  8348. }
  8349. else
  8350. /* No. Welcome in I/O City. */
  8351. ExpensiveResolveVolumeRootPath(pvol, pszVolumeRootPathBuf);
  8352. CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME);
  8353. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8354. }
  8355. VOLUMERESULT VOLUMERESULTFromLastError(VOLUMERESULT vr)
  8356. {
  8357. switch (GetLastError())
  8358. {
  8359. case ERROR_OUTOFMEMORY:
  8360. vr = VR_OUT_OF_MEMORY;
  8361. break;
  8362. case ERROR_BAD_PATHNAME:
  8363. vr = VR_INVALID_PATH;
  8364. break;
  8365. default:
  8366. break;
  8367. }
  8368. return(vr);
  8369. }
  8370. TWINRESULT WriteVolume(HCACHEDFILE hcf, PVOLUME pvol)
  8371. {
  8372. TWINRESULT tr;
  8373. DBVOLUME dbvol;
  8374. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  8375. ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
  8376. /* Write database volume followed by LinkInfo structure. */
  8377. dbvol.hvol = (HVOLUME)pvol;
  8378. if (WriteToCachedFile(hcf, (PCVOID)&dbvol, sizeof(dbvol), NULL) &&
  8379. WriteToCachedFile(hcf, pvol->pli, *(PDWORD)(pvol->pli), NULL))
  8380. tr = TR_SUCCESS;
  8381. else
  8382. tr = TR_BRIEFCASE_WRITE_FAILED;
  8383. return(tr);
  8384. }
  8385. TWINRESULT ReadVolume(HCACHEDFILE hcf, PVOLUMELIST pvl,
  8386. PLINKINFO pliBuf, UINT ucbLinkInfoBufLen,
  8387. HHANDLETRANS hhtVolumes)
  8388. {
  8389. TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  8390. DBVOLUME dbvol;
  8391. DWORD dwcbRead;
  8392. UINT ucbLinkInfoLen;
  8393. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  8394. ASSERT(IS_VALID_STRUCT_PTR(pvl, CVOLUMELIST));
  8395. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pliBuf, LINKINFO, ucbLinkInfoBufLen));
  8396. ASSERT(IS_VALID_HANDLE(hhtVolumes, HANDLETRANS));
  8397. if (ReadFromCachedFile(hcf, &dbvol, sizeof(dbvol), &dwcbRead) &&
  8398. dwcbRead == sizeof(dbvol) &&
  8399. ReadFromCachedFile(hcf, &ucbLinkInfoLen, sizeof(ucbLinkInfoLen), &dwcbRead) &&
  8400. dwcbRead == sizeof(ucbLinkInfoLen) &&
  8401. ucbLinkInfoLen <= ucbLinkInfoBufLen)
  8402. {
  8403. /* Read the remainder of the LinkInfo structure into memory. */
  8404. DWORD dwcbRemainder;
  8405. pliBuf->ucbSize = ucbLinkInfoLen;
  8406. dwcbRemainder = ucbLinkInfoLen - sizeof(ucbLinkInfoLen);
  8407. if (ReadFromCachedFile(hcf, (PBYTE)pliBuf + sizeof(ucbLinkInfoLen),
  8408. dwcbRemainder, &dwcbRead) &&
  8409. dwcbRead == dwcbRemainder &&
  8410. IsValidLinkInfo(pliBuf))
  8411. {
  8412. PVOLUME pvol;
  8413. if (CreateVolume(pvl, pliBuf, &pvol))
  8414. {
  8415. /*
  8416. * To leave read volumes with 0 initial lock count, we must undo
  8417. * the LockVolume() performed by CreateVolume().
  8418. */
  8419. UnlockVolume(pvol);
  8420. if (AddHandleToHandleTranslator(hhtVolumes,
  8421. (HGENERIC)(dbvol.hvol),
  8422. (HGENERIC)pvol))
  8423. tr = TR_SUCCESS;
  8424. else
  8425. {
  8426. UnlinkVolume(pvol);
  8427. DestroyVolume(pvol);
  8428. tr = TR_OUT_OF_MEMORY;
  8429. }
  8430. }
  8431. else
  8432. tr = TR_OUT_OF_MEMORY;
  8433. }
  8434. }
  8435. return(tr);
  8436. }
  8437. BOOL CreateVolumeList(DWORD dwFlags, HWND hwndOwner,
  8438. PHVOLUMELIST phvl)
  8439. {
  8440. BOOL bResult = FALSE;
  8441. PVOLUMELIST pvl;
  8442. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RLI_IFLAGS));
  8443. ASSERT(IS_FLAG_CLEAR(dwFlags, RLI_IFL_ALLOW_UI) ||
  8444. IS_VALID_HANDLE(hwndOwner, WND));
  8445. ASSERT(IS_VALID_WRITE_PTR(phvl, HVOLUMELIST));
  8446. if (AllocateMemory(sizeof(*pvl), &pvl))
  8447. {
  8448. NEWSTRINGTABLE nszt;
  8449. /* Create string table for volume root path strngs. */
  8450. nszt.hbc = NUM_VOLUME_HASH_BUCKETS;
  8451. if (CreateStringTable(&nszt, &(pvl->hst)))
  8452. {
  8453. NEWPTRARRAY npa;
  8454. /* Create pointer array of volumes. */
  8455. npa.aicInitialPtrs = NUM_START_VOLUMES;
  8456. npa.aicAllocGranularity = NUM_VOLUMES_TO_ADD;
  8457. npa.dwFlags = NPA_FL_SORTED_ADD;
  8458. if (CreatePtrArray(&npa, &(pvl->hpa)))
  8459. {
  8460. pvl->dwFlags = dwFlags;
  8461. pvl->hwndOwner = hwndOwner;
  8462. *phvl = (HVOLUMELIST)pvl;
  8463. bResult = TRUE;
  8464. }
  8465. else
  8466. {
  8467. DestroyStringTable(pvl->hst);
  8468. CREATEVOLUMELIST_BAIL:
  8469. FreeMemory(pvl);
  8470. }
  8471. }
  8472. else
  8473. goto CREATEVOLUMELIST_BAIL;
  8474. }
  8475. ASSERT(! bResult ||
  8476. IS_VALID_HANDLE(*phvl, VOLUMELIST));
  8477. return(bResult);
  8478. }
  8479. void DestroyVolumeList(HVOLUMELIST hvl)
  8480. {
  8481. ARRAYINDEX aicPtrs;
  8482. ARRAYINDEX ai;
  8483. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8484. /* First free all volumes in array. */
  8485. aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
  8486. for (ai = 0; ai < aicPtrs; ai++)
  8487. DestroyVolume(GetPtr(((PCVOLUMELIST)hvl)->hpa, ai));
  8488. /* Now wipe out the array. */
  8489. DestroyPtrArray(((PCVOLUMELIST)hvl)->hpa);
  8490. ASSERT(! GetStringCount(((PCVOLUMELIST)hvl)->hst));
  8491. DestroyStringTable(((PCVOLUMELIST)hvl)->hst);
  8492. FreeMemory((PVOLUMELIST)hvl);
  8493. }
  8494. void InvalidateVolumeListInfo(HVOLUMELIST hvl)
  8495. {
  8496. ARRAYINDEX aicPtrs;
  8497. ARRAYINDEX ai;
  8498. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8499. aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
  8500. for (ai = 0; ai < aicPtrs; ai++)
  8501. InvalidateVolumeInfo(GetPtr(((PCVOLUMELIST)hvl)->hpa, ai));
  8502. WARNING_OUT((TEXT("InvalidateVolumeListInfo(): Volume cache invalidated.")));
  8503. }
  8504. void ClearVolumeListInfo(HVOLUMELIST hvl)
  8505. {
  8506. ARRAYINDEX aicPtrs;
  8507. ARRAYINDEX ai;
  8508. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8509. aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
  8510. for (ai = 0; ai < aicPtrs; ai++)
  8511. ClearVolumeInfo(GetPtr(((PCVOLUMELIST)hvl)->hpa, ai));
  8512. WARNING_OUT((TEXT("ClearVolumeListInfo(): Volume cache cleared.")));
  8513. }
  8514. VOLUMERESULT AddVolume(HVOLUMELIST hvl, LPCTSTR pcszPath,
  8515. PHVOLUME phvol, LPTSTR pszPathSuffixBuf)
  8516. {
  8517. VOLUMERESULT vr;
  8518. TCHAR rgchPath[MAX_PATH_LEN];
  8519. LPTSTR pszFileName;
  8520. DWORD dwPathLen;
  8521. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8522. ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
  8523. ASSERT(IS_VALID_WRITE_PTR(phvol, HVOLUME));
  8524. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathSuffixBuf, STR, MAX_PATH_LEN));
  8525. dwPathLen = GetFullPathName(pcszPath, ARRAYSIZE(rgchPath), rgchPath,
  8526. &pszFileName);
  8527. if (dwPathLen > 0 && dwPathLen < ARRAYSIZE(rgchPath))
  8528. {
  8529. ARRAYINDEX aiFound;
  8530. /* Does a volume for this root path already exist? */
  8531. if (LinearSearchArray(((PVOLUMELIST)hvl)->hpa,
  8532. &SearchForVolumeByRootPathCmp, rgchPath,
  8533. &aiFound))
  8534. {
  8535. PVOLUME pvol;
  8536. LPCTSTR pcszVolumeRootPath;
  8537. /* Yes. */
  8538. pvol = GetPtr(((PVOLUMELIST)hvl)->hpa, aiFound);
  8539. LockVolume(pvol);
  8540. ASSERT(pvol->vs == VS_AVAILABLE &&
  8541. IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID));
  8542. pcszVolumeRootPath = GetBfcString(pvol->hsRootPath);
  8543. ASSERT(lstrlen(pcszVolumeRootPath) <= lstrlen(rgchPath));
  8544. lstrcpy(pszPathSuffixBuf, rgchPath + lstrlen(pcszVolumeRootPath));
  8545. *phvol = (HVOLUME)pvol;
  8546. vr = VR_SUCCESS;
  8547. }
  8548. else
  8549. {
  8550. DWORD dwOutFlags;
  8551. TCHAR rgchNetResource[MAX_PATH_LEN];
  8552. LPTSTR pszRootPathSuffix;
  8553. /* No. Create a new volume. */
  8554. if (GetCanonicalPathInfo(pcszPath, rgchPath, &dwOutFlags,
  8555. rgchNetResource, &pszRootPathSuffix))
  8556. {
  8557. PLINKINFO pli;
  8558. lstrcpy(pszPathSuffixBuf, pszRootPathSuffix);
  8559. *pszRootPathSuffix = TEXT('\0');
  8560. WARNING_OUT((TEXT("AddVolume(): Creating LinkInfo for root path %s."),
  8561. rgchPath));
  8562. if (CreateLinkInfo(rgchPath, &pli))
  8563. {
  8564. PVOLUME pvol;
  8565. if (CreateVolume((PVOLUMELIST)hvl, pli, &pvol))
  8566. {
  8567. TCHAR rgchUnusedVolumeRootPath[MAX_PATH_LEN];
  8568. ResolveVolumeRootPath(pvol, rgchUnusedVolumeRootPath);
  8569. *phvol = (HVOLUME)pvol;
  8570. vr = VR_SUCCESS;
  8571. }
  8572. else
  8573. vr = VR_OUT_OF_MEMORY;
  8574. DestroyLinkInfo(pli);
  8575. }
  8576. else
  8577. /*
  8578. * Differentiate between VR_UNAVAILABLE_VOLUME and
  8579. * VR_OUT_OF_MEMORY.
  8580. */
  8581. vr = VOLUMERESULTFromLastError(VR_UNAVAILABLE_VOLUME);
  8582. }
  8583. else
  8584. vr = VOLUMERESULTFromLastError(VR_INVALID_PATH);
  8585. }
  8586. }
  8587. else
  8588. {
  8589. ASSERT(! dwPathLen);
  8590. vr = VOLUMERESULTFromLastError(VR_INVALID_PATH);
  8591. }
  8592. ASSERT(vr != VR_SUCCESS ||
  8593. (IS_VALID_HANDLE(*phvol, VOLUME) &&
  8594. EVAL(IsValidPathSuffix(pszPathSuffixBuf))));
  8595. return(vr);
  8596. }
  8597. void DeleteVolume(HVOLUME hvol)
  8598. {
  8599. ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
  8600. if (! UnlockVolume((PVOLUME)hvol))
  8601. {
  8602. UnlinkVolume((PVOLUME)hvol);
  8603. DestroyVolume((PVOLUME)hvol);
  8604. }
  8605. }
  8606. COMPARISONRESULT CompareVolumes(HVOLUME hvolFirst,
  8607. HVOLUME hvolSecond)
  8608. {
  8609. ASSERT(IS_VALID_HANDLE(hvolFirst, VOLUME));
  8610. ASSERT(IS_VALID_HANDLE(hvolSecond, VOLUME));
  8611. /* This comparison works across volume lists. */
  8612. return(CompareLinkInfoVolumes(((PCVOLUME)hvolFirst)->pli,
  8613. ((PCVOLUME)hvolSecond)->pli));
  8614. }
  8615. BOOL CopyVolume(HVOLUME hvolSrc, HVOLUMELIST hvlDest,
  8616. PHVOLUME phvolCopy)
  8617. {
  8618. BOOL bResult;
  8619. PVOLUME pvol;
  8620. ASSERT(IS_VALID_HANDLE(hvolSrc, VOLUME));
  8621. ASSERT(IS_VALID_HANDLE(hvlDest, VOLUMELIST));
  8622. ASSERT(IS_VALID_WRITE_PTR(phvolCopy, HVOLUME));
  8623. /* Is the destination volume list the source volume's volume list? */
  8624. if (((PCVOLUME)hvolSrc)->pvlParent == (PCVOLUMELIST)hvlDest)
  8625. {
  8626. /* Yes. Use the source volume. */
  8627. LockVolume((PVOLUME)hvolSrc);
  8628. pvol = (PVOLUME)hvolSrc;
  8629. bResult = TRUE;
  8630. }
  8631. else
  8632. bResult = CreateVolume((PVOLUMELIST)hvlDest, ((PCVOLUME)hvolSrc)->pli,
  8633. &pvol);
  8634. if (bResult)
  8635. *phvolCopy = (HVOLUME)pvol;
  8636. ASSERT(! bResult ||
  8637. IS_VALID_HANDLE(*phvolCopy, VOLUME));
  8638. return(bResult);
  8639. }
  8640. BOOL IsVolumeAvailable(HVOLUME hvol)
  8641. {
  8642. TCHAR rgchUnusedVolumeRootPath[MAX_PATH_LEN];
  8643. ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
  8644. ResolveVolumeRootPath((PVOLUME)hvol, rgchUnusedVolumeRootPath);
  8645. ASSERT(IsValidVOLUMESTATE(((PCVOLUME)hvol)->vs) &&
  8646. ((PCVOLUME)hvol)->vs != VS_UNKNOWN);
  8647. return(((PCVOLUME)hvol)->vs == VS_AVAILABLE);
  8648. }
  8649. void GetVolumeRootPath(HVOLUME hvol, LPTSTR pszRootPathBuf)
  8650. {
  8651. ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
  8652. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
  8653. ResolveVolumeRootPath((PVOLUME)hvol, pszRootPathBuf);
  8654. ASSERT(IsRootPath(pszRootPathBuf));
  8655. }
  8656. ULONG GetVolumeCount(HVOLUMELIST hvl)
  8657. {
  8658. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8659. return(GetPtrCount(((PCVOLUMELIST)hvl)->hpa));
  8660. }
  8661. void DescribeVolume(HVOLUME hvol, PVOLUMEDESC pvoldesc)
  8662. {
  8663. PCVOID pcv;
  8664. ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
  8665. ASSERT(pvoldesc->ulSize == sizeof(*pvoldesc));
  8666. pvoldesc->dwFlags = 0;
  8667. if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_VOLUME_SERIAL_NUMBER, &pcv))
  8668. {
  8669. pvoldesc->dwSerialNumber = *(PCDWORD)pcv;
  8670. SET_FLAG(pvoldesc->dwFlags, VD_FL_SERIAL_NUMBER_VALID);
  8671. }
  8672. else
  8673. pvoldesc->dwSerialNumber = 0;
  8674. if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_VOLUME_LABELW, &pcv) && pcv)
  8675. {
  8676. lstrcpy(pvoldesc->rgchVolumeLabel, pcv);
  8677. SET_FLAG(pvoldesc->dwFlags, VD_FL_VOLUME_LABEL_VALID);
  8678. }
  8679. else if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_VOLUME_LABEL, &pcv) && pcv)
  8680. {
  8681. MultiByteToWideChar( OurGetACP(), 0, pcv, -1, pvoldesc->rgchVolumeLabel, MAX_PATH);
  8682. SET_FLAG(pvoldesc->dwFlags, VD_FL_VOLUME_LABEL_VALID);
  8683. }
  8684. else
  8685. {
  8686. pvoldesc->rgchVolumeLabel[0] = TEXT('\0');
  8687. }
  8688. if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_NET_RESOURCEW, &pcv) && pcv)
  8689. {
  8690. lstrcpy(pvoldesc->rgchNetResource, pcv);
  8691. SET_FLAG(pvoldesc->dwFlags, VD_FL_NET_RESOURCE_VALID);
  8692. }
  8693. else if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_NET_RESOURCE, &pcv) && pcv)
  8694. {
  8695. MultiByteToWideChar( OurGetACP(), 0, pcv, -1, pvoldesc->rgchNetResource, MAX_PATH);
  8696. SET_FLAG(pvoldesc->dwFlags, VD_FL_NET_RESOURCE_VALID);
  8697. }
  8698. else
  8699. pvoldesc->rgchNetResource[0] = TEXT('\0');
  8700. ASSERT(IS_VALID_STRUCT_PTR(pvoldesc, CVOLUMEDESC));
  8701. }
  8702. TWINRESULT WriteVolumeList(HCACHEDFILE hcf, HVOLUMELIST hvl)
  8703. {
  8704. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  8705. DWORD dwcbDBVolumeListHeaderOffset;
  8706. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  8707. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8708. /* Save initial file position. */
  8709. dwcbDBVolumeListHeaderOffset = GetCachedFilePointerPosition(hcf);
  8710. if (dwcbDBVolumeListHeaderOffset != INVALID_SEEK_POSITION)
  8711. {
  8712. DBVOLUMELISTHEADER dbvlh;
  8713. /* Leave space for volume list header. */
  8714. ZeroMemory(&dbvlh, sizeof(dbvlh));
  8715. if (WriteToCachedFile(hcf, (PCVOID)&dbvlh, sizeof(dbvlh), NULL))
  8716. {
  8717. ARRAYINDEX aicPtrs;
  8718. ARRAYINDEX ai;
  8719. UINT ucbMaxLinkInfoLen = 0;
  8720. LONG lcVolumes = 0;
  8721. tr = TR_SUCCESS;
  8722. aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
  8723. /* Write all volumes. */
  8724. for (ai = 0; ai < aicPtrs; ai++)
  8725. {
  8726. PVOLUME pvol;
  8727. pvol = GetPtr(((PCVOLUMELIST)hvl)->hpa, ai);
  8728. /*
  8729. * As a sanity check, don't save any volume with a lock count of 0.
  8730. * A 0 lock count implies that the volume has not been referenced
  8731. * since it was restored from the database, or something is broken.
  8732. */
  8733. if (pvol->ulcLock > 0)
  8734. {
  8735. tr = WriteVolume(hcf, pvol);
  8736. if (tr == TR_SUCCESS)
  8737. {
  8738. ASSERT(lcVolumes < LONG_MAX);
  8739. lcVolumes++;
  8740. if (pvol->pli->ucbSize > ucbMaxLinkInfoLen)
  8741. ucbMaxLinkInfoLen = pvol->pli->ucbSize;
  8742. }
  8743. else
  8744. break;
  8745. }
  8746. else
  8747. ERROR_OUT((TEXT("WriteVolumeList(): VOLUME has 0 lock count and will not be written.")));
  8748. }
  8749. /* Save volume list header. */
  8750. if (tr == TR_SUCCESS)
  8751. {
  8752. dbvlh.lcVolumes = lcVolumes;
  8753. dbvlh.ucbMaxLinkInfoLen = ucbMaxLinkInfoLen;
  8754. tr = WriteDBSegmentHeader(hcf, dwcbDBVolumeListHeaderOffset,
  8755. &dbvlh, sizeof(dbvlh));
  8756. TRACE_OUT((TEXT("WriteVolumeList(): Wrote %ld volumes; maximum LinkInfo length %u bytes."),
  8757. dbvlh.lcVolumes,
  8758. dbvlh.ucbMaxLinkInfoLen));
  8759. }
  8760. }
  8761. }
  8762. return(tr);
  8763. }
  8764. TWINRESULT ReadVolumeList(HCACHEDFILE hcf, HVOLUMELIST hvl,
  8765. PHHANDLETRANS phht)
  8766. {
  8767. TWINRESULT tr;
  8768. DBVOLUMELISTHEADER dbvlh;
  8769. DWORD dwcbRead;
  8770. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  8771. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8772. ASSERT(IS_VALID_WRITE_PTR(phht, HHANDLETRANS));
  8773. if (ReadFromCachedFile(hcf, &dbvlh, sizeof(dbvlh), &dwcbRead) &&
  8774. dwcbRead == sizeof(dbvlh))
  8775. {
  8776. HHANDLETRANS hht;
  8777. tr = TR_OUT_OF_MEMORY;
  8778. if (CreateHandleTranslator(dbvlh.lcVolumes, &hht))
  8779. {
  8780. PLINKINFO pliBuf;
  8781. if (AllocateMemory(dbvlh.ucbMaxLinkInfoLen, &pliBuf))
  8782. {
  8783. LONG l;
  8784. tr = TR_SUCCESS;
  8785. TRACE_OUT((TEXT("ReadPathList(): Reading %ld volumes; maximum LinkInfo length %u bytes."),
  8786. dbvlh.lcVolumes,
  8787. dbvlh.ucbMaxLinkInfoLen));
  8788. for (l = 0; l < dbvlh.lcVolumes; l++)
  8789. {
  8790. tr = ReadVolume(hcf, (PVOLUMELIST)hvl, pliBuf,
  8791. dbvlh.ucbMaxLinkInfoLen, hht);
  8792. if (tr != TR_SUCCESS)
  8793. break;
  8794. }
  8795. if (tr == TR_SUCCESS)
  8796. {
  8797. PrepareForHandleTranslation(hht);
  8798. *phht = hht;
  8799. ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
  8800. ASSERT(IS_VALID_HANDLE(*phht, HANDLETRANS));
  8801. }
  8802. else
  8803. DestroyHandleTranslator(hht);
  8804. FreeMemory(pliBuf);
  8805. }
  8806. }
  8807. }
  8808. else
  8809. tr = TR_CORRUPT_BRIEFCASE;
  8810. ASSERT(tr != TR_SUCCESS ||
  8811. (IS_VALID_HANDLE(hvl, VOLUMELIST) &&
  8812. IS_VALID_HANDLE(*phht, HANDLETRANS)));
  8813. return(tr);
  8814. }
  8815. BOOL
  8816. EnumFirstBrfcasePath (
  8817. IN HBRFCASE Brfcase,
  8818. OUT PBRFPATH_ENUM e
  8819. )
  8820. {
  8821. e->PathList = GetBriefcasePathList(Brfcase);
  8822. e->Max = GetPtrCount(((PCPATHLIST)e->PathList)->hpa);
  8823. e->Index = 0;
  8824. return EnumNextBrfcasePath (e);
  8825. }
  8826. BOOL
  8827. EnumNextBrfcasePath (
  8828. IN OUT PBRFPATH_ENUM e
  8829. )
  8830. {
  8831. if (e->Index >= e->Max) {
  8832. return FALSE;
  8833. }
  8834. e->Path = GetPtr(((PCPATHLIST)e->PathList)->hpa, e->Index);
  8835. GetPathString (e->Path, e->PathString);
  8836. e->Index++;
  8837. return TRUE;
  8838. }
  8839. BOOL
  8840. ReplaceBrfcasePath (
  8841. IN PBRFPATH_ENUM PathEnum,
  8842. IN PCTSTR NewPath
  8843. )
  8844. {
  8845. HSTRING hsNew;
  8846. PCTSTR PathSuffix;
  8847. PPATH Path;
  8848. PCPATHLIST PathList;
  8849. MYASSERT (NewPath[1] == TEXT(':') && NewPath[2] == TEXT('\\'));
  8850. PathSuffix = NewPath + 3;
  8851. Path = (PPATH)PathEnum->Path;
  8852. if (CharCount (NewPath) <= CharCount (PathEnum->PathString)) {
  8853. //
  8854. // just copy over
  8855. //
  8856. StringCopy ((PTSTR)GetBfcString (Path->hsPathSuffix), PathSuffix);
  8857. } else {
  8858. PathList = (PCPATHLIST)PathEnum->PathList;
  8859. if (!AddString (PathSuffix, PathList->hst, GetHashBucketIndex, &hsNew)) {
  8860. return FALSE;
  8861. }
  8862. DeleteString (Path->hsPathSuffix);
  8863. Path->hsPathSuffix = hsNew;
  8864. }
  8865. return TRUE;
  8866. }