diff options
author | Armin Le Grand <Armin.Le.Grand@cib.de> | 2016-09-01 16:16:39 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@cib.de> | 2016-10-11 13:56:22 +0200 |
commit | ed646dc595b2ee5248b0994a2b44a7a5a7bfbbd5 (patch) | |
tree | 8dba8640b8ebc980c0cf94aa8120a70cd64f5f26 | |
parent | 9c9f184138dd9e3dbd50d5d50fc86ca172ae4dfe (diff) |
profilesafe: Initial creation of BackupFileHelper
Added helper class to allow easy creation/deployment
of backups of a file (here: registrymodifications). It
works like a 'stack' of backups, supports easy push/pop
of backed-up versions.
Change-Id: Ie19e1209534f23a3dbd6106a5ca13b24b8fefe4d
-rw-r--r-- | comphelper/Library_comphelper.mk | 1 | ||||
-rw-r--r-- | comphelper/source/misc/backupfilehelper.cxx | 230 | ||||
-rw-r--r-- | configmgr/source/components.cxx | 17 | ||||
-rw-r--r-- | include/comphelper/backupfilehelper.hxx | 115 |
4 files changed, 363 insertions, 0 deletions
diff --git a/comphelper/Library_comphelper.mk b/comphelper/Library_comphelper.mk index 4d4e7345298d..59869a7a393f 100644 --- a/comphelper/Library_comphelper.mk +++ b/comphelper/Library_comphelper.mk @@ -76,6 +76,7 @@ $(eval $(call gb_Library_add_exception_objects,comphelper,\ comphelper/source/misc/accimplaccess \ comphelper/source/misc/anytostring \ comphelper/source/misc/asyncnotification \ + comphelper/source/misc/backupfilehelper \ comphelper/source/misc/comphelper_module \ comphelper/source/misc/comphelper_services \ comphelper/source/misc/componentbase \ diff --git a/comphelper/source/misc/backupfilehelper.cxx b/comphelper/source/misc/backupfilehelper.cxx new file mode 100644 index 000000000000..778f0a0e7d27 --- /dev/null +++ b/comphelper/source/misc/backupfilehelper.cxx @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <comphelper/backupfilehelper.hxx> +#include <rtl/ustring.hxx> + +namespace comphelper +{ + sal_uInt16 BackupFileHelper::mnMaxAllowedBackups = 10; + + BackupFileHelper::BackupFileHelper( + const OUString& rBaseURL, + sal_uInt16 nNumBackups) + : mrBaseURL(rBaseURL), + mnNumBackups(::std::min(::std::max(nNumBackups, sal_uInt16(1)), mnMaxAllowedBackups)), + maBase(), + maExt(), + maBaseFile(rBaseURL), + mbBaseFileIsOpen(false) + { + } + + bool BackupFileHelper::tryPush() + { + if (isDifferentOrNew()) + { + // the new file is different or new, create a new first backup + // rename/move/cleanup other backup files, make space for new first backup + push(); + + // copy new one to now free 1st position + osl::File::copy(mrBaseURL, getName(1)); + + return true; + } + + return false; + } + + bool BackupFileHelper::isPopPossible() + { + return firstExists(); + } + + bool BackupFileHelper::tryPop() + { + if (firstExists()) + { + // first copy exists, copy over original and delete + const OUString aOneName(getName(1)); + maBaseFile.close(); + osl::File::copy(aOneName, mrBaseURL); + osl::File::remove(aOneName); + + // rename/move/cleanup other backup files + pop(); + + return true; + } + + return false; + } + + rtl::OUString BackupFileHelper::getName(sal_uInt16 n) + { + if (maExt.isEmpty()) + { + return OUString(maBase + "_" + OUString::number(n)); + } + + return OUString(maBase + "_" + OUString::number(n) + "." + maExt); + } + + bool BackupFileHelper::firstExists() + { + if (baseFileOpen() && splitBaseURL()) + { + // check if 1st copy exists + osl::File aOneFile(getName(1)); + const osl::FileBase::RC aResult(aOneFile.open(osl_File_OpenFlag_Read)); + + return (osl::File::E_None == aResult); + } + + return false; + } + + void BackupFileHelper::pop() + { + for (sal_uInt16 a(2); a < mnMaxAllowedBackups + 1; a++) + { + const OUString aSourceName(getName(a)); + + if (a > mnNumBackups + 1) + { + // try to delete that file, it is out of scope + osl::File::remove(aSourceName); + } + else + { + // rename that file by decreasing index by one + osl::File::move(aSourceName, getName(a - 1)); + } + } + } + + void BackupFileHelper::push() + { + for (sal_uInt16 a(0); a < mnMaxAllowedBackups; a++) + { + const sal_uInt16 nIndex(mnMaxAllowedBackups - a); + const OUString aSourceName(getName(nIndex)); + + if (nIndex >= mnNumBackups) + { + // try to delete that file, it is out of scope + osl::File::remove(aSourceName); + } + else + { + // rename that file by increasing index by one + osl::File::move(aSourceName, getName(nIndex + 1)); + } + } + } + + bool BackupFileHelper::isDifferentOrNew() + { + if (baseFileOpen() && splitBaseURL()) + { + osl::File aLastFile(getName(1)); + const osl::FileBase::RC aResult(aLastFile.open(osl_File_OpenFlag_Read)); + bool bDifferentOrNew(false); + + if (osl::File::E_None == aResult) + { + // exists, check for being equal + bDifferentOrNew = !equalsBase(aLastFile); + } + else if (osl::File::E_NOENT == aResult) + { + // does not exist - also copy + bDifferentOrNew = true; + } + + return bDifferentOrNew; + } + + return false; + } + + bool BackupFileHelper::equalsBase(osl::File& rLastFile) + { + sal_uInt64 nBaseLen(0); + sal_uInt64 nLastLen(0); + maBaseFile.getSize(nBaseLen); + rLastFile.getSize(nLastLen); + + if (nBaseLen == nLastLen) + { + // same filesize -> need to check content + sal_uInt8 aArrayOld[1024]; + sal_uInt8 aArrayLast[1024]; + sal_uInt64 nBytesReadBase(0); + sal_uInt64 nBytesReadLast(0); + bool bDiffers(false); + + // both rewind on start + maBaseFile.setPos(0, 0); + rLastFile.setPos(0, 0); + + while (!bDiffers + && osl::File::E_None == maBaseFile.read(static_cast<void*>(aArrayOld), 1024, nBytesReadBase) + && osl::File::E_None == rLastFile.read(static_cast<void*>(aArrayLast), 1024, nBytesReadLast) + && 0 != nBytesReadBase + && nBytesReadBase == nBytesReadLast) + { + bDiffers = memcmp(aArrayOld, aArrayLast, nBytesReadBase); + } + + return !bDiffers; + } + + return false; + } + + bool BackupFileHelper::splitBaseURL() + { + if (maBase.isEmpty() && !mrBaseURL.isEmpty()) + { + const sal_Int32 nIndex(mrBaseURL.lastIndexOf('.')); + + if (-1 == nIndex) + { + maBase = mrBaseURL; + } + else if (nIndex > 0) + { + maBase = mrBaseURL.copy(0, nIndex); + } + + if (mrBaseURL.getLength() > nIndex + 1) + { + maExt = mrBaseURL.copy(nIndex + 1); + } + } + + return !maBase.isEmpty(); + } + + bool BackupFileHelper::baseFileOpen() + { + if (!mbBaseFileIsOpen && !mrBaseURL.isEmpty()) + { + mbBaseFileIsOpen = (osl::File::E_None == maBaseFile.open(osl_File_OpenFlag_Read)); + } + + return mbBaseFileIsOpen; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/configmgr/source/components.cxx b/configmgr/source/components.cxx index 4797a6732136..f7c473c53899 100644 --- a/configmgr/source/components.cxx +++ b/configmgr/source/components.cxx @@ -49,6 +49,7 @@ #include <sal/log.hxx> #include <sal/types.h> #include <salhelper/thread.hxx> +#include <comphelper/backupfilehelper.hxx> #include "additions.hxx" #include "components.hxx" @@ -616,6 +617,22 @@ Components::~Components() for (WeakRootSet::iterator i(roots_.begin()); i != roots_.end(); ++i) { (*i)->setAlive(false); } + + // test backup of registrymodifications (currently off) + static bool bFeatureSecureUserConfig(false); + + if (bFeatureSecureUserConfig && ModificationTarget::File == modificationTarget_ && !modificationFileUrl_.isEmpty()) + { + static sal_uInt16 nNumCopies(5); + comphelper::BackupFileHelper aBackupFileHelper(modificationFileUrl_, nNumCopies); + aBackupFileHelper.tryPush(); + static bool bTryPop(false); + + if (bTryPop) + { + aBackupFileHelper.tryPop(); + } + } } void Components::parseFileLeniently( diff --git a/include/comphelper/backupfilehelper.hxx b/include/comphelper/backupfilehelper.hxx new file mode 100644 index 000000000000..3061254615ae --- /dev/null +++ b/include/comphelper/backupfilehelper.hxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_COMPHELPER_BACKUPFILEHELPER_HXX +#define INCLUDED_COMPHELPER_BACKUPFILEHELPER_HXX + +#include <sal/config.h> + +#include <comphelper/comphelperdllapi.h> +#include <rtl/ustring.hxx> + +namespace comphelper +{ + /** Helper class to backup/restore a single file + * + * You need to hand over the URL of the file to look at and + * a maximum number of allowed copies. That number is internally + * limited to a max of 10 (see implementation). The number of + * allowed copies is limited to [1..max]. + * + * Calling tryPush() will check if there is no backup yet or if + * there is one that the file has changed. If yes, a new copy is + * created on a kind of 'stack' of copies. Tre return value can + * be used to see if a backup was indeed created. + * + * Calling tryPop() will do the opposite: If a backup is available, + * delete the orig file and re-instantiate the backup. The backup + * is taken off the 'stack' of copies. The return value can be + * used to check if this was done. + * + * isPopPossible can be called to see if there is a backup available + * before calling tryPop(). + * + * The 'stack' of copies works by using the same path, filename + * and extension, but adding a '_1' -> '_(num_of_copy)' to it. + */ + class COMPHELPER_DLLPUBLIC BackupFileHelper + { + private: + // internal data + const OUString& mrBaseURL; + sal_uInt16 mnNumBackups; + OUString maBase; + OUString maExt; + osl::File maBaseFile; + bool mbBaseFileIsOpen; + + // internal upper limit (max) of allowed backups + static sal_uInt16 mnMaxAllowedBackups; + + public: + /** Constructor to handle Backups of the given file + * + * @param rBaseURL + * URL to an existing file that needs to be backed up + * + * @param nNumBackups + * Specifies the maximum number of backups to allow for + * the file. This value gets truncated to [1..max] where + * max currently is 10 and defined in the implementation. + * It is used in tryPush() and tryPop() calls to cleanup/ + * reduce the number of existing backups + */ + BackupFileHelper(const OUString& rBaseURL, sal_uInt16 nNumBackups = 5); + + /** tries to create a new backup, if there is none yet, or if the + * last differs from the base file. It will then put a new verion + * on the 'stack' of copies and evtl. delete the oldest backup. + * Also may cleanup older backups when NumBackups given in the + * constructor has changed. + * + * @return bool + * returns true if a new backup was actually created + */ + bool tryPush(); + + /** finds out if a restore is possible + * + * @return bool + * returns true if a restore to an older backup is possible + */ + bool isPopPossible(); + + /** tries to execute a restore. Will overwrite the base file + * in that case and take one version off the 'stack' of copies. + * Also may cleanup older backups when NumBackups given in the + * constructor has changed. + * + * @return bool + * returns true if a restore was actually created + */ + bool tryPop(); + + private: + // internal helper methods + rtl::OUString getName(sal_uInt16 n); + bool firstExists(); + void pop(); + void push(); + bool isDifferentOrNew(); + bool equalsBase(osl::File& rLastFile); + bool splitBaseURL(); + bool baseFileOpen(); + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |