Team Fortress 2 Source Code as on 22/4/2020
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.

908 lines
25 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Utility to interrogate and modify the data in the OSX IPC Server
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. // README:README
  8. //
  9. // This file implements the --wrap for ld on linux that lets file i/o api's
  10. // behave as if it were running on a case insensitive file system. Unfortunately,
  11. // this is needed by both steam2 and steam3. It was decided to check the source
  12. // into both locations, otherwise someone would find the .o and have no idea
  13. // where to go for the source if it was in the 'other' tree. Also, because this
  14. // needs to be linked into every elf binary, the .o is checked in for Steam3 so that it is
  15. // always available. In Steam2 it sits with the PosixWin32.cpp implementation and gets
  16. // compiled along side of it through the make system. If you are reading this in Steam3,
  17. // you will probably want to actually make your changes in steam2 and do a baseless merge
  18. // to the steam3 copy.
  19. //
  20. // HOWTO: Add a new function. Add the function with _WRAP to the makefiles as noted below.
  21. // Add the implementation to pathmatch.cpp - probably mimicking the existing functions.
  22. // Build steam2 and copy to matching steam3/client. Take the pathmatch.o from steam 2
  23. // and check it in to steam3 (in the location noted below). Full rebuild (re-link really)
  24. // of steam3. Test steam and check in.
  25. //
  26. // If you are looking at updating this file, please update the following as needed:
  27. //
  28. // STEAM2.../Projects/GazelleProto/Client/Engine/obj/RELEASE_NORMAL/libsteam_linux/Common/Misc/pathmatch.o
  29. // This is where steam2 builds the pathmatch.o out to.
  30. //
  31. // STEAM2.../Projects/GazelleProto/Makefile.shlib.base - contains _WRAP references
  32. // STEAM2.../Projects/Common/Misc/pathmatch.cpp - Where the source is checked in, keep in sync with:
  33. // STEAM3.../src/common/pathmatch.cpp - should be identical to previous file, but discoverable in steam3.
  34. // STEAM3.../src/lib/linux32/release/pathmatch.o - steam3 checked in version
  35. // STEAM3.../src/devtools/makefile_base_posix.mak - look for the _WRAP references
  36. #ifdef LINUX
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <stdint.h>
  41. #include <strings.h>
  42. #include <unistd.h>
  43. #include <getopt.h>
  44. #include <errno.h>
  45. #include <signal.h>
  46. #include <ctype.h>
  47. #include <dirent.h>
  48. #include <sys/types.h>
  49. #include <sys/stat.h>
  50. #include <sys/time.h>
  51. #include <sys/mount.h>
  52. #include <fcntl.h>
  53. #include <utime.h>
  54. #include <map>
  55. #include <string>
  56. #include <time.h>
  57. // Enable to do pathmatch caching. Beware: this code isn't threadsafe.
  58. // #define DO_PATHMATCH_CACHE
  59. #ifdef UTF8_PATHMATCH
  60. #define strcasecmp utf8casecmp
  61. #endif
  62. static bool s_bShowDiag;
  63. #define DEBUG_MSG( ... ) if ( s_bShowDiag ) fprintf( stderr, ##__VA_ARGS__ )
  64. #define DEBUG_BREAK() __asm__ __volatile__ ( "int $3" )
  65. #define _COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
  66. #define WRAP( fn, ret, ... ) \
  67. ret __real_##fn(__VA_ARGS__); \
  68. ret __wrap_##fn(__VA_ARGS__)
  69. #define CALL( fn ) __real_##fn
  70. // Needed by pathmatch code
  71. extern "C" int __real_access(const char *pathname, int mode);
  72. extern "C" DIR *__real_opendir(const char *name);
  73. // UTF-8 work from PhysicsFS: http://icculus.org/physfs/
  74. // Even if it wasn't under the zlib license, Ryan wrote all this code originally.
  75. #define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF
  76. #define UNICODE_BOGUS_CHAR_CODEPOINT '?'
  77. inline __attribute__ ((always_inline)) static uint32_t utf8codepoint(const char **_str)
  78. {
  79. const char *str = *_str;
  80. uint32_t retval = 0;
  81. uint32_t octet = (uint32_t) ((uint8_t) *str);
  82. uint32_t octet2, octet3, octet4;
  83. if (octet == 0) // null terminator, end of string.
  84. return 0;
  85. else if (octet < 128) // one octet char: 0 to 127
  86. {
  87. (*_str)++; // skip to next possible start of codepoint.
  88. return octet;
  89. }
  90. else if ((octet > 127) && (octet < 192)) // bad (starts with 10xxxxxx).
  91. {
  92. // Apparently each of these is supposed to be flagged as a bogus
  93. // char, instead of just resyncing to the next valid codepoint.
  94. (*_str)++; // skip to next possible start of codepoint.
  95. return UNICODE_BOGUS_CHAR_VALUE;
  96. }
  97. else if (octet < 224) // two octets
  98. {
  99. octet -= (128+64);
  100. octet2 = (uint32_t) ((uint8_t) *(++str));
  101. if ((octet2 & (128+64)) != 128) // Format isn't 10xxxxxx?
  102. return UNICODE_BOGUS_CHAR_VALUE;
  103. *_str += 2; // skip to next possible start of codepoint.
  104. retval = ((octet << 6) | (octet2 - 128));
  105. if ((retval >= 0x80) && (retval <= 0x7FF))
  106. return retval;
  107. }
  108. else if (octet < 240) // three octets
  109. {
  110. octet -= (128+64+32);
  111. octet2 = (uint32_t) ((uint8_t) *(++str));
  112. if ((octet2 & (128+64)) != 128) // Format isn't 10xxxxxx?
  113. return UNICODE_BOGUS_CHAR_VALUE;
  114. octet3 = (uint32_t) ((uint8_t) *(++str));
  115. if ((octet3 & (128+64)) != 128) // Format isn't 10xxxxxx?
  116. return UNICODE_BOGUS_CHAR_VALUE;
  117. *_str += 3; // skip to next possible start of codepoint.
  118. retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) );
  119. // There are seven "UTF-16 surrogates" that are illegal in UTF-8.
  120. switch (retval)
  121. {
  122. case 0xD800:
  123. case 0xDB7F:
  124. case 0xDB80:
  125. case 0xDBFF:
  126. case 0xDC00:
  127. case 0xDF80:
  128. case 0xDFFF:
  129. return UNICODE_BOGUS_CHAR_VALUE;
  130. }
  131. // 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge.
  132. if ((retval >= 0x800) && (retval <= 0xFFFD))
  133. return retval;
  134. }
  135. else if (octet < 248) // four octets
  136. {
  137. octet -= (128+64+32+16);
  138. octet2 = (uint32_t) ((uint8_t) *(++str));
  139. if ((octet2 & (128+64)) != 128) // Format isn't 10xxxxxx?
  140. return UNICODE_BOGUS_CHAR_VALUE;
  141. octet3 = (uint32_t) ((uint8_t) *(++str));
  142. if ((octet3 & (128+64)) != 128) // Format isn't 10xxxxxx?
  143. return UNICODE_BOGUS_CHAR_VALUE;
  144. octet4 = (uint32_t) ((uint8_t) *(++str));
  145. if ((octet4 & (128+64)) != 128) // Format isn't 10xxxxxx?
  146. return UNICODE_BOGUS_CHAR_VALUE;
  147. *_str += 4; // skip to next possible start of codepoint.
  148. retval = ( ((octet << 18)) | ((octet2 - 128) << 12) |
  149. ((octet3 - 128) << 6) | ((octet4 - 128)) );
  150. if ((retval >= 0x10000) && (retval <= 0x10FFFF))
  151. return retval;
  152. }
  153. // Five and six octet sequences became illegal in rfc3629.
  154. // We throw the codepoint away, but parse them to make sure we move
  155. // ahead the right number of bytes and don't overflow the buffer.
  156. else if (octet < 252) // five octets
  157. {
  158. octet = (uint32_t) ((uint8_t) *(++str));
  159. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  160. return UNICODE_BOGUS_CHAR_VALUE;
  161. octet = (uint32_t) ((uint8_t) *(++str));
  162. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  163. return UNICODE_BOGUS_CHAR_VALUE;
  164. octet = (uint32_t) ((uint8_t) *(++str));
  165. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  166. return UNICODE_BOGUS_CHAR_VALUE;
  167. octet = (uint32_t) ((uint8_t) *(++str));
  168. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  169. return UNICODE_BOGUS_CHAR_VALUE;
  170. *_str += 5; // skip to next possible start of codepoint.
  171. return UNICODE_BOGUS_CHAR_VALUE;
  172. }
  173. else // six octets
  174. {
  175. octet = (uint32_t) ((uint8_t) *(++str));
  176. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  177. return UNICODE_BOGUS_CHAR_VALUE;
  178. octet = (uint32_t) ((uint8_t) *(++str));
  179. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  180. return UNICODE_BOGUS_CHAR_VALUE;
  181. octet = (uint32_t) ((uint8_t) *(++str));
  182. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  183. return UNICODE_BOGUS_CHAR_VALUE;
  184. octet = (uint32_t) ((uint8_t) *(++str));
  185. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  186. return UNICODE_BOGUS_CHAR_VALUE;
  187. octet = (uint32_t) ((uint8_t) *(++str));
  188. if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
  189. return UNICODE_BOGUS_CHAR_VALUE;
  190. *_str += 6; // skip to next possible start of codepoint.
  191. return UNICODE_BOGUS_CHAR_VALUE;
  192. }
  193. return UNICODE_BOGUS_CHAR_VALUE;
  194. }
  195. typedef struct CaseFoldMapping
  196. {
  197. uint32_t from;
  198. uint32_t to0;
  199. uint32_t to1;
  200. uint32_t to2;
  201. } CaseFoldMapping;
  202. typedef struct CaseFoldHashBucket
  203. {
  204. const uint8_t count;
  205. const CaseFoldMapping *list;
  206. } CaseFoldHashBucket;
  207. #include "pathmatch_casefolding.h"
  208. inline __attribute__ ((always_inline)) static void locate_case_fold_mapping(const uint32_t from, uint32_t *to)
  209. {
  210. const uint8_t hashed = ((from ^ (from >> 8)) & 0xFF);
  211. const CaseFoldHashBucket *bucket = &case_fold_hash[hashed];
  212. const CaseFoldMapping *mapping = bucket->list;
  213. uint32_t i;
  214. for (i = 0; i < bucket->count; i++, mapping++)
  215. {
  216. if (mapping->from == from)
  217. {
  218. to[0] = mapping->to0;
  219. to[1] = mapping->to1;
  220. to[2] = mapping->to2;
  221. return;
  222. }
  223. }
  224. // Not found...there's no remapping for this codepoint.
  225. to[0] = from;
  226. to[1] = 0;
  227. to[2] = 0;
  228. }
  229. inline __attribute__ ((always_inline)) static uint32_t *fold_utf8(const char *str)
  230. {
  231. uint32_t *retval = new uint32_t[(strlen(str) * 3) + 1];
  232. uint32_t *dst = retval;
  233. while (*str)
  234. {
  235. const char ch = *str;
  236. if (ch & 0x80) // high bit set? UTF-8 sequence!
  237. {
  238. uint32_t fold[3];
  239. locate_case_fold_mapping(utf8codepoint(&str), fold);
  240. *(dst++) = fold[0];
  241. if (fold[1])
  242. {
  243. *(dst++) = fold[1];
  244. if (fold[2])
  245. *(dst++) = fold[2];
  246. }
  247. }
  248. else // simple ASCII test.
  249. {
  250. *(dst++) = (uint32_t) (((ch >= 'A') && (ch <= 'Z')) ? ch + 32 : ch);
  251. str++;
  252. }
  253. }
  254. *dst = 0;
  255. return retval;
  256. }
  257. inline __attribute__ ((always_inline)) static int utf8casecmp_loop(const uint32_t *folded1, const uint32_t *folded2)
  258. {
  259. while (true)
  260. {
  261. const uint32_t ch1 = *(folded1++);
  262. const uint32_t ch2 = *(folded2++);
  263. if (ch1 < ch2)
  264. return -1;
  265. else if (ch1 > ch2)
  266. return 1;
  267. else if (ch1 == 0)
  268. return 0; // complete match.
  269. }
  270. }
  271. #ifdef UTF8_PATHMATCH
  272. static int utf8casecmp(const char *str1, const char *str2)
  273. {
  274. uint32_t *folded1 = fold_utf8(str1);
  275. uint32_t *folded2 = fold_utf8(str2);
  276. const int retval = utf8casecmp_loop(folded1, folded2);
  277. delete[] folded1;
  278. delete[] folded2;
  279. return retval;
  280. }
  281. #endif
  282. // Simple object to help make sure a DIR* from opendir
  283. // gets closed when it goes out of scope.
  284. class CDirPtr
  285. {
  286. public:
  287. CDirPtr() { m_pDir = NULL; }
  288. CDirPtr( DIR *pDir ) : m_pDir(pDir) {}
  289. ~CDirPtr() { Close(); }
  290. void operator=(DIR *pDir) { Close(); m_pDir = pDir; }
  291. operator DIR *() { return m_pDir; }
  292. operator bool() { return m_pDir != NULL; }
  293. private:
  294. void Close() { if ( m_pDir ) closedir( m_pDir ); }
  295. DIR *m_pDir;
  296. };
  297. // Object used to temporarily slice a path into a smaller componentent
  298. // and then repair it when going out of scope. Typically used as an unnamed
  299. // temp object that is a parameter to a function.
  300. class CDirTrimmer
  301. {
  302. public:
  303. CDirTrimmer( char * pPath, size_t nTrimIdx )
  304. {
  305. m_pPath = pPath;
  306. m_idx = nTrimIdx;
  307. m_c = m_pPath[nTrimIdx];
  308. m_pPath[nTrimIdx] = '\0';
  309. }
  310. ~CDirTrimmer() { m_pPath[m_idx] = m_c; }
  311. operator const char *() { return m_pPath; }
  312. private:
  313. size_t m_idx;
  314. char *m_pPath;
  315. char m_c;
  316. };
  317. enum PathMod_t
  318. {
  319. kPathUnchanged,
  320. kPathLowered,
  321. kPathChanged,
  322. kPathFailed,
  323. };
  324. static bool Descend( char *pPath, size_t nStartIdx, bool bAllowBasenameMismatch, size_t nLevel = 0 )
  325. {
  326. DEBUG_MSG( "(%zu) Descend: %s, (%s), %s\n", nLevel, pPath, pPath+nStartIdx, bAllowBasenameMismatch ? "true" : "false " );
  327. // We assume up through nStartIdx is valid and matching
  328. size_t nNextSlash = nStartIdx+1;
  329. // path might be a dir
  330. if ( pPath[nNextSlash] == '\0' )
  331. {
  332. return true;
  333. }
  334. bool bIsDir = false; // is the new component a directory for certain?
  335. while ( pPath[nNextSlash] != '\0' && pPath[nNextSlash] != '/' )
  336. {
  337. nNextSlash++;
  338. }
  339. // Modify the pPath string
  340. if ( pPath[nNextSlash] == '/' )
  341. bIsDir = true;
  342. // See if we have an immediate match
  343. if ( __real_access( CDirTrimmer(pPath, nNextSlash), F_OK ) == 0 )
  344. {
  345. if ( !bIsDir )
  346. return true;
  347. bool bRet = Descend( pPath, nNextSlash, bAllowBasenameMismatch, nLevel+1 );
  348. if ( bRet )
  349. return true;
  350. }
  351. // Start enumerating dirents
  352. CDirPtr spDir;
  353. if ( nStartIdx )
  354. {
  355. // we have a path
  356. spDir = __real_opendir( CDirTrimmer( pPath, nStartIdx ) );
  357. nStartIdx++;
  358. }
  359. else
  360. {
  361. // we either start at root or cwd
  362. const char *pRoot = ".";
  363. if ( *pPath == '/' )
  364. {
  365. pRoot = "/";
  366. nStartIdx++;
  367. }
  368. spDir = __real_opendir( pRoot );
  369. }
  370. errno = 0;
  371. struct dirent *pEntry = spDir ? readdir( spDir ) : NULL;
  372. char *pszComponent = pPath + nStartIdx;
  373. size_t cbComponent = nNextSlash - nStartIdx;
  374. while ( pEntry )
  375. {
  376. DEBUG_MSG( "\t(%zu) comparing %s with %s\n", nLevel, pEntry->d_name, (const char *)CDirTrimmer(pszComponent, cbComponent) );
  377. // the candidate must match the target, but not be a case-identical match (we would
  378. // have looked there in the short-circuit code above, so don't look again)
  379. bool bMatches = ( strcasecmp( CDirTrimmer(pszComponent, cbComponent), pEntry->d_name ) == 0 &&
  380. strcmp( CDirTrimmer(pszComponent, cbComponent), pEntry->d_name ) != 0 );
  381. if ( bMatches )
  382. {
  383. char *pSrc = pEntry->d_name;
  384. char *pDst = &pPath[nStartIdx];
  385. // found a match; copy it in.
  386. while ( *pSrc && (*pSrc != '/') )
  387. {
  388. *pDst++ = *pSrc++;
  389. }
  390. if ( !bIsDir )
  391. return true;
  392. if ( Descend( pPath, nNextSlash, bAllowBasenameMismatch, nLevel+1 ) )
  393. return true;
  394. // If descend fails, try more directories
  395. }
  396. pEntry = readdir( spDir );
  397. }
  398. if ( bIsDir )
  399. {
  400. DEBUG_MSG( "(%zu) readdir failed to find '%s' in '%s'\n", nLevel, (const char *)CDirTrimmer(pszComponent, cbComponent), (const char *)CDirTrimmer( pPath, nStartIdx ) );
  401. }
  402. // Sometimes it's ok for the filename portion to not match
  403. // since we might be opening for write. Note that if
  404. // the filename matches case insensitive, that will be
  405. // preferred over preserving the input name
  406. if ( !bIsDir && bAllowBasenameMismatch )
  407. return true;
  408. return false;
  409. }
  410. #ifdef DO_PATHMATCH_CACHE
  411. typedef std::map<std::string, std::pair<std::string, time_t> > resultCache_t;
  412. typedef std::map<std::string, std::pair<std::string, time_t> >::iterator resultCacheItr_t;
  413. static resultCache_t resultCache;
  414. static const int k_cMaxCacheLifetimeSeconds = 2;
  415. #endif // DO_PATHMATCH_CACHE
  416. PathMod_t pathmatch( const char *pszIn, char **ppszOut, bool bAllowBasenameMismatch, char *pszOutBuf, size_t OutBufLen )
  417. {
  418. // Path matching can be very expensive, and the cost is unpredictable because it
  419. // depends on how many files are in directories on a user's machine. Therefore
  420. // it should be disabled whenever possible, and only enabled in environments (such
  421. // as running with loose files such as out of Perforce) where it is needed.
  422. static const char *s_pszPathMatchEnabled = getenv("ENABLE_PATHMATCH");
  423. if ( !s_pszPathMatchEnabled )
  424. return kPathUnchanged;
  425. static const char *s_pszDbgPathMatch = getenv("DBG_PATHMATCH");
  426. s_bShowDiag = ( s_pszDbgPathMatch != NULL );
  427. *ppszOut = NULL;
  428. if ( __real_access( pszIn, F_OK ) == 0 )
  429. return kPathUnchanged;
  430. #ifdef DO_PATHMATCH_CACHE
  431. resultCacheItr_t cachedResult = resultCache.find( pszIn );
  432. if ( cachedResult != resultCache.end() )
  433. {
  434. unsigned int age = time( NULL ) - cachedResult->second.second;
  435. const char *pszResult = cachedResult->second.first.c_str();
  436. if ( pszResult[0] != '\0' || age <= k_cMaxCacheLifetimeSeconds )
  437. {
  438. if ( pszResult[0] != '\0' )
  439. {
  440. *ppszOut = strdup( pszResult );
  441. DEBUG_MSG( "Cached '%s' -> '%s'\n", pszIn, *ppszOut );
  442. return kPathChanged;
  443. }
  444. else
  445. {
  446. DEBUG_MSG( "Cached '%s' -> kPathFailed\n", pszIn );
  447. return kPathFailed;
  448. }
  449. }
  450. else if ( age <= k_cMaxCacheLifetimeSeconds )
  451. {
  452. DEBUG_MSG( "Rechecking '%s' - cache is %u seconds old\n", pszIn, age );
  453. }
  454. }
  455. #endif // DO_PATHMATCH_CACHE
  456. char *pPath;
  457. if( strlen( pszIn ) >= OutBufLen )
  458. {
  459. pPath = strdup( pszIn );
  460. }
  461. else
  462. {
  463. strncpy( pszOutBuf, pszIn, OutBufLen );
  464. pPath = pszOutBuf;
  465. }
  466. if ( pPath )
  467. {
  468. // I believe this code is broken. I'm guessing someone wanted to avoid lowercasing
  469. // the path before the steam directory - but it's actually skipping lowercasing
  470. // whenever steam is found anywhere - including the filename. For example,
  471. // /home/mikesart/valvesrc/console/l4d2/game/left4dead2_dlc1/particles/steam_fx.pcf
  472. // winds up only having the "steam_fx.pcf" portion lowercased.
  473. #ifdef NEVER
  474. // optimization, if the path contained steam somewhere
  475. // assume the path up through the component with 'steam' in
  476. // is valid (because we almost certainly obtained it
  477. // progamatically
  478. char *p = strcasestr( pPath, "steam" );
  479. if ( p )
  480. {
  481. while ( p > pPath )
  482. {
  483. if ( p[-1] == '/' )
  484. break;
  485. p--;
  486. }
  487. if ( ( p == pPath+1 ) && ( *pPath != '/' ) )
  488. p = pPath;
  489. }
  490. else
  491. {
  492. p = pPath;
  493. }
  494. #else
  495. char *p = pPath;
  496. #endif
  497. // Try the lower casing of the remaining path
  498. char *pBasename = p;
  499. while ( *p )
  500. {
  501. if ( *p == '/' )
  502. pBasename = p+1;
  503. *p = tolower(*p);
  504. p++;
  505. }
  506. if ( __real_access( pPath, F_OK ) == 0 )
  507. {
  508. *ppszOut = pPath;
  509. DEBUG_MSG( "Lowered '%s' -> '%s'\n", pszIn, pPath );
  510. return kPathLowered;
  511. }
  512. // path didn't match lowered successfully, restore the basename
  513. // if bAllowBasenameMismatch was true
  514. if ( bAllowBasenameMismatch )
  515. {
  516. const char *pSrc = pszIn + (pBasename - pPath);
  517. while ( *pBasename )
  518. {
  519. *pBasename++ = *pSrc++;
  520. }
  521. }
  522. if ( s_pszDbgPathMatch && strcasestr( s_pszDbgPathMatch, pszIn ) )
  523. {
  524. DEBUG_MSG( "Breaking '%s' in '%s'\n", pszIn, s_pszDbgPathMatch );
  525. DEBUG_BREAK();
  526. }
  527. bool bSuccess = Descend( pPath, 0, bAllowBasenameMismatch );
  528. if ( bSuccess )
  529. {
  530. *ppszOut = pPath;
  531. DEBUG_MSG( "Matched '%s' -> '%s'\n", pszIn, pPath );
  532. }
  533. else
  534. {
  535. DEBUG_MSG( "Unmatched %s\n", pszIn );
  536. }
  537. #ifndef DO_PATHMATCH_CACHE
  538. return bSuccess ? kPathChanged : kPathFailed;
  539. #else
  540. time_t now = time(NULL);
  541. if ( bSuccess )
  542. {
  543. resultCache[ pszIn ] = std::make_pair( *ppszOut, now );
  544. return kPathChanged;
  545. }
  546. else
  547. {
  548. resultCache[ pszIn ] = std::make_pair( "", now );
  549. return kPathFailed;
  550. }
  551. #endif
  552. }
  553. return kPathFailed;
  554. }
  555. // Wrapper object that manages the 'typical' usage cases of pathmatch()
  556. class CWrap
  557. {
  558. public:
  559. CWrap( const char *pSuppliedPath, bool bAllowMismatchedBasename )
  560. : m_pSuppliedPath( pSuppliedPath ), m_pBestMatch( NULL )
  561. {
  562. m_eResult = pathmatch( m_pSuppliedPath, &m_pBestMatch, bAllowMismatchedBasename, m_BestMatchBuf, sizeof( m_BestMatchBuf ) );
  563. if ( m_pBestMatch == NULL )
  564. {
  565. m_pBestMatch = const_cast<char*>( m_pSuppliedPath );
  566. }
  567. }
  568. ~CWrap()
  569. {
  570. if ( ( m_pBestMatch != m_pSuppliedPath ) && ( m_pBestMatch != m_BestMatchBuf ) )
  571. free( m_pBestMatch );
  572. }
  573. const char *GetBest() const { return m_pBestMatch; }
  574. const char *GetOriginal() const { return m_pSuppliedPath; }
  575. PathMod_t GetMatchResult() const { return m_eResult; }
  576. operator const char*() { return GetBest(); }
  577. private:
  578. const char *m_pSuppliedPath;
  579. char *m_pBestMatch;
  580. char m_BestMatchBuf[ 512 ];
  581. PathMod_t m_eResult;
  582. };
  583. #ifdef MAIN_TEST
  584. void usage()
  585. {
  586. puts("pathmatch [options] <path>");
  587. //puts("options:");
  588. //puts("\t");
  589. exit(-1);
  590. }
  591. void test( const char *pszFile, bool bAllowBasenameMismatch )
  592. {
  593. char *pNewPath;
  594. char NewPathBuf[ 512 ];
  595. PathMod_t nStat = pathmatch( pszFile, &pNewPath, bAllowBasenameMismatch, NewPathBuf, sizeof( NewPathBuf ) );
  596. printf("AllowMismatchedBasename: %s\n", bAllowBasenameMismatch ? "true" : "false" );
  597. printf("Path Was: ");
  598. switch ( nStat )
  599. {
  600. case kPathUnchanged:
  601. puts("kPathUnchanged");
  602. break;
  603. case kPathLowered:
  604. puts("kPathLowered");
  605. break;
  606. case kPathChanged:
  607. puts("kPathChanged");
  608. break;
  609. case kPathFailed:
  610. puts("kPathFailed");
  611. break;
  612. }
  613. printf(" Path In: %s\n", pszFile );
  614. printf("Path Out: %s\n", nStat == kPathUnchanged ? pszFile : pNewPath );
  615. if ( pNewPath )
  616. free( pNewPath );
  617. }
  618. int
  619. main(int argc, char **argv)
  620. {
  621. if ( argc <= 1 || argc > 2 )
  622. usage();
  623. test( argv[1], false );
  624. test( argv[1], true );
  625. return 0;
  626. }
  627. #endif
  628. extern "C" {
  629. WRAP(freopen, FILE *, const char *path, const char *mode, FILE *stream)
  630. {
  631. // if mode does not have w, a, or +, it's open for read.
  632. bool bAllowBasenameMismatch = strpbrk( mode, "wa+" ) != NULL;
  633. CWrap mpath( path, bAllowBasenameMismatch );
  634. return CALL(freopen)( mpath, mode, stream );
  635. }
  636. WRAP(fopen, FILE *, const char *path, const char *mode)
  637. {
  638. // if mode does not have w, a, or +, it's open for read.
  639. bool bAllowBasenameMismatch = strpbrk( mode, "wa+" ) != NULL;
  640. CWrap mpath( path, bAllowBasenameMismatch );
  641. return CALL(fopen)( mpath, mode );
  642. }
  643. WRAP(fopen64, FILE *, const char *path, const char *mode)
  644. {
  645. // if mode does not have w, a, or +, it's open for read.
  646. bool bAllowBasenameMismatch = strpbrk( mode, "wa+" ) != NULL;
  647. CWrap mpath( path, bAllowBasenameMismatch );
  648. return CALL(fopen64)( mpath, mode );
  649. }
  650. WRAP(open, int, const char *pathname, int flags, mode_t mode)
  651. {
  652. bool bAllowBasenameMismatch = ((flags & (O_WRONLY | O_RDWR)) != 0);
  653. CWrap mpath( pathname, bAllowBasenameMismatch );
  654. return CALL(open)( mpath, flags, mode );
  655. }
  656. WRAP(open64, int, const char *pathname, int flags, mode_t mode)
  657. {
  658. bool bAllowBasenameMismatch = ((flags & (O_WRONLY | O_RDWR)) != 0);
  659. CWrap mpath( pathname, bAllowBasenameMismatch );
  660. return CALL(open64)( mpath, flags, mode );
  661. }
  662. int __wrap_creat(const char *pathname, mode_t mode)
  663. {
  664. return __wrap_open( pathname, O_CREAT|O_WRONLY|O_TRUNC, mode );
  665. }
  666. int __wrap_access(const char *pathname, int mode)
  667. {
  668. return __real_access( CWrap( pathname, false ), mode );
  669. }
  670. WRAP(stat, int, const char *path, struct stat *buf)
  671. {
  672. return CALL(stat)( CWrap( path, false ), buf );
  673. }
  674. WRAP(lstat, int, const char *path, struct stat *buf)
  675. {
  676. return CALL(lstat)( CWrap( path, false ), buf );
  677. }
  678. WRAP(scandir, int, const char *dirp, struct dirent ***namelist,
  679. int (*filter)(const struct dirent *),
  680. int (*compar)(const struct dirent **, const struct dirent **))
  681. {
  682. return CALL(scandir)( CWrap( dirp, false ), namelist, filter, compar );
  683. }
  684. WRAP(opendir, DIR*, const char *name)
  685. {
  686. return CALL(opendir)( CWrap( name, false ) );
  687. }
  688. WRAP(__xstat, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  689. {
  690. return CALL(__xstat)( __ver, CWrap( __filename, false), __stat_buf );
  691. }
  692. WRAP(__lxstat, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  693. {
  694. return CALL(__lxstat)( __ver, CWrap( __filename, false), __stat_buf );
  695. }
  696. WRAP(__xstat64, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  697. {
  698. return CALL(__xstat64)( __ver, CWrap( __filename, false), __stat_buf );
  699. }
  700. WRAP(__lxstat64, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  701. {
  702. return CALL(__lxstat64)( __ver, CWrap( __filename, false), __stat_buf );
  703. }
  704. WRAP(chmod, int, const char *path, mode_t mode)
  705. {
  706. return CALL(chmod)( CWrap( path, false), mode );
  707. }
  708. WRAP(chown, int, const char *path, uid_t owner, gid_t group)
  709. {
  710. return CALL(chown)( CWrap( path, false), owner, group );
  711. }
  712. WRAP(lchown, int, const char *path, uid_t owner, gid_t group)
  713. {
  714. return CALL(lchown)( CWrap( path, false), owner, group );
  715. }
  716. WRAP(symlink, int, const char *oldpath, const char *newpath)
  717. {
  718. return CALL(symlink)( CWrap( oldpath, false), CWrap( newpath, true ) );
  719. }
  720. WRAP(link, int, const char *oldpath, const char *newpath)
  721. {
  722. return CALL(link)( CWrap( oldpath, false), CWrap( newpath, true ) );
  723. }
  724. WRAP(mknod, int, const char *pathname, mode_t mode, dev_t dev)
  725. {
  726. return CALL(mknod)( CWrap( pathname, true), mode, dev );
  727. }
  728. WRAP(mount, int, const char *source, const char *target,
  729. const char *filesystemtype, unsigned long mountflags,
  730. const void *data)
  731. {
  732. return CALL(mount)( CWrap( source, false ), CWrap( target, false ), filesystemtype, mountflags, data );
  733. }
  734. WRAP(unlink, int, const char *pathname)
  735. {
  736. return CALL(unlink)( CWrap( pathname, false ) );
  737. }
  738. WRAP(mkfifo, int, const char *pathname, mode_t mode)
  739. {
  740. return CALL(mkfifo)( CWrap( pathname, true ), mode );
  741. }
  742. WRAP(rename, int, const char *oldpath, const char *newpath)
  743. {
  744. return CALL(rename)( CWrap( oldpath, false), CWrap( newpath, true ) );
  745. }
  746. WRAP(utime, int, const char *filename, const struct utimbuf *times)
  747. {
  748. return CALL(utime)( CWrap( filename, false), times );
  749. }
  750. WRAP(utimes, int, const char *filename, const struct timeval times[2])
  751. {
  752. return CALL(utimes)( CWrap( filename, false), times );
  753. }
  754. WRAP(realpath, char *, const char *path, char *resolved_path)
  755. {
  756. return CALL(realpath)( CWrap( path, true ), resolved_path );
  757. }
  758. WRAP(mkdir, int, const char *pathname, mode_t mode)
  759. {
  760. return CALL(mkdir)( CWrap( pathname, true ), mode );
  761. }
  762. WRAP(rmdir, char *, const char *pathname)
  763. {
  764. return CALL(rmdir)( CWrap( pathname, false ) );
  765. }
  766. };
  767. #endif