Make file read-only once assigned to a PropertyFileIncluded instance

This commit is contained in:
wmayer 2013-05-07 22:42:58 +02:00
parent ab8a8cd371
commit 9a9510d9e3
5 changed files with 155 additions and 43 deletions

View File

@ -607,7 +607,7 @@ Document::~Document()
// Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed // Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed
// Python object or not. In the constructor we forced the wrapper to own the object so we need // Python object or not. In the constructor we forced the wrapper to own the object so we need
// not to dec'ref the Python object any more. // not to dec'ref the Python object any more.
// But we must still invalidate the Python object because it need not to be // But we must still invalidate the Python object because it doesn't need to be
// destructed right now because the interpreter can own several references to it. // destructed right now because the interpreter can own several references to it.
Base::PyObjectBase* doc = (Base::PyObjectBase*)DocumentPythonObject.ptr(); Base::PyObjectBase* doc = (Base::PyObjectBase*)DocumentPythonObject.ptr();
// Call before decrementing the reference counter, otherwise a heap error can occur // Call before decrementing the reference counter, otherwise a heap error can occur

View File

@ -42,7 +42,7 @@
#include "Document.h" #include "Document.h"
#include "PropertyContainer.h" #include "PropertyContainer.h"
#include "DocumentObject.h" #include "DocumentObject.h"
#define new DEBUG_CLIENTBLOCK
using namespace App; using namespace App;
using namespace Base; using namespace Base;
using namespace std; using namespace std;
@ -66,10 +66,25 @@ PropertyFileIncluded::~PropertyFileIncluded()
// clean up // clean up
if (!_cValue.empty()) { if (!_cValue.empty()) {
Base::FileInfo file(_cValue.c_str()); Base::FileInfo file(_cValue.c_str());
file.setPermissions(Base::FileInfo::ReadWrite);
file.deleteFile(); file.deleteFile();
} }
} }
void PropertyFileIncluded::aboutToSetValue(void)
{
// This is a trick to check in Copy() if it is called
// directly from outside or by the Undo/Redo mechanism.
// In the latter case it is sufficient to rename the file
// because another file will be assigned afterwards.
// If Copy() is directly called (e.g. to copy the file to
// another document) a copy of the file needs to be created.
// This copy will be deleted again in the class destructor.
this->StatusBits.set(10);
Property::aboutToSetValue();
this->StatusBits.reset(10);
}
std::string PropertyFileIncluded::getDocTransientPath(void) const std::string PropertyFileIncluded::getDocTransientPath(void) const
{ {
std::string path; std::string path;
@ -118,8 +133,10 @@ void PropertyFileIncluded::setValue(const char* sFile, const char* sName)
// remove old file (if not moved by undo) // remove old file (if not moved by undo)
Base::FileInfo value(_cValue); Base::FileInfo value(_cValue);
std::string pathAct = value.dirPath(); std::string pathAct = value.dirPath();
if (value.exists()) if (value.exists()) {
value.setPermissions(Base::FileInfo::ReadWrite);
value.deleteFile(); value.deleteFile();
}
// if a special name given, use this instead // if a special name given, use this instead
if (sName) { if (sName) {
@ -153,25 +170,29 @@ void PropertyFileIncluded::setValue(const char* sFile, const char* sName)
_BaseFileName = file.fileName(); _BaseFileName = file.fileName();
} }
// That's wrong and can lead to loss of data!!! // The following applies only on files that are inside the transient
// Just consider the example that two objects with this property // directory:
// exist in the same document and as an initial step the data are // When a file is read-only it is supposed to be assigned to a
// copied from one object to the other. A rename will cause the one // PropertyFileIncluded instance. In this case we must copy the
// object to loose its data. // file because otherwise the above instance looses its data.
#if 0 // If the file is writable it is supposed to be of free use and
// it can be simply renamed.
// if the file is already in transient dir of the document, just use it // if the file is already in transient dir of the document, just use it
if (path == pathTrans) { if (path == pathTrans && file.isWritable()) {
bool done = file.renameFile(_cValue.c_str()); bool done = file.renameFile(_cValue.c_str());
if (!done) { if (!done) {
std::stringstream str; std::stringstream str;
str << "Cannot rename file " << file.filePath() << " to " << _cValue; str << "Cannot rename file " << file.filePath() << " to " << _cValue;
throw Base::Exception(str.str()); throw Base::Exception(str.str());
} }
// make the file read-only
Base::FileInfo dst(_cValue);
dst.setPermissions(Base::FileInfo::ReadOnly);
} }
// otherwise copy from origin location // otherwise copy from origin location
else else {
#endif
{
// if file already exists in transient dir make a new unique name // if file already exists in transient dir make a new unique name
Base::FileInfo fi(_cValue); Base::FileInfo fi(_cValue);
if (fi.exists()) { if (fi.exists()) {
@ -200,6 +221,10 @@ void PropertyFileIncluded::setValue(const char* sFile, const char* sName)
str << "Cannot copy file from " << file.filePath() << " to " << _cValue; str << "Cannot copy file from " << file.filePath() << " to " << _cValue;
throw Base::Exception(str.str()); throw Base::Exception(str.str());
} }
// make the file read-only
Base::FileInfo dst(_cValue);
dst.setPermissions(Base::FileInfo::ReadOnly);
} }
hasSetValue(); hasSetValue();
@ -350,6 +375,9 @@ void PropertyFileIncluded::Restore(Base::XMLReader &reader)
reader.readBinFile(_cValue.c_str()); reader.readBinFile(_cValue.c_str());
reader.readEndElement("FileIncluded"); reader.readEndElement("FileIncluded");
_BaseFileName = file; _BaseFileName = file;
// set read-only after restoring the file
Base::FileInfo fi(_cValue.c_str());
fi.setPermissions(Base::FileInfo::ReadOnly);
hasSetValue(); hasSetValue();
} }
} }
@ -375,7 +403,8 @@ void PropertyFileIncluded::SaveDocFile (Base::Writer &writer) const
void PropertyFileIncluded::RestoreDocFile(Base::Reader &reader) void PropertyFileIncluded::RestoreDocFile(Base::Reader &reader)
{ {
Base::ofstream to(Base::FileInfo(_cValue.c_str())); Base::FileInfo fi(_cValue.c_str());
Base::ofstream to(fi);
if (!to) { if (!to) {
std::stringstream str; std::stringstream str;
str << "PropertyFileIncluded::RestoreDocFile(): " str << "PropertyFileIncluded::RestoreDocFile(): "
@ -390,6 +419,9 @@ void PropertyFileIncluded::RestoreDocFile(Base::Reader &reader)
to.put((const char)c); to.put((const char)c);
} }
to.close(); to.close();
// set read-only after restoring the file
fi.setPermissions(Base::FileInfo::ReadOnly);
hasSetValue(); hasSetValue();
} }
@ -404,6 +436,18 @@ Property *PropertyFileIncluded::Copy(void) const
if (file.exists()) { if (file.exists()) {
// create a new name in the document transient directory // create a new name in the document transient directory
Base::FileInfo newName(getUniqueFileName(file.dirPath(), file.fileName())); Base::FileInfo newName(getUniqueFileName(file.dirPath(), file.fileName()));
if (this->StatusBits.test(10)) {
// rename the file
bool done = file.renameFile(newName.filePath().c_str());
if (!done) {
std::stringstream str;
str << "PropertyFileIncluded::Copy(): "
<< "Renaming the file '" << file.filePath() << "' to '"
<< newName.filePath() << "' failed.";
throw Base::Exception(str.str());
}
}
else {
// copy the file // copy the file
bool done = file.copyTo(newName.filePath().c_str()); bool done = file.copyTo(newName.filePath().c_str());
if (!done) { if (!done) {
@ -413,10 +457,14 @@ Property *PropertyFileIncluded::Copy(void) const
<< newName.filePath() << "' failed."; << newName.filePath() << "' failed.";
throw Base::Exception(str.str()); throw Base::Exception(str.str());
} }
}
// remember the new name for the Undo // remember the new name for the Undo
Base::Console().Log("Copy '%s' to '%s'\n",_cValue.c_str(),newName.filePath().c_str()); Base::Console().Log("Copy '%s' to '%s'\n",_cValue.c_str(),newName.filePath().c_str());
prop->_cValue = newName.filePath().c_str(); prop->_cValue = newName.filePath().c_str();
// make backup files writable to avoid copying them again on undo/redo
newName.setPermissions(Base::FileInfo::ReadWrite);
} }
return prop; return prop;
@ -429,15 +477,31 @@ void PropertyFileIncluded::Paste(const Property &from)
// make sure that source and destination file are different // make sure that source and destination file are different
if (_cValue != prop._cValue) { if (_cValue != prop._cValue) {
// delete old file (if still there) // delete old file (if still there)
Base::FileInfo(_cValue).deleteFile(); Base::FileInfo fi(_cValue);
fi.setPermissions(Base::FileInfo::ReadWrite);
fi.deleteFile();
// get path to destination which can be the transient directory // get path to destination which can be the transient directory
// of another document // of another document
std::string path = getDocTransientPath(); std::string pathTrans = getDocTransientPath();
Base::FileInfo fiSrc(prop._cValue); Base::FileInfo fiSrc(prop._cValue);
Base::FileInfo fiDst(path + "/" + prop._BaseFileName); Base::FileInfo fiDst(pathTrans + "/" + prop._BaseFileName);
std::string path = fiSrc.dirPath();
if (fiSrc.exists()) { if (fiSrc.exists()) {
fiDst.setFile(getUniqueFileName(fiDst.dirPath(), fiDst.fileName())); fiDst.setFile(getUniqueFileName(fiDst.dirPath(), fiDst.fileName()));
// if the file is already in transient dir of the document, just use it
if (path == pathTrans) {
if (!fiSrc.renameFile(fiDst.filePath().c_str())) {
std::stringstream str;
str << "PropertyFileIncluded::Paste(): "
<< "Renaming the file '" << fiSrc.filePath() << "' to '"
<< fiDst.filePath() << "' failed.";
throw Base::Exception(str.str());
}
}
else {
if (!fiSrc.copyTo(fiDst.filePath().c_str())) { if (!fiSrc.copyTo(fiDst.filePath().c_str())) {
std::stringstream str; std::stringstream str;
str << "PropertyFileIncluded::Paste(): " str << "PropertyFileIncluded::Paste(): "
@ -445,6 +509,10 @@ void PropertyFileIncluded::Paste(const Property &from)
<< fiDst.filePath() << "' failed."; << fiDst.filePath() << "' failed.";
throw Base::Exception(str.str()); throw Base::Exception(str.str());
} }
}
// set the file again read-only
fiDst.setPermissions(Base::FileInfo::ReadOnly);
_cValue = fiDst.filePath(); _cValue = fiDst.filePath();
} }
else { else {

View File

@ -108,6 +108,7 @@ protected:
// get the transient path if the property is in a DocumentObject // get the transient path if the property is in a DocumentObject
std::string getDocTransientPath(void) const; std::string getDocTransientPath(void) const;
std::string getUniqueFileName(const std::string&, const std::string&) const; std::string getUniqueFileName(const std::string&, const std::string&) const;
void aboutToSetValue(void);
protected: protected:
mutable std::string _cValue; mutable std::string _cValue;

View File

@ -52,10 +52,20 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <cstdio> #include <cstdio>
#define new DEBUG_CLIENTBLOCK
using namespace Base; using namespace Base;
#ifndef R_OK
#define R_OK 4 /* Test for read permission */
#endif
#ifndef W_OK
#define W_OK 2 /* Test for write permission */
#endif
#ifndef X_OK
#define X_OK 1 /* Test for execute permission */
#endif
#ifndef F_OK
#define F_OK 0 /* Test for existence */
#endif
//********************************************************************************** //**********************************************************************************
// helper // helper
@ -263,9 +273,9 @@ bool FileInfo::exists () const
{ {
#if defined (FC_OS_WIN32) #if defined (FC_OS_WIN32)
std::wstring wstr = toStdWString(); std::wstring wstr = toStdWString();
return _waccess(wstr.c_str(),0) == 0; return _waccess(wstr.c_str(),F_OK) == 0;
#elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) #elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
return access(FileName.c_str(),0) == 0; return access(FileName.c_str(),F_OK) == 0;
#endif #endif
} }
@ -273,9 +283,9 @@ bool FileInfo::isReadable () const
{ {
#if defined (FC_OS_WIN32) #if defined (FC_OS_WIN32)
std::wstring wstr = toStdWString(); std::wstring wstr = toStdWString();
return _waccess(wstr.c_str(),4) == 0; return _waccess(wstr.c_str(),R_OK) == 0;
#elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) #elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
return access(FileName.c_str(),4) == 0; return access(FileName.c_str(),R_OK) == 0;
#endif #endif
} }
@ -283,9 +293,29 @@ bool FileInfo::isWritable () const
{ {
#if defined (FC_OS_WIN32) #if defined (FC_OS_WIN32)
std::wstring wstr = toStdWString(); std::wstring wstr = toStdWString();
return _waccess(wstr.c_str(),2) == 0; return _waccess(wstr.c_str(),W_OK) == 0;
#elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) #elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
return access(FileName.c_str(),2) == 0; return access(FileName.c_str(),W_OK) == 0;
#endif
}
bool FileInfo::setPermissions (Permissions perms)
{
bool ret = false;
int mode = 0;
if (perms & FileInfo::ReadOnly)
mode |= S_IREAD;
if (perms & FileInfo::WriteOnly)
mode |= S_IWRITE;
if (mode == 0) // bad argument
return false;
#if defined (FC_OS_WIN32)
std::wstring wstr = toStdWString();
return _wchmod(wstr.c_str(),mode) == 0;
#elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
return chmod(FileName.c_str(),mode) == 0;
#endif #endif
} }
@ -473,13 +503,18 @@ bool FileInfo::deleteDirectoryRecursive(void) const
std::vector<Base::FileInfo> List = getDirectoryContent(); std::vector<Base::FileInfo> List = getDirectoryContent();
for (std::vector<Base::FileInfo>::iterator It = List.begin();It!=List.end();++It) { for (std::vector<Base::FileInfo>::iterator It = List.begin();It!=List.end();++It) {
if (It->isDir()) if (It->isDir()) {
It->setPermissions(FileInfo::ReadWrite);
It->deleteDirectoryRecursive(); It->deleteDirectoryRecursive();
else if(It->isFile()) }
else if (It->isFile()) {
It->setPermissions(FileInfo::ReadWrite);
It->deleteFile(); It->deleteFile();
else }
else {
Base::Exception("FileInfo::deleteDirectoryRecursive(): Unknown object Type in directory!"); Base::Exception("FileInfo::deleteDirectoryRecursive(): Unknown object Type in directory!");
} }
}
return deleteDirectory(); return deleteDirectory();
} }

View File

@ -42,6 +42,12 @@ namespace Base
class BaseExport FileInfo class BaseExport FileInfo
{ {
public: public:
enum Permissions {
WriteOnly = 0x01,
ReadOnly = 0x02,
ReadWrite = 0x03,
};
/// Constrction /// Constrction
FileInfo (const char* _FileName=""); FileInfo (const char* _FileName="");
FileInfo (const std::string &_FileName); FileInfo (const std::string &_FileName);
@ -89,6 +95,8 @@ public:
bool isReadable () const; bool isReadable () const;
/// Checks if the file exist and is writable /// Checks if the file exist and is writable
bool isWritable () const; bool isWritable () const;
/// Tries to set the file permisson
bool setPermissions (Permissions);
/// Checks if it is a file (not a direrctory) /// Checks if it is a file (not a direrctory)
bool isFile () const; bool isFile () const;
/// Checks if it is a directory (not a file) /// Checks if it is a directory (not a file)
@ -109,7 +117,7 @@ public:
std::vector<Base::FileInfo> getDirectoryContent(void) const; std::vector<Base::FileInfo> getDirectoryContent(void) const;
/// Delete an empty directory /// Delete an empty directory
bool deleteDirectory(void) const; bool deleteDirectory(void) const;
/// Delete a directory and all its content /// Delete a directory and all its content.
bool deleteDirectoryRecursive(void) const; bool deleteDirectoryRecursive(void) const;
//@} //@}