Counter Strike : Global Offensive Source Code
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.

898 lines
24 KiB

  1. //========= Copyright (c), Valve LLC, 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. static int utf8casecmp(const char *str1, const char *str2)
  272. {
  273. uint32_t *folded1 = fold_utf8(str1);
  274. uint32_t *folded2 = fold_utf8(str2);
  275. const int retval = utf8casecmp_loop(folded1, folded2);
  276. delete[] folded1;
  277. delete[] folded2;
  278. return retval;
  279. }
  280. // Simple object to help make sure a DIR* from opendir
  281. // gets closed when it goes out of scope.
  282. class CDirPtr
  283. {
  284. public:
  285. CDirPtr() { m_pDir = NULL; }
  286. CDirPtr( DIR *pDir ) : m_pDir(pDir) {}
  287. ~CDirPtr() { Close(); }
  288. void operator=(DIR *pDir) { Close(); m_pDir = pDir; }
  289. operator DIR *() { return m_pDir; }
  290. operator bool() { return m_pDir != NULL; }
  291. private:
  292. void Close() { if ( m_pDir ) closedir( m_pDir ); }
  293. DIR *m_pDir;
  294. };
  295. // Object used to temporarily slice a path into a smaller componentent
  296. // and then repair it when going out of scope. Typically used as an unnamed
  297. // temp object that is a parameter to a function.
  298. class CDirTrimmer
  299. {
  300. public:
  301. CDirTrimmer( char * pPath, size_t nTrimIdx )
  302. {
  303. m_pPath = pPath;
  304. m_idx = nTrimIdx;
  305. m_c = m_pPath[nTrimIdx];
  306. m_pPath[nTrimIdx] = '\0';
  307. }
  308. ~CDirTrimmer() { m_pPath[m_idx] = m_c; }
  309. operator const char *() { return m_pPath; }
  310. private:
  311. size_t m_idx;
  312. char *m_pPath;
  313. char m_c;
  314. };
  315. enum PathMod_t
  316. {
  317. kPathUnchanged,
  318. kPathLowered,
  319. kPathChanged,
  320. kPathFailed,
  321. };
  322. static bool Descend( char *pPath, size_t nStartIdx, bool bAllowBasenameMismatch, size_t nLevel = 0 )
  323. {
  324. DEBUG_MSG( "(%zu) Descend: %s, (%s), %s\n", nLevel, pPath, pPath+nStartIdx, bAllowBasenameMismatch ? "true" : "false " );
  325. // We assume up through nStartIdx is valid and matching
  326. size_t nNextSlash = nStartIdx+1;
  327. // path might be a dir
  328. if ( pPath[nNextSlash] == '\0' )
  329. {
  330. return true;
  331. }
  332. bool bIsDir = false; // is the new component a directory for certain?
  333. while ( pPath[nNextSlash] != '\0' && pPath[nNextSlash] != '/' )
  334. {
  335. nNextSlash++;
  336. }
  337. // Modify the pPath string
  338. if ( pPath[nNextSlash] == '/' )
  339. bIsDir = true;
  340. // See if we have an immediate match
  341. if ( __real_access( CDirTrimmer(pPath, nNextSlash), F_OK ) == 0 )
  342. {
  343. if ( !bIsDir )
  344. return true;
  345. bool bRet = Descend( pPath, nNextSlash, bAllowBasenameMismatch, nLevel+1 );
  346. if ( bRet )
  347. return true;
  348. }
  349. // Start enumerating dirents
  350. CDirPtr spDir;
  351. if ( nStartIdx )
  352. {
  353. // we have a path
  354. spDir = __real_opendir( CDirTrimmer( pPath, nStartIdx ) );
  355. nStartIdx++;
  356. }
  357. else
  358. {
  359. // we either start at root or cwd
  360. const char *pRoot = ".";
  361. if ( *pPath == '/' )
  362. {
  363. pRoot = "/";
  364. nStartIdx++;
  365. }
  366. spDir = __real_opendir( pRoot );
  367. }
  368. errno = 0;
  369. struct dirent *pEntry = spDir ? readdir( spDir ) : NULL;
  370. char *pszComponent = pPath + nStartIdx;
  371. size_t cbComponent = nNextSlash - nStartIdx;
  372. while ( pEntry )
  373. {
  374. DEBUG_MSG( "\t(%zu) comparing %s with %s\n", nLevel, pEntry->d_name, (const char *)CDirTrimmer(pszComponent, cbComponent) );
  375. // the candidate must match the target, but not be a case-identical match (we would
  376. // have looked there in the short-circuit code above, so don't look again)
  377. bool bMatches = ( strcasecmp( CDirTrimmer(pszComponent, cbComponent), pEntry->d_name ) == 0 &&
  378. strcmp( CDirTrimmer(pszComponent, cbComponent), pEntry->d_name ) != 0 );
  379. if ( bMatches )
  380. {
  381. char *pSrc = pEntry->d_name;
  382. char *pDst = &pPath[nStartIdx];
  383. // found a match; copy it in.
  384. while ( *pSrc && (*pSrc != '/') )
  385. {
  386. *pDst++ = *pSrc++;
  387. }
  388. if ( !bIsDir )
  389. return true;
  390. if ( Descend( pPath, nNextSlash, bAllowBasenameMismatch, nLevel+1 ) )
  391. return true;
  392. // If descend fails, try more directories
  393. }
  394. pEntry = readdir( spDir );
  395. }
  396. if ( bIsDir )
  397. {
  398. DEBUG_MSG( "(%zu) readdir failed to find '%s' in '%s'\n", nLevel, (const char *)CDirTrimmer(pszComponent, cbComponent), (const char *)CDirTrimmer( pPath, nStartIdx ) );
  399. }
  400. // Sometimes it's ok for the filename portion to not match
  401. // since we might be opening for write. Note that if
  402. // the filename matches case insensitive, that will be
  403. // preferred over preserving the input name
  404. if ( !bIsDir && bAllowBasenameMismatch )
  405. return true;
  406. return false;
  407. }
  408. #ifdef DO_PATHMATCH_CACHE
  409. typedef std::map<std::string, std::pair<std::string, time_t> > resultCache_t;
  410. typedef std::map<std::string, std::pair<std::string, time_t> >::iterator resultCacheItr_t;
  411. static resultCache_t resultCache;
  412. static const int k_cMaxCacheLifetimeSeconds = 2;
  413. #endif // DO_PATHMATCH_CACHE
  414. PathMod_t pathmatch( const char *pszIn, char **ppszOut, bool bAllowBasenameMismatch, char *pszOutBuf, size_t OutBufLen )
  415. {
  416. static const char *s_pszDbgPathMatch = getenv("DBG_PATHMATCH");
  417. s_bShowDiag = ( s_pszDbgPathMatch != NULL );
  418. *ppszOut = NULL;
  419. if ( __real_access( pszIn, F_OK ) == 0 )
  420. return kPathUnchanged;
  421. #ifdef DO_PATHMATCH_CACHE
  422. resultCacheItr_t cachedResult = resultCache.find( pszIn );
  423. if ( cachedResult != resultCache.end() )
  424. {
  425. unsigned int age = time( NULL ) - cachedResult->second.second;
  426. const char *pszResult = cachedResult->second.first.c_str();
  427. if ( pszResult[0] != '\0' || age <= k_cMaxCacheLifetimeSeconds )
  428. {
  429. if ( pszResult[0] != '\0' )
  430. {
  431. *ppszOut = strdup( pszResult );
  432. DEBUG_MSG( "Cached '%s' -> '%s'\n", pszIn, *ppszOut );
  433. return kPathChanged;
  434. }
  435. else
  436. {
  437. DEBUG_MSG( "Cached '%s' -> kPathFailed\n", pszIn );
  438. return kPathFailed;
  439. }
  440. }
  441. else if ( age <= k_cMaxCacheLifetimeSeconds )
  442. {
  443. DEBUG_MSG( "Rechecking '%s' - cache is %u seconds old\n", pszIn, age );
  444. }
  445. }
  446. #endif // DO_PATHMATCH_CACHE
  447. char *pPath;
  448. if( strlen( pszIn ) >= OutBufLen )
  449. {
  450. pPath = strdup( pszIn );
  451. }
  452. else
  453. {
  454. strncpy( pszOutBuf, pszIn, OutBufLen );
  455. pPath = pszOutBuf;
  456. }
  457. if ( pPath )
  458. {
  459. // I believe this code is broken. I'm guessing someone wanted to avoid lowercasing
  460. // the path before the steam directory - but it's actually skipping lowercasing
  461. // whenever steam is found anywhere - including the filename. For example,
  462. // /home/mikesart/valvesrc/console/l4d2/game/left4dead2_dlc1/particles/steam_fx.pcf
  463. // winds up only having the "steam_fx.pcf" portion lowercased.
  464. #ifdef NEVER
  465. // optimization, if the path contained steam somewhere
  466. // assume the path up through the component with 'steam' in
  467. // is valid (because we almost certainly obtained it
  468. // progamatically
  469. char *p = strcasestr( pPath, "steam" );
  470. if ( p )
  471. {
  472. while ( p > pPath )
  473. {
  474. if ( p[-1] == '/' )
  475. break;
  476. p--;
  477. }
  478. if ( ( p == pPath+1 ) && ( *pPath != '/' ) )
  479. p = pPath;
  480. }
  481. else
  482. {
  483. p = pPath;
  484. }
  485. #else
  486. char *p = pPath;
  487. #endif
  488. // Try the lower casing of the remaining path
  489. char *pBasename = p;
  490. while ( *p )
  491. {
  492. if ( *p == '/' )
  493. pBasename = p+1;
  494. *p = tolower(*p);
  495. p++;
  496. }
  497. if ( __real_access( pPath, F_OK ) == 0 )
  498. {
  499. *ppszOut = pPath;
  500. DEBUG_MSG( "Lowered '%s' -> '%s'\n", pszIn, pPath );
  501. return kPathLowered;
  502. }
  503. // path didn't match lowered successfully, restore the basename
  504. // if bAllowBasenameMismatch was true
  505. if ( bAllowBasenameMismatch )
  506. {
  507. const char *pSrc = pszIn + (pBasename - pPath);
  508. while ( *pBasename )
  509. {
  510. *pBasename++ = *pSrc++;
  511. }
  512. }
  513. if ( s_pszDbgPathMatch && strcasestr( s_pszDbgPathMatch, pszIn ) )
  514. {
  515. DEBUG_MSG( "Breaking '%s' in '%s'\n", pszIn, s_pszDbgPathMatch );
  516. DEBUG_BREAK();
  517. }
  518. bool bSuccess = Descend( pPath, 0, bAllowBasenameMismatch );
  519. if ( bSuccess )
  520. {
  521. *ppszOut = pPath;
  522. DEBUG_MSG( "Matched '%s' -> '%s'\n", pszIn, pPath );
  523. }
  524. else
  525. {
  526. DEBUG_MSG( "Unmatched %s\n", pszIn );
  527. }
  528. #ifndef DO_PATHMATCH_CACHE
  529. return bSuccess ? kPathChanged : kPathFailed;
  530. #else
  531. time_t now = time(NULL);
  532. if ( bSuccess )
  533. {
  534. resultCache[ pszIn ] = std::make_pair( *ppszOut, now );
  535. return kPathChanged;
  536. }
  537. else
  538. {
  539. resultCache[ pszIn ] = std::make_pair( "", now );
  540. return kPathFailed;
  541. }
  542. #endif
  543. }
  544. return kPathFailed;
  545. }
  546. // Wrapper object that manages the 'typical' usage cases of pathmatch()
  547. class CWrap
  548. {
  549. public:
  550. CWrap( const char *pSuppliedPath, bool bAllowMismatchedBasename )
  551. : m_pSuppliedPath( pSuppliedPath ), m_pBestMatch( NULL )
  552. {
  553. m_eResult = pathmatch( m_pSuppliedPath, &m_pBestMatch, bAllowMismatchedBasename, m_BestMatchBuf, sizeof( m_BestMatchBuf ) );
  554. if ( m_pBestMatch == NULL )
  555. {
  556. m_pBestMatch = const_cast<char*>( m_pSuppliedPath );
  557. }
  558. }
  559. ~CWrap()
  560. {
  561. if ( ( m_pBestMatch != m_pSuppliedPath ) && ( m_pBestMatch != m_BestMatchBuf ) )
  562. free( m_pBestMatch );
  563. }
  564. const char *GetBest() const { return m_pBestMatch; }
  565. const char *GetOriginal() const { return m_pSuppliedPath; }
  566. PathMod_t GetMatchResult() const { return m_eResult; }
  567. operator const char*() { return GetBest(); }
  568. private:
  569. const char *m_pSuppliedPath;
  570. char *m_pBestMatch;
  571. char m_BestMatchBuf[ 512 ];
  572. PathMod_t m_eResult;
  573. };
  574. #ifdef MAIN_TEST
  575. void usage()
  576. {
  577. puts("pathmatch [options] <path>");
  578. //puts("options:");
  579. //puts("\t");
  580. exit(-1);
  581. }
  582. void test( const char *pszFile, bool bAllowBasenameMismatch )
  583. {
  584. char *pNewPath;
  585. char NewPathBuf[ 512 ];
  586. PathMod_t nStat = pathmatch( pszFile, &pNewPath, bAllowBasenameMismatch, NewPathBuf, sizeof( NewPathBuf ) );
  587. printf("AllowMismatchedBasename: %s\n", bAllowBasenameMismatch ? "true" : "false" );
  588. printf("Path Was: ");
  589. switch ( nStat )
  590. {
  591. case kPathUnchanged:
  592. puts("kPathUnchanged");
  593. break;
  594. case kPathLowered:
  595. puts("kPathLowered");
  596. break;
  597. case kPathChanged:
  598. puts("kPathChanged");
  599. break;
  600. case kPathFailed:
  601. puts("kPathFailed");
  602. break;
  603. }
  604. printf(" Path In: %s\n", pszFile );
  605. printf("Path Out: %s\n", nStat == kPathUnchanged ? pszFile : pNewPath );
  606. if ( pNewPath )
  607. free( pNewPath );
  608. }
  609. int
  610. main(int argc, char **argv)
  611. {
  612. if ( argc <= 1 || argc > 2 )
  613. usage();
  614. test( argv[1], false );
  615. test( argv[1], true );
  616. return 0;
  617. }
  618. #endif
  619. extern "C" {
  620. WRAP(freopen, FILE *, const char *path, const char *mode, FILE *stream)
  621. {
  622. // if mode does not have w, a, or +, it's open for read.
  623. bool bAllowBasenameMismatch = strpbrk( mode, "wa+" ) != NULL;
  624. CWrap mpath( path, bAllowBasenameMismatch );
  625. return CALL(freopen)( mpath, mode, stream );
  626. }
  627. WRAP(fopen, FILE *, const char *path, const char *mode)
  628. {
  629. // if mode does not have w, a, or +, it's open for read.
  630. bool bAllowBasenameMismatch = strpbrk( mode, "wa+" ) != NULL;
  631. CWrap mpath( path, bAllowBasenameMismatch );
  632. return CALL(fopen)( mpath, mode );
  633. }
  634. WRAP(fopen64, FILE *, const char *path, const char *mode)
  635. {
  636. // if mode does not have w, a, or +, it's open for read.
  637. bool bAllowBasenameMismatch = strpbrk( mode, "wa+" ) != NULL;
  638. CWrap mpath( path, bAllowBasenameMismatch );
  639. return CALL(fopen64)( mpath, mode );
  640. }
  641. WRAP(open, int, const char *pathname, int flags, mode_t mode)
  642. {
  643. bool bAllowBasenameMismatch = ((flags & (O_WRONLY | O_RDWR)) != 0);
  644. CWrap mpath( pathname, bAllowBasenameMismatch );
  645. return CALL(open)( mpath, flags, mode );
  646. }
  647. WRAP(open64, int, const char *pathname, int flags, mode_t mode)
  648. {
  649. bool bAllowBasenameMismatch = ((flags & (O_WRONLY | O_RDWR)) != 0);
  650. CWrap mpath( pathname, bAllowBasenameMismatch );
  651. return CALL(open64)( mpath, flags, mode );
  652. }
  653. int __wrap_creat(const char *pathname, mode_t mode)
  654. {
  655. return __wrap_open( pathname, O_CREAT|O_WRONLY|O_TRUNC, mode );
  656. }
  657. int __wrap_access(const char *pathname, int mode)
  658. {
  659. return __real_access( CWrap( pathname, false ), mode );
  660. }
  661. WRAP(stat, int, const char *path, struct stat *buf)
  662. {
  663. return CALL(stat)( CWrap( path, false ), buf );
  664. }
  665. WRAP(lstat, int, const char *path, struct stat *buf)
  666. {
  667. return CALL(lstat)( CWrap( path, false ), buf );
  668. }
  669. WRAP(scandir, int, const char *dirp, struct dirent ***namelist,
  670. int (*filter)(const struct dirent *),
  671. int (*compar)(const struct dirent **, const struct dirent **))
  672. {
  673. return CALL(scandir)( CWrap( dirp, false ), namelist, filter, compar );
  674. }
  675. WRAP(opendir, DIR*, const char *name)
  676. {
  677. return CALL(opendir)( CWrap( name, false ) );
  678. }
  679. WRAP(__xstat, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  680. {
  681. return CALL(__xstat)( __ver, CWrap( __filename, false), __stat_buf );
  682. }
  683. WRAP(__lxstat, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  684. {
  685. return CALL(__lxstat)( __ver, CWrap( __filename, false), __stat_buf );
  686. }
  687. WRAP(__xstat64, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  688. {
  689. return CALL(__xstat64)( __ver, CWrap( __filename, false), __stat_buf );
  690. }
  691. WRAP(__lxstat64, int, int __ver, __const char *__filename, struct stat *__stat_buf)
  692. {
  693. return CALL(__lxstat64)( __ver, CWrap( __filename, false), __stat_buf );
  694. }
  695. WRAP(chmod, int, const char *path, mode_t mode)
  696. {
  697. return CALL(chmod)( CWrap( path, false), mode );
  698. }
  699. WRAP(chown, int, const char *path, uid_t owner, gid_t group)
  700. {
  701. return CALL(chown)( CWrap( path, false), owner, group );
  702. }
  703. WRAP(lchown, int, const char *path, uid_t owner, gid_t group)
  704. {
  705. return CALL(lchown)( CWrap( path, false), owner, group );
  706. }
  707. WRAP(symlink, int, const char *oldpath, const char *newpath)
  708. {
  709. return CALL(symlink)( CWrap( oldpath, false), CWrap( newpath, true ) );
  710. }
  711. WRAP(link, int, const char *oldpath, const char *newpath)
  712. {
  713. return CALL(link)( CWrap( oldpath, false), CWrap( newpath, true ) );
  714. }
  715. WRAP(mknod, int, const char *pathname, mode_t mode, dev_t dev)
  716. {
  717. return CALL(mknod)( CWrap( pathname, true), mode, dev );
  718. }
  719. WRAP(mount, int, const char *source, const char *target,
  720. const char *filesystemtype, unsigned long mountflags,
  721. const void *data)
  722. {
  723. return CALL(mount)( CWrap( source, false ), CWrap( target, false ), filesystemtype, mountflags, data );
  724. }
  725. WRAP(unlink, int, const char *pathname)
  726. {
  727. return CALL(unlink)( CWrap( pathname, false ) );
  728. }
  729. WRAP(mkfifo, int, const char *pathname, mode_t mode)
  730. {
  731. return CALL(mkfifo)( CWrap( pathname, true ), mode );
  732. }
  733. WRAP(rename, int, const char *oldpath, const char *newpath)
  734. {
  735. return CALL(rename)( CWrap( oldpath, false), CWrap( newpath, true ) );
  736. }
  737. WRAP(utime, int, const char *filename, const struct utimbuf *times)
  738. {
  739. return CALL(utime)( CWrap( filename, false), times );
  740. }
  741. WRAP(utimes, int, const char *filename, const struct timeval times[2])
  742. {
  743. return CALL(utimes)( CWrap( filename, false), times );
  744. }
  745. WRAP(realpath, char *, const char *path, char *resolved_path)
  746. {
  747. return CALL(realpath)( CWrap( path, true ), resolved_path );
  748. }
  749. WRAP(mkdir, int, const char *pathname, mode_t mode)
  750. {
  751. return CALL(mkdir)( CWrap( pathname, true ), mode );
  752. }
  753. WRAP(rmdir, char *, const char *pathname)
  754. {
  755. return CALL(rmdir)( CWrap( pathname, false ) );
  756. }
  757. };
  758. #endif