Check for file permissions via SGPath.
This commit is contained in:
@@ -14,6 +14,12 @@ using std::endl;
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
#include <io.h> // for _wchmod
|
||||
#else
|
||||
#include <sys/stat.h> // for chmod
|
||||
#endif
|
||||
|
||||
void test_dir()
|
||||
{
|
||||
simgear::Dir temp = simgear::Dir::tempDir("foo");
|
||||
@@ -134,6 +140,104 @@ void test_path_dir()
|
||||
|
||||
}
|
||||
|
||||
void test_permissions()
|
||||
{
|
||||
// start with an empty dir
|
||||
SGPath p = simgear::Dir::current().path() / "path_test_permissions";
|
||||
simgear::Dir pd(p);
|
||||
if (pd.exists()) {
|
||||
pd.removeChildren();
|
||||
} else {
|
||||
pd.create(0700);
|
||||
}
|
||||
|
||||
// windows doesn't seem to actualy create a read-only directory, so this
|
||||
// test fails in strange ways there.
|
||||
#if !defined(SG_WINDOWS)
|
||||
SGPath fileInRO = p / "read-only" / "fileA";
|
||||
SG_CHECK_EQUAL(fileInRO.create_dir(0500), 0);
|
||||
|
||||
SG_VERIFY(!fileInRO.exists());
|
||||
SG_VERIFY(!fileInRO.canWrite());
|
||||
|
||||
fileInRO.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRO.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRO.canWrite(), false);
|
||||
|
||||
fileInRO.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRO.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRO.canWrite(), false);
|
||||
#endif
|
||||
|
||||
/////////
|
||||
SGPath fileInRW = p / "read-write" / "fileA";
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.create_dir(0700), -3); // not permitted
|
||||
|
||||
fileInRW.setPermissionChecker(nullptr);
|
||||
SG_CHECK_EQUAL(fileInRW.create_dir(0700), 0);
|
||||
|
||||
SG_VERIFY(!fileInRW.exists());
|
||||
SG_VERIFY(fileInRW.canWrite());
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
|
||||
fileInRW.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), true);
|
||||
|
||||
|
||||
// create the file
|
||||
{
|
||||
sg_ofstream os(fileInRW);
|
||||
SG_VERIFY(os.is_open());
|
||||
os << "nonense" << endl;
|
||||
}
|
||||
|
||||
// should now be readable + writeable
|
||||
fileInRW.set_cached(false);
|
||||
fileInRW.setPermissionChecker(nullptr);
|
||||
SG_VERIFY(fileInRW.exists());
|
||||
SG_VERIFY(fileInRW.canWrite());
|
||||
SG_VERIFY(fileInRW.canRead());
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), true);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
|
||||
fileInRW.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), true);
|
||||
|
||||
// mark the file as read-only
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring wp = fileInRW.wstr();
|
||||
_wchmod(wp.c_str(), _S_IREAD);
|
||||
#else
|
||||
::chmod(fileInRW.c_str(), 0400);
|
||||
#endif
|
||||
|
||||
fileInRW.set_cached(false);
|
||||
fileInRW.setPermissionChecker(nullptr);
|
||||
|
||||
SG_VERIFY(fileInRW.exists());
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
SG_VERIFY(fileInRW.canRead());
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), true);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
|
||||
fileInRW.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
SGPath pa;
|
||||
@@ -229,29 +333,19 @@ int main(int argc, char* argv[])
|
||||
SG_CHECK_EQUAL(pf.lower_extension(), "gz");
|
||||
SG_CHECK_EQUAL(pf.complete_lower_extension(), "txt.gz");
|
||||
|
||||
SG_CHECK_EQUAL(pf.canRead(), true);
|
||||
SG_CHECK_EQUAL(pf.canWrite(), true);
|
||||
SG_CHECK_EQUAL(pf.canRead(), false);
|
||||
SG_CHECK_EQUAL(pf.canWrite(), false);
|
||||
|
||||
SGPath pp(&validateNone);
|
||||
SG_CHECK_EQUAL(pp.canRead(), false);
|
||||
SG_CHECK_EQUAL(pp.canWrite(), false);
|
||||
|
||||
pp.append("./test-dir/file.txt");
|
||||
SG_CHECK_EQUAL(pp.create_dir(0700), -3);
|
||||
|
||||
pp.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(pp.canRead(), true);
|
||||
SG_CHECK_EQUAL(pp.canWrite(), false);
|
||||
SG_CHECK_EQUAL(pp.create_dir(0700), -3);
|
||||
|
||||
pp.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(pp.canRead(), false);
|
||||
SG_CHECK_EQUAL(pp.canWrite(), true);
|
||||
|
||||
test_dir();
|
||||
|
||||
test_path_dir();
|
||||
|
||||
test_permissions();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
}
|
||||
|
||||
@@ -460,10 +460,11 @@ void SGPath::validate() const
|
||||
|
||||
if (path.empty()) {
|
||||
_exists = false;
|
||||
_canWrite = _canRead = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(SG_WINDOWS)
|
||||
struct _stat buf ;
|
||||
bool remove_trailing = false;
|
||||
std::wstring statPath(wstr());
|
||||
@@ -473,12 +474,24 @@ void SGPath::validate() const
|
||||
|
||||
if (_wstat(statPath.c_str(), &buf ) < 0) {
|
||||
_exists = false;
|
||||
_canRead = false;
|
||||
|
||||
// check parent directory for write-ability
|
||||
std::wstring parentPath = simgear::strutils::convertUtf8ToWString(dir());
|
||||
struct _stat parentBuf;
|
||||
if (_wstat(parentPath.c_str(), &parentBuf) >= 0) {
|
||||
_canWrite = parentBuf.st_mode & _S_IWRITE;
|
||||
} else {
|
||||
_canWrite = false;
|
||||
}
|
||||
} else {
|
||||
_exists = true;
|
||||
_isFile = ((S_IFREG & buf.st_mode ) !=0);
|
||||
_isDir = ((S_IFDIR & buf.st_mode ) !=0);
|
||||
_modTime = buf.st_mtime;
|
||||
_size = buf.st_size;
|
||||
_canRead = _S_IREAD & buf.st_mode;
|
||||
_canWrite = _S_IWRITE & buf.st_mode;
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -486,15 +499,35 @@ void SGPath::validate() const
|
||||
|
||||
if (stat(path.c_str(), &buf ) < 0) {
|
||||
_exists = false;
|
||||
_canRead = false;
|
||||
|
||||
// check parent directory for write-ability
|
||||
std::string parentPath = dir();
|
||||
struct stat parentBuf;
|
||||
if (stat(parentPath.c_str(), &parentBuf) >= 0) {
|
||||
_canWrite = parentBuf.st_mode & S_IWUSR;
|
||||
} else {
|
||||
_canWrite = false;
|
||||
}
|
||||
} else {
|
||||
_exists = true;
|
||||
_isFile = ((S_ISREG(buf.st_mode )) != 0);
|
||||
_isDir = ((S_ISDIR(buf.st_mode )) != 0);
|
||||
_modTime = buf.st_mtime;
|
||||
_size = buf.st_size;
|
||||
_canRead = S_IRUSR & buf.st_mode;
|
||||
_canWrite = S_IWUSR & buf.st_mode;
|
||||
}
|
||||
|
||||
#endif
|
||||
// ensure permissions are no less restrictive than what the
|
||||
// permissions checker offers
|
||||
if ( _permission_checker ) {
|
||||
Permissions p = _permission_checker(*this);
|
||||
_canRead &= p.read;
|
||||
_canWrite &= p.write;
|
||||
}
|
||||
|
||||
_cached = true;
|
||||
}
|
||||
|
||||
@@ -504,18 +537,7 @@ void SGPath::checkAccess() const
|
||||
if( _rwCached && _cacheEnabled )
|
||||
return;
|
||||
|
||||
if( _permission_checker )
|
||||
{
|
||||
Permissions p = _permission_checker(*this);
|
||||
_canRead = p.read;
|
||||
_canWrite = p.write;
|
||||
}
|
||||
else
|
||||
{
|
||||
_canRead = true;
|
||||
_canWrite = true;
|
||||
}
|
||||
|
||||
validate();
|
||||
_rwCached = true;
|
||||
}
|
||||
|
||||
@@ -555,7 +577,7 @@ bool SGPath::isFile() const
|
||||
|
||||
int SGPath::create_dir(mode_t mode)
|
||||
{
|
||||
if( !canWrite() )
|
||||
if ( !permissionsAllowsWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_WARN, "Error creating directory for '" << *this << "'"
|
||||
@@ -703,7 +725,7 @@ std::string SGPath::str_native() const
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::remove()
|
||||
{
|
||||
if( !canWrite() )
|
||||
if( !permissionsAllowsWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ")"
|
||||
" reason: access denied" );
|
||||
@@ -712,7 +734,15 @@ bool SGPath::remove()
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = wstr();
|
||||
int err = _wunlink(ps.c_str());
|
||||
|
||||
// windows forbids removing a read-only file, let's try to deal
|
||||
// with that case
|
||||
int err = _wchmod(ps.c_str(), _S_IWRITE | _S_IREAD);
|
||||
if (err != 0) {
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to make file writeable prior to remove:" << *this);
|
||||
} else {
|
||||
err = _wunlink(ps.c_str());
|
||||
}
|
||||
#else
|
||||
std::string ps = local8BitStr();
|
||||
int err = ::unlink(ps.c_str());
|
||||
@@ -1018,3 +1048,10 @@ std::wstring SGPath::wstr() const
|
||||
{
|
||||
return simgear::strutils::convertUtf8ToWString(path);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::permissionsAllowsWrite() const
|
||||
{
|
||||
return _permission_checker ? _permission_checker(*this).write : true;
|
||||
}
|
||||
|
||||
|
||||
@@ -335,6 +335,8 @@ private:
|
||||
void validate() const;
|
||||
void checkAccess() const;
|
||||
|
||||
bool permissionsAllowsWrite() const;
|
||||
|
||||
std::string path;
|
||||
PermissionChecker _permission_checker;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user