Leaked source code of windows server 2003
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.

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