summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2016-09-01 16:16:39 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2016-10-11 13:56:22 +0200
commited646dc595b2ee5248b0994a2b44a7a5a7bfbbd5 (patch)
tree8dba8640b8ebc980c0cf94aa8120a70cd64f5f26
parent9c9f184138dd9e3dbd50d5d50fc86ca172ae4dfe (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.mk1
-rw-r--r--comphelper/source/misc/backupfilehelper.cxx230
-rw-r--r--configmgr/source/components.cxx17
-rw-r--r--include/comphelper/backupfilehelper.hxx115
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: */