From 031c833e4dd58d77613b29035f7e4baa2557f14e Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Thu, 12 Dec 2013 21:03:24 +0100 Subject: [PATCH] SGPath: add support for custom PermissionChecker Allow to add permission checks (read/write-access) for eg. limiting access based on IORules. --- simgear/misc/path_test.cxx | 45 +++++++++- simgear/misc/sg_path.cxx | 165 ++++++++++++++++++++++++++++++------- simgear/misc/sg_path.hxx | 38 +++++++-- 3 files changed, 211 insertions(+), 37 deletions(-) diff --git a/simgear/misc/path_test.cxx b/simgear/misc/path_test.cxx index 459f064c..a062a723 100644 --- a/simgear/misc/path_test.cxx +++ b/simgear/misc/path_test.cxx @@ -43,6 +43,30 @@ void test_dir() cout << temp.path().modTime() << endl; } +SGPath::Permissions validateNone(const SGPath&) +{ + SGPath::Permissions p; + p.read = false; + p.write = false; + return p; +} + +SGPath::Permissions validateRead(const SGPath&) +{ + SGPath::Permissions p; + p.read = true; + p.write = false; + return p; +} + +SGPath::Permissions validateWrite(const SGPath&) +{ + SGPath::Permissions p; + p.read = false; + p.write = true; + return p; +} + int main(int argc, char* argv[]) { SGPath pa; @@ -143,7 +167,26 @@ int main(int argc, char* argv[]) COMPARE(pf.dir(), ""); COMPARE(pf.lower_extension(), "gz"); COMPARE(pf.complete_lower_extension(), "txt.gz"); - + + COMPARE(pf.canRead(), true); + COMPARE(pf.canWrite(), true); + + SGPath pp(&validateNone); + COMPARE(pp.canRead(), false); + COMPARE(pp.canWrite(), false); + + pp.append("./test-dir/file.txt"); + COMPARE(pp.create_dir(0700), -3); + + pp.setPermissonChecker(&validateRead); + COMPARE(pp.canRead(), true); + COMPARE(pp.canWrite(), false); + COMPARE(pp.create_dir(0700), -3); + + pp.setPermissonChecker(&validateWrite); + COMPARE(pp.canRead(), false); + COMPARE(pp.canWrite(), true); + test_dir(); cout << "all tests passed OK" << endl; diff --git a/simgear/misc/sg_path.cxx b/simgear/misc/sg_path.cxx index cc71a9af..375daae6 100644 --- a/simgear/misc/sg_path.cxx +++ b/simgear/misc/sg_path.cxx @@ -75,27 +75,35 @@ SGPath::fix() // default constructor -SGPath::SGPath() +SGPath::SGPath(PermissonChecker validator) : path(""), + _permisson_checker(validator), _cached(false), + _rwCached(false), _cacheEnabled(true) { } // create a path based on "path" -SGPath::SGPath( const std::string& p ) +SGPath::SGPath( const std::string& p, PermissonChecker validator ) : path(p), + _permisson_checker(validator), _cached(false), + _rwCached(false), _cacheEnabled(true) { fix(); } // create a path based on "path" and a "subpath" -SGPath::SGPath( const SGPath& p, const std::string& r ) +SGPath::SGPath( const SGPath& p, + const std::string& r, + PermissonChecker validator ) : path(p.path), + _permisson_checker(validator), _cached(false), + _rwCached(false), _cacheEnabled(p._cacheEnabled) { append(r); @@ -104,8 +112,12 @@ SGPath::SGPath( const SGPath& p, const std::string& r ) SGPath::SGPath(const SGPath& p) : path(p.path), + _permisson_checker(p._permisson_checker), _cached(p._cached), + _rwCached(p._rwCached), _cacheEnabled(p._cacheEnabled), + _canRead(p._canRead), + _canWrite(p._canWrite), _exists(p._exists), _isDir(p._isDir), _isFile(p._isFile), @@ -116,8 +128,12 @@ SGPath::SGPath(const SGPath& p) : SGPath& SGPath::operator=(const SGPath& p) { path = p.path; + _permisson_checker = p._permisson_checker, _cached = p._cached; + _rwCached = p._rwCached; _cacheEnabled = p._cacheEnabled; + _canRead = p._canRead; + _canWrite = p._canWrite; _exists = p._exists; _isDir = p._isDir; _isFile = p._isFile; @@ -135,11 +151,26 @@ void SGPath::set( const string& p ) { path = p; fix(); _cached = false; + _rwCached = false; } +//------------------------------------------------------------------------------ +void SGPath::setPermissonChecker(PermissonChecker validator) +{ + _permisson_checker = validator; + _rwCached = false; +} + +//------------------------------------------------------------------------------ +SGPath::PermissonChecker SGPath::getPermissonChecker() const +{ + return _permisson_checker; +} + +//------------------------------------------------------------------------------ void SGPath::set_cached(bool cached) { - _cacheEnabled = cached; + _cacheEnabled = cached; } // append another piece to the existing path @@ -154,6 +185,7 @@ void SGPath::append( const string& p ) { } fix(); _cached = false; + _rwCached = false; } //------------------------------------------------------------------------------ @@ -180,6 +212,7 @@ void SGPath::concat( const string& p ) { } fix(); _cached = false; + _rwCached = false; } @@ -278,7 +311,7 @@ void SGPath::validate() const if (_cached && _cacheEnabled) { return; } - + #ifdef _WIN32 struct _stat buf ; @@ -310,12 +343,46 @@ void SGPath::validate() const _cached = true; } +void SGPath::checkAccess() const +{ + if( _rwCached && _cacheEnabled ) + return; + + if( _permisson_checker ) + { + Permissions p = _permisson_checker(*this); + _canRead = p.read; + _canWrite = p.write; + } + else + { + _canRead = true; + _canWrite = true; + } + + _rwCached = true; +} + bool SGPath::exists() const { validate(); return _exists; } +//------------------------------------------------------------------------------ +bool SGPath::canRead() const +{ + checkAccess(); + return _canRead; +} + +//------------------------------------------------------------------------------ +bool SGPath::canWrite() const +{ + checkAccess(); + return _canWrite; +} + bool SGPath::isDir() const { validate(); @@ -344,7 +411,7 @@ int SGPath::create_dir( mode_t mode ) { bool absolute = !path.empty() && path[0] == sgDirPathSep; unsigned int i = 1; - SGPath dir = absolute ? string( 1, sgDirPathSep ) : ""; + SGPath dir(absolute ? string( 1, sgDirPathSep ) : "", _permisson_checker); dir.concat( path_elements[0] ); #ifdef _WIN32 if ( dir.str().find(':') != string::npos && path_elements.size() >= 2 ) { @@ -360,16 +427,26 @@ int SGPath::create_dir( mode_t mode ) { if ( r == 0 ) { return 0; // Directory already exists } - if ( sgMkDir( dir.c_str(), mode) ) { - SG_LOG( SG_IO, SG_ALERT, "Error creating directory: " + dir.str() ); + for(;;) + { + if( !dir.canWrite() ) + { + SG_LOG( SG_IO, + SG_ALERT, "Error creating directory: (" << dir.str() << ")" << + " reason: access denied" ); + return -3; + } + else if( sgMkDir(dir.c_str(), mode) ) + { + SG_LOG( SG_IO, + SG_ALERT, "Error creating directory: (" << dir.str() << ")" ); return -2; - } - for(; i < path_elements.size(); i++) { - dir.append(path_elements[i]); - if ( sgMkDir( dir.c_str(), mode) ) { - SG_LOG( SG_IO, SG_ALERT, "Error creating directory: " + dir.str() ); - return -2; - } + } + + if( i >= path_elements.size() ) + return 0; + + dir.append(path_elements[i++]); } return 0; @@ -456,14 +533,27 @@ std::string SGPath::str_native() const #endif } +//------------------------------------------------------------------------------ bool SGPath::remove() { - int err = ::unlink(c_str()); - if (err) { - SG_LOG(SG_IO, SG_WARN, "file remove failed: (" << str() << ") " << strerror(errno)); - } - _cached = false; // stat again if required - return (err == 0); + if( !canWrite() ) + { + SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ")" + " reason: access denied" ); + return false; + } + + int err = ::unlink(c_str()); + if( err ) + { + SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ") " + " reason: " << strerror(errno) ); + // TODO check if failed unlink can really change any of the cached values + } + + _cached = false; // stat again if required + _rwCached = false; + return (err == 0); } time_t SGPath::modTime() const @@ -482,8 +572,17 @@ bool SGPath::operator!=(const SGPath& other) const return (path != other.path); } +//------------------------------------------------------------------------------ bool SGPath::rename(const SGPath& newName) { + if( !newName.canWrite() ) + { + SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() << + " to " << newName.str() << + " reason: access denied" ); + return false; + } + #ifdef SG_WINDOWS if (newName.exists()) { SGPath r(newName); @@ -492,15 +591,19 @@ bool SGPath::rename(const SGPath& newName) } } #endif - if (::rename(c_str(), newName.c_str()) != 0) { - SG_LOG(SG_IO, SG_WARN, "renamed failed: from " << str() << " to " << newName.str() - << " reason: " << strerror(errno)); - return false; - } - - path = newName.path; - _cached = false; - return true; + if( ::rename(c_str(), newName.c_str()) != 0 ) + { + SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() << + " to " << newName.str() << + " reason: " << strerror(errno) ); + return false; + } + + path = newName.path; + _cached = false; + _rwCached = false; + + return true; } //------------------------------------------------------------------------------ @@ -508,7 +611,7 @@ SGPath SGPath::fromEnv(const char* name, const SGPath& def) { const char* val = getenv(name); if( val && val[0] ) - return SGPath(val); + return SGPath(val, def._permisson_checker); return def; } diff --git a/simgear/misc/sg_path.hxx b/simgear/misc/sg_path.hxx index bd27694f..97587fe6 100644 --- a/simgear/misc/sg_path.hxx +++ b/simgear/misc/sg_path.hxx @@ -52,8 +52,15 @@ class SGPath { public: + struct Permissions + { + bool read : 1; + bool write : 1; + }; + typedef Permissions (*PermissonChecker)(const SGPath&); + /** Default constructor */ - SGPath(); + explicit SGPath(PermissonChecker validator = NULL); /** Copy contructor */ SGPath(const SGPath& p); @@ -64,14 +71,16 @@ public: * Construct a path based on the starting path provided. * @param p initial path */ - SGPath( const std::string& p ); + SGPath( const std::string& p, PermissonChecker validator = NULL ); /** * Construct a path based on the starting path provided and a relative subpath * @param p initial path * @param r relative subpath */ - SGPath( const SGPath& p, const std::string& r ); + SGPath( const SGPath& p, + const std::string& r, + PermissonChecker validator = NULL ); /** Destructor */ ~SGPath(); @@ -85,7 +94,10 @@ public: bool operator==(const SGPath& other) const; bool operator!=(const SGPath& other) const; - + + void setPermissonChecker(PermissonChecker validator); + PermissonChecker getPermissonChecker() const; + /** * Set if file information (exists, type, mod-time) is cached or * retrieved each time it is queried. Caching is enabled by default @@ -198,6 +210,17 @@ public: */ int create_dir(mode_t mode); + /** + * Check if reading file is allowed. Readabilty does not imply the existance + * of the file. + * + * @note By default all files will be marked as readable. No check is made + * if the operating system allows the given file to be read. Derived + * classes may actually implement custom read/write rights. + */ + bool canRead() const; + bool canWrite() const; + bool isFile() const; bool isDir() const; @@ -258,11 +281,16 @@ private: void fix(); void validate() const; + void checkAccess() const; std::string path; - + PermissonChecker _permisson_checker; + mutable bool _cached : 1; + mutable bool _rwCached : 1; bool _cacheEnabled : 1; ///< cacheing can be disbled if required + mutable bool _canRead : 1; + mutable bool _canWrite : 1; mutable bool _exists : 1; mutable bool _isDir : 1; mutable bool _isFile : 1;