//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Utility to interrogate and modify the data in the OSX IPC Server
//
// $NoKeywords: $
//=============================================================================
// README:README
//
// This file implements the --wrap for ld on linux that lets file i/o api's
// behave as if it were running on a case insensitive file system. Unfortunately,
// this is needed by both steam2 and steam3. It was decided to check the source
// into both locations, otherwise someone would find the .o and have no idea
// where to go for the source if it was in the 'other' tree. Also, because this
// needs to be linked into every elf binary, the .o is checked in for Steam3 so that it is
// always available. In Steam2 it sits with the PosixWin32.cpp implementation and gets
// compiled along side of it through the make system. If you are reading this in Steam3,
// you will probably want to actually make your changes in steam2 and do a baseless merge
// to the steam3 copy.
//
// HOWTO: Add a new function. Add the function with _WRAP to the makefiles as noted below.
// Add the implementation to pathmatch.cpp - probably mimicking the existing functions.
// Build steam2 and copy to matching steam3/client. Take the pathmatch.o from steam 2
// and check it in to steam3 (in the location noted below). Full rebuild (re-link really)
// of steam3. Test steam and check in.
//
// If you are looking at updating this file, please update the following as needed:
//
// STEAM2.../Projects/GazelleProto/Client/Engine/obj/RELEASE_NORMAL/libsteam_linux/Common/Misc/pathmatch.o
// This is where steam2 builds the pathmatch.o out to.
//
// STEAM2.../Projects/GazelleProto/Makefile.shlib.base - contains _WRAP references
// STEAM2.../Projects/Common/Misc/pathmatch.cpp - Where the source is checked in, keep in sync with:
// STEAM3.../src/common/pathmatch.cpp - should be identical to previous file, but discoverable in steam3.
// STEAM3.../src/lib/linux32/release/pathmatch.o - steam3 checked in version
// STEAM3.../src/devtools/makefile_base_posix.mak - look for the _WRAP references
# ifdef LINUX
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdint.h>
# include <strings.h>
# include <unistd.h>
# include <getopt.h>
# include <errno.h>
# include <signal.h>
# include <ctype.h>
# include <dirent.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <sys/mount.h>
# include <fcntl.h>
# include <utime.h>
# include <map>
# include <string>
# include <time.h>
// Enable to do pathmatch caching. Beware: this code isn't threadsafe.
// #define DO_PATHMATCH_CACHE
# ifdef UTF8_PATHMATCH
# define strcasecmp utf8casecmp
# endif
static bool s_bShowDiag ;
# define DEBUG_MSG( ... ) if ( s_bShowDiag ) fprintf( stderr, ##__VA_ARGS__ )
# ifdef POSIX
# include <signal.h>
# define DEBUG_BREAK() raise(SIGINT)
# elif !defined (__arm__)
# define DEBUG_BREAK() __asm__ __volatile__ ( "int $3" )
# else
# define DEBUG_BREAK()
# endif
# define _COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
# define WRAP( fn, ret, ... ) \
ret __real_ # # fn ( __VA_ARGS__ ) ; \
ret __wrap_ # # fn ( __VA_ARGS__ )
# define CALL( fn ) __real_##fn
// Needed by pathmatch code
extern " C " int __real_access ( const char * pathname , int mode ) ;
extern " C " DIR * __real_opendir ( const char * name ) ;
// UTF-8 work from PhysicsFS: http://icculus.org/physfs/
// Even if it wasn't under the zlib license, Ryan wrote all this code originally.
# define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF
# define UNICODE_BOGUS_CHAR_CODEPOINT '?'
inline __attribute__ ( ( always_inline ) ) static uint32_t utf8codepoint ( const char * * _str )
{
const char * str = * _str ;
uint32_t retval = 0 ;
uint32_t octet = ( uint32_t ) ( ( uint8_t ) * str ) ;
uint32_t octet2 , octet3 , octet4 ;
if ( octet = = 0 ) // null terminator, end of string.
return 0 ;
else if ( octet < 128 ) // one octet char: 0 to 127
{
( * _str ) + + ; // skip to next possible start of codepoint.
return octet ;
}
else if ( ( octet > 127 ) & & ( octet < 192 ) ) // bad (starts with 10xxxxxx).
{
// Apparently each of these is supposed to be flagged as a bogus
// char, instead of just resyncing to the next valid codepoint.
( * _str ) + + ; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_VALUE ;
}
else if ( octet < 224 ) // two octets
{
octet - = ( 128 + 64 ) ;
octet2 = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet2 & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
* _str + = 2 ; // skip to next possible start of codepoint.
retval = ( ( octet < < 6 ) | ( octet2 - 128 ) ) ;
if ( ( retval > = 0x80 ) & & ( retval < = 0x7FF ) )
return retval ;
}
else if ( octet < 240 ) // three octets
{
octet - = ( 128 + 64 + 32 ) ;
octet2 = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet2 & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet3 = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet3 & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
* _str + = 3 ; // skip to next possible start of codepoint.
retval = ( ( ( octet < < 12 ) ) | ( ( octet2 - 128 ) < < 6 ) | ( ( octet3 - 128 ) ) ) ;
// There are seven "UTF-16 surrogates" that are illegal in UTF-8.
switch ( retval )
{
case 0xD800 :
case 0xDB7F :
case 0xDB80 :
case 0xDBFF :
case 0xDC00 :
case 0xDF80 :
case 0xDFFF :
return UNICODE_BOGUS_CHAR_VALUE ;
}
// 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge.
if ( ( retval > = 0x800 ) & & ( retval < = 0xFFFD ) )
return retval ;
}
else if ( octet < 248 ) // four octets
{
octet - = ( 128 + 64 + 32 + 16 ) ;
octet2 = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet2 & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet3 = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet3 & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet4 = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet4 & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
* _str + = 4 ; // skip to next possible start of codepoint.
retval = ( ( ( octet < < 18 ) ) | ( ( octet2 - 128 ) < < 12 ) |
( ( octet3 - 128 ) < < 6 ) | ( ( octet4 - 128 ) ) ) ;
if ( ( retval > = 0x10000 ) & & ( retval < = 0x10FFFF ) )
return retval ;
}
// Five and six octet sequences became illegal in rfc3629.
// We throw the codepoint away, but parse them to make sure we move
// ahead the right number of bytes and don't overflow the buffer.
else if ( octet < 252 ) // five octets
{
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
* _str + = 5 ; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_VALUE ;
}
else // six octets
{
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
octet = ( uint32_t ) ( ( uint8_t ) * ( + + str ) ) ;
if ( ( octet & ( 128 + 64 ) ) ! = 128 ) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE ;
* _str + = 6 ; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_VALUE ;
}
return UNICODE_BOGUS_CHAR_VALUE ;
}
typedef struct CaseFoldMapping
{
uint32_t from ;
uint32_t to0 ;
uint32_t to1 ;
uint32_t to2 ;
} CaseFoldMapping ;
typedef struct CaseFoldHashBucket
{
const uint8_t count ;
const CaseFoldMapping * list ;
} CaseFoldHashBucket ;
# include "pathmatch_casefolding.h"
inline __attribute__ ( ( always_inline ) ) static void locate_case_fold_mapping ( const uint32_t from , uint32_t * to )
{
const uint8_t hashed = ( ( from ^ ( from > > 8 ) ) & 0xFF ) ;
const CaseFoldHashBucket * bucket = & case_fold_hash [ hashed ] ;
const CaseFoldMapping * mapping = bucket - > list ;
uint32_t i ;
for ( i = 0 ; i < bucket - > count ; i + + , mapping + + )
{
if ( mapping - > from = = from )
{
to [ 0 ] = mapping - > to0 ;
to [ 1 ] = mapping - > to1 ;
to [ 2 ] = mapping - > to2 ;
return ;
}
}
// Not found...there's no remapping for this codepoint.
to [ 0 ] = from ;
to [ 1 ] = 0 ;
to [ 2 ] = 0 ;
}
inline __attribute__ ( ( always_inline ) ) static uint32_t * fold_utf8 ( const char * str )
{
uint32_t * retval = new uint32_t [ ( strlen ( str ) * 3 ) + 1 ] ;
uint32_t * dst = retval ;
while ( * str )
{
const char ch = * str ;
if ( ch & 0x80 ) // high bit set? UTF-8 sequence!
{
uint32_t fold [ 3 ] ;
locate_case_fold_mapping ( utf8codepoint ( & str ) , fold ) ;
* ( dst + + ) = fold [ 0 ] ;
if ( fold [ 1 ] )
{
* ( dst + + ) = fold [ 1 ] ;
if ( fold [ 2 ] )
* ( dst + + ) = fold [ 2 ] ;
}
}
else // simple ASCII test.
{
* ( dst + + ) = ( uint32_t ) ( ( ( ch > = ' A ' ) & & ( ch < = ' Z ' ) ) ? ch + 32 : ch ) ;
str + + ;
}
}
* dst = 0 ;
return retval ;
}
inline __attribute__ ( ( always_inline ) ) static int utf8casecmp_loop ( const uint32_t * folded1 , const uint32_t * folded2 )
{
while ( true )
{
const uint32_t ch1 = * ( folded1 + + ) ;
const uint32_t ch2 = * ( folded2 + + ) ;
if ( ch1 < ch2 )
return - 1 ;
else if ( ch1 > ch2 )
return 1 ;
else if ( ch1 = = 0 )
return 0 ; // complete match.
}
}
# ifdef UTF8_PATHMATCH
static int utf8casecmp ( const char * str1 , const char * str2 )
{
uint32_t * folded1 = fold_utf8 ( str1 ) ;
uint32_t * folded2 = fold_utf8 ( str2 ) ;
const int retval = utf8casecmp_loop ( folded1 , folded2 ) ;
delete [ ] folded1 ;
delete [ ] folded2 ;
return retval ;
}
# endif
// Simple object to help make sure a DIR* from opendir
// gets closed when it goes out of scope.
class CDirPtr
{
public :
CDirPtr ( ) { m_pDir = NULL ; }
CDirPtr ( DIR * pDir ) : m_pDir ( pDir ) { }
~ CDirPtr ( ) { Close ( ) ; }
void operator = ( DIR * pDir ) { Close ( ) ; m_pDir = pDir ; }
operator DIR * ( ) { return m_pDir ; }
operator bool ( ) { return m_pDir ! = NULL ; }
private :
void Close ( ) { if ( m_pDir ) closedir ( m_pDir ) ; }
DIR * m_pDir ;
} ;
// Object used to temporarily slice a path into a smaller componentent
// and then repair it when going out of scope. Typically used as an unnamed
// temp object that is a parameter to a function.
class CDirTrimmer
{
public :
CDirTrimmer ( char * pPath , size_t nTrimIdx )
{
m_pPath = pPath ;
m_idx = nTrimIdx ;
m_c = m_pPath [ nTrimIdx ] ;
m_pPath [ nTrimIdx ] = ' \0 ' ;
}
~ CDirTrimmer ( ) { m_pPath [ m_idx ] = m_c ; }
operator const char * ( ) { return m_pPath ; }
private :
size_t m_idx ;
char * m_pPath ;
char m_c ;
} ;
enum PathMod_t
{
kPathUnchanged ,
kPathLowered ,
kPathChanged ,
kPathFailed ,
} ;
static bool Descend ( char * pPath , size_t nStartIdx , bool bAllowBasenameMismatch , size_t nLevel = 0 )
{
DEBUG_MSG ( " (%zu) Descend: %s, (%s), %s \n " , nLevel , pPath , pPath + nStartIdx , bAllowBasenameMismatch ? " true " : " false " ) ;
// We assume up through nStartIdx is valid and matching
size_t nNextSlash = nStartIdx + 1 ;
// path might be a dir
if ( pPath [ nNextSlash ] = = ' \0 ' )
{
return true ;
}
bool bIsDir = false ; // is the new component a directory for certain?
while ( pPath [ nNextSlash ] ! = ' \0 ' & & pPath [ nNextSlash ] ! = ' / ' )
{
nNextSlash + + ;
}
// Modify the pPath string
if ( pPath [ nNextSlash ] = = ' / ' )
bIsDir = true ;
// See if we have an immediate match
if ( __real_access ( CDirTrimmer ( pPath , nNextSlash ) , F_OK ) = = 0 )
{
if ( ! bIsDir )
return true ;
bool bRet = Descend ( pPath , nNextSlash , bAllowBasenameMismatch , nLevel + 1 ) ;
if ( bRet )
return true ;
}
// Start enumerating dirents
CDirPtr spDir ;
if ( nStartIdx )
{
// we have a path
spDir = __real_opendir ( CDirTrimmer ( pPath , nStartIdx ) ) ;
nStartIdx + + ;
}
else
{
// we either start at root or cwd
const char * pRoot = " . " ;
if ( * pPath = = ' / ' )
{
pRoot = " / " ;
nStartIdx + + ;
}
spDir = __real_opendir ( pRoot ) ;
}
errno = 0 ;
struct dirent * pEntry = spDir ? readdir ( spDir ) : NULL ;
char * pszComponent = pPath + nStartIdx ;
size_t cbComponent = nNextSlash - nStartIdx ;
while ( pEntry )
{
DEBUG_MSG ( " \t (%zu) comparing %s with %s \n " , nLevel , pEntry - > d_name , ( const char * ) CDirTrimmer ( pszComponent , cbComponent ) ) ;
// the candidate must match the target, but not be a case-identical match (we would
// have looked there in the short-circuit code above, so don't look again)
bool bMatches = ( strcasecmp ( CDirTrimmer ( pszComponent , cbComponent ) , pEntry - > d_name ) = = 0 & &
strcmp ( CDirTrimmer ( pszComponent , cbComponent ) , pEntry - > d_name ) ! = 0 ) ;
if ( bMatches )
{
char * pSrc = pEntry - > d_name ;
char * pDst = & pPath [ nStartIdx ] ;
// found a match; copy it in.
while ( * pSrc & & ( * pSrc ! = ' / ' ) )
{
* pDst + + = * pSrc + + ;
}
if ( ! bIsDir )
return true ;
if ( Descend ( pPath , nNextSlash , bAllowBasenameMismatch , nLevel + 1 ) )
return true ;
// If descend fails, try more directories
}
pEntry = readdir ( spDir ) ;
}
if ( bIsDir )
{
DEBUG_MSG ( " (%zu) readdir failed to find '%s' in '%s' \n " , nLevel , ( const char * ) CDirTrimmer ( pszComponent , cbComponent ) , ( const char * ) CDirTrimmer ( pPath , nStartIdx ) ) ;
}
// Sometimes it's ok for the filename portion to not match
// since we might be opening for write. Note that if
// the filename matches case insensitive, that will be
// preferred over preserving the input name
if ( ! bIsDir & & bAllowBasenameMismatch )
return true ;
return false ;
}
# ifdef DO_PATHMATCH_CACHE
typedef std : : map < std : : string , std : : pair < std : : string , time_t > > resultCache_t ;
typedef std : : map < std : : string , std : : pair < std : : string , time_t > > : : iterator resultCacheItr_t ;
static resultCache_t resultCache ;
static const int k_cMaxCacheLifetimeSeconds = 2 ;
# endif // DO_PATHMATCH_CACHE
PathMod_t pathmatch ( const char * pszIn , char * * ppszOut , bool bAllowBasenameMismatch , char * pszOutBuf , size_t OutBufLen )
{
// Path matching can be very expensive, and the cost is unpredictable because it
// depends on how many files are in directories on a user's machine. Therefore
// it should be disabled whenever possible, and only enabled in environments (such
// as running with loose files such as out of Perforce) where it is needed.
static const char * s_pszPathMatchEnabled = getenv ( " ENABLE_PATHMATCH " ) ;
if ( ! s_pszPathMatchEnabled )
return kPathUnchanged ;
static const char * s_pszDbgPathMatch = getenv ( " DBG_PATHMATCH " ) ;
s_bShowDiag = ( s_pszDbgPathMatch ! = NULL ) ;
* ppszOut = NULL ;
if ( __real_access ( pszIn , F_OK ) = = 0 )
return kPathUnchanged ;
# ifdef DO_PATHMATCH_CACHE
resultCacheItr_t cachedResult = resultCache . find ( pszIn ) ;
if ( cachedResult ! = resultCache . end ( ) )
{
unsigned int age = time ( NULL ) - cachedResult - > second . second ;
const char * pszResult = cachedResult - > second . first . c_str ( ) ;
if ( pszResult [ 0 ] ! = ' \0 ' | | age < = k_cMaxCacheLifetimeSeconds )
{
if ( pszResult [ 0 ] ! = ' \0 ' )
{
* ppszOut = strdup ( pszResult ) ;
DEBUG_MSG ( " Cached '%s' -> '%s' \n " , pszIn , * ppszOut ) ;
return kPathChanged ;
}
else
{
DEBUG_MSG ( " Cached '%s' -> kPathFailed \n " , pszIn ) ;
return kPathFailed ;
}
}
else if ( age < = k_cMaxCacheLifetimeSeconds )
{
DEBUG_MSG ( " Rechecking '%s' - cache is %u seconds old \n " , pszIn , age ) ;
}
}
# endif // DO_PATHMATCH_CACHE
char * pPath ;
if ( strlen ( pszIn ) > = OutBufLen )
{
pPath = strdup ( pszIn ) ;
}
else
{
strncpy ( pszOutBuf , pszIn , OutBufLen ) ;
pPath = pszOutBuf ;
}
if ( pPath )
{
// I believe this code is broken. I'm guessing someone wanted to avoid lowercasing
// the path before the steam directory - but it's actually skipping lowercasing
// whenever steam is found anywhere - including the filename. For example,
// /home/mikesart/valvesrc/console/l4d2/game/left4dead2_dlc1/particles/steam_fx.pcf
// winds up only having the "steam_fx.pcf" portion lowercased.
# ifdef NEVER
// optimization, if the path contained steam somewhere
// assume the path up through the component with 'steam' in
// is valid (because we almost certainly obtained it
// progamatically
char * p = strcasestr ( pPath , " steam " ) ;
if ( p )
{
while ( p > pPath )
{
if ( p [ - 1 ] = = ' / ' )
break ;
p - - ;
}
if ( ( p = = pPath + 1 ) & & ( * pPath ! = ' / ' ) )
p = pPath ;
}
else
{
p = pPath ;
}
# else
char * p = pPath ;
# endif
// Try the lower casing of the remaining path
char * pBasename = p ;
while ( * p )
{
if ( * p = = ' / ' )
pBasename = p + 1 ;
* p = tolower ( * p ) ;
p + + ;
}
if ( __real_access ( pPath , F_OK ) = = 0 )
{
* ppszOut = pPath ;
DEBUG_MSG ( " Lowered '%s' -> '%s' \n " , pszIn , pPath ) ;
return kPathLowered ;
}
// path didn't match lowered successfully, restore the basename
// if bAllowBasenameMismatch was true
if ( bAllowBasenameMismatch )
{
const char * pSrc = pszIn + ( pBasename - pPath ) ;
while ( * pBasename )
{
* pBasename + + = * pSrc + + ;
}
}
if ( s_pszDbgPathMatch & & strcasestr ( s_pszDbgPathMatch , pszIn ) )
{
DEBUG_MSG ( " Breaking '%s' in '%s' \n " , pszIn , s_pszDbgPathMatch ) ;
DEBUG_BREAK ( ) ;
}
bool bSuccess = Descend ( pPath , 0 , bAllowBasenameMismatch ) ;
if ( bSuccess )
{
* ppszOut = pPath ;
DEBUG_MSG ( " Matched '%s' -> '%s' \n " , pszIn , pPath ) ;
}
else
{
DEBUG_MSG ( " Unmatched %s \n " , pszIn ) ;
}
# ifndef DO_PATHMATCH_CACHE
return bSuccess ? kPathChanged : kPathFailed ;
# else
time_t now = time ( NULL ) ;
if ( bSuccess )
{
resultCache [ pszIn ] = std : : make_pair ( * ppszOut , now ) ;
return kPathChanged ;
}
else
{
resultCache [ pszIn ] = std : : make_pair ( " " , now ) ;
return kPathFailed ;
}
# endif
}
return kPathFailed ;
}
// Wrapper object that manages the 'typical' usage cases of pathmatch()
class CWrap
{
public :
CWrap ( const char * pSuppliedPath , bool bAllowMismatchedBasename )
: m_pSuppliedPath ( pSuppliedPath ) , m_pBestMatch ( NULL )
{
m_eResult = pathmatch ( m_pSuppliedPath , & m_pBestMatch , bAllowMismatchedBasename , m_BestMatchBuf , sizeof ( m_BestMatchBuf ) ) ;
if ( m_pBestMatch = = NULL )
{
m_pBestMatch = const_cast < char * > ( m_pSuppliedPath ) ;
}
}
~ CWrap ( )
{
if ( ( m_pBestMatch ! = m_pSuppliedPath ) & & ( m_pBestMatch ! = m_BestMatchBuf ) )
free ( m_pBestMatch ) ;
}
const char * GetBest ( ) const { return m_pBestMatch ; }
const char * GetOriginal ( ) const { return m_pSuppliedPath ; }
PathMod_t GetMatchResult ( ) const { return m_eResult ; }
operator const char * ( ) { return GetBest ( ) ; }
private :
const char * m_pSuppliedPath ;
char * m_pBestMatch ;
char m_BestMatchBuf [ 512 ] ;
PathMod_t m_eResult ;
} ;
# ifdef MAIN_TEST
void usage ( )
{
puts ( " pathmatch [options] <path> " ) ;
//puts("options:");
//puts("\t");
exit ( - 1 ) ;
}
void test ( const char * pszFile , bool bAllowBasenameMismatch )
{
char * pNewPath ;
char NewPathBuf [ 512 ] ;
PathMod_t nStat = pathmatch ( pszFile , & pNewPath , bAllowBasenameMismatch , NewPathBuf , sizeof ( NewPathBuf ) ) ;
printf ( " AllowMismatchedBasename: %s \n " , bAllowBasenameMismatch ? " true " : " false " ) ;
printf ( " Path Was: " ) ;
switch ( nStat )
{
case kPathUnchanged :
puts ( " kPathUnchanged " ) ;
break ;
case kPathLowered :
puts ( " kPathLowered " ) ;
break ;
case kPathChanged :
puts ( " kPathChanged " ) ;
break ;
case kPathFailed :
puts ( " kPathFailed " ) ;
break ;
}
printf ( " Path In: %s \n " , pszFile ) ;
printf ( " Path Out: %s \n " , nStat = = kPathUnchanged ? pszFile : pNewPath ) ;
if ( pNewPath )
free ( pNewPath ) ;
}
int
main ( int argc , char * * argv )
{
if ( argc < = 1 | | argc > 2 )
usage ( ) ;
test ( argv [ 1 ] , false ) ;
test ( argv [ 1 ] , true ) ;
return 0 ;
}
# endif
extern " C " {
WRAP ( freopen , FILE * , const char * path , const char * mode , FILE * stream )
{
// if mode does not have w, a, or +, it's open for read.
bool bAllowBasenameMismatch = strpbrk ( mode , " wa+ " ) ! = NULL ;
CWrap mpath ( path , bAllowBasenameMismatch ) ;
return CALL ( freopen ) ( mpath , mode , stream ) ;
}
# ifndef ANDROID
WRAP ( fopen , FILE * , const char * path , const char * mode )
{
// if mode does not have w, a, or +, it's open for read.
bool bAllowBasenameMismatch = strpbrk ( mode , " wa+ " ) ! = NULL ;
CWrap mpath ( path , bAllowBasenameMismatch ) ;
return CALL ( fopen ) ( mpath , mode ) ;
}
WRAP ( fopen64 , FILE * , const char * path , const char * mode )
{
// if mode does not have w, a, or +, it's open for read.
bool bAllowBasenameMismatch = strpbrk ( mode , " wa+ " ) ! = NULL ;
CWrap mpath ( path , bAllowBasenameMismatch ) ;
return CALL ( fopen64 ) ( mpath , mode ) ;
}
WRAP ( open , int , const char * pathname , int flags , mode_t mode )
{
bool bAllowBasenameMismatch = ( ( flags & ( O_WRONLY | O_RDWR ) ) ! = 0 ) ;
CWrap mpath ( pathname , bAllowBasenameMismatch ) ;
return CALL ( open ) ( mpath , flags , mode ) ;
}
WRAP ( open64 , int , const char * pathname , int flags , mode_t mode )
{
bool bAllowBasenameMismatch = ( ( flags & ( O_WRONLY | O_RDWR ) ) ! = 0 ) ;
CWrap mpath ( pathname , bAllowBasenameMismatch ) ;
return CALL ( open64 ) ( mpath , flags , mode ) ;
}
int __wrap_creat ( const char * pathname , mode_t mode )
{
return __wrap_open ( pathname , O_CREAT | O_WRONLY | O_TRUNC , mode ) ;
}
# endif
int __wrap_access ( const char * pathname , int mode )
{
return __real_access ( CWrap ( pathname , false ) , mode ) ;
}
WRAP ( stat , int , const char * path , struct stat * buf )
{
return CALL ( stat ) ( CWrap ( path , false ) , buf ) ;
}
WRAP ( lstat , int , const char * path , struct stat * buf )
{
return CALL ( lstat ) ( CWrap ( path , false ) , buf ) ;
}
WRAP ( scandir , int , const char * dirp , struct dirent * * * namelist ,
int ( * filter ) ( const struct dirent * ) ,
int ( * compar ) ( const struct dirent * * , const struct dirent * * ) )
{
return CALL ( scandir ) ( CWrap ( dirp , false ) , namelist , filter , compar ) ;
}
WRAP ( opendir , DIR * , const char * name )
{
return CALL ( opendir ) ( CWrap ( name , false ) ) ;
}
# ifndef ANDROID
WRAP ( __xstat , int , int __ver , __const char * __filename , struct stat * __stat_buf )
{
return CALL ( __xstat ) ( __ver , CWrap ( __filename , false ) , __stat_buf ) ;
}
WRAP ( __lxstat , int , int __ver , __const char * __filename , struct stat * __stat_buf )
{
return CALL ( __lxstat ) ( __ver , CWrap ( __filename , false ) , __stat_buf ) ;
}
WRAP ( __xstat64 , int , int __ver , __const char * __filename , struct stat * __stat_buf )
{
return CALL ( __xstat64 ) ( __ver , CWrap ( __filename , false ) , __stat_buf ) ;
}
WRAP ( __lxstat64 , int , int __ver , __const char * __filename , struct stat * __stat_buf )
{
return CALL ( __lxstat64 ) ( __ver , CWrap ( __filename , false ) , __stat_buf ) ;
}
# endif
WRAP ( chmod , int , const char * path , mode_t mode )
{
return CALL ( chmod ) ( CWrap ( path , false ) , mode ) ;
}
WRAP ( chown , int , const char * path , uid_t owner , gid_t group )
{
return CALL ( chown ) ( CWrap ( path , false ) , owner , group ) ;
}
WRAP ( lchown , int , const char * path , uid_t owner , gid_t group )
{
return CALL ( lchown ) ( CWrap ( path , false ) , owner , group ) ;
}
WRAP ( symlink , int , const char * oldpath , const char * newpath )
{
return CALL ( symlink ) ( CWrap ( oldpath , false ) , CWrap ( newpath , true ) ) ;
}
WRAP ( link , int , const char * oldpath , const char * newpath )
{
return CALL ( link ) ( CWrap ( oldpath , false ) , CWrap ( newpath , true ) ) ;
}
WRAP ( mknod , int , const char * pathname , mode_t mode , dev_t dev )
{
return CALL ( mknod ) ( CWrap ( pathname , true ) , mode , dev ) ;
}
WRAP ( mount , int , const char * source , const char * target ,
const char * filesystemtype , unsigned long mountflags ,
const void * data )
{
return CALL ( mount ) ( CWrap ( source , false ) , CWrap ( target , false ) , filesystemtype , mountflags , data ) ;
}
WRAP ( unlink , int , const char * pathname )
{
return CALL ( unlink ) ( CWrap ( pathname , false ) ) ;
}
WRAP ( mkfifo , int , const char * pathname , mode_t mode )
{
return CALL ( mkfifo ) ( CWrap ( pathname , true ) , mode ) ;
}
WRAP ( rename , int , const char * oldpath , const char * newpath )
{
return CALL ( rename ) ( CWrap ( oldpath , false ) , CWrap ( newpath , true ) ) ;
}
WRAP ( utime , int , const char * filename , const struct utimbuf * times )
{
return CALL ( utime ) ( CWrap ( filename , false ) , times ) ;
}
WRAP ( utimes , int , const char * filename , const struct timeval times [ 2 ] )
{
return CALL ( utimes ) ( CWrap ( filename , false ) , times ) ;
}
WRAP ( realpath , char * , const char * path , char * resolved_path )
{
return CALL ( realpath ) ( CWrap ( path , true ) , resolved_path ) ;
}
WRAP ( mkdir , int , const char * pathname , mode_t mode )
{
return CALL ( mkdir ) ( CWrap ( pathname , true ) , mode ) ;
}
WRAP ( rmdir , char * , const char * pathname )
{
return CALL ( rmdir ) ( CWrap ( pathname , false ) ) ;
}
} ;
# endif