summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure.ac2
-rw-r--r--debian/changelog6
-rwxr-xr-xdebian/rules4
-rw-r--r--doc/CHANGES.html18
-rw-r--r--doc/rsbackup-manual.in.html4
-rw-r--r--doc/rsbackup.16
-rw-r--r--doc/rsbackup.514
-rwxr-xr-xscripts/dist3
-rw-r--r--src/Command.cc6
-rw-r--r--src/Command.h8
-rw-r--r--src/ConfDirective.cc31
-rw-r--r--src/Store.cc10
-rw-r--r--src/Store.h10
-rw-r--r--src/rsbackup.cc27
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/expect/store/notmounted.txt7
-rwxr-xr-xtests/glob-store4
-rwxr-xr-xtests/setup.sh6
-rwxr-xr-xtests/store18
19 files changed, 146 insertions, 41 deletions
diff --git a/configure.ac b/configure.ac
index 815cea1..a9b9df4 100755
--- a/configure.ac
+++ b/configure.ac
@@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ([2.61])
-AC_INIT([rsbackup], [5.0], [rjk@greenend.org.uk])
+AC_INIT([rsbackup], [5.1], [rjk@greenend.org.uk])
AC_CONFIG_AUX_DIR([config.aux])
AM_INIT_AUTOMAKE([foreign])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
diff --git a/debian/changelog b/debian/changelog
index 7c5992e..ddfe5d6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+rsbackup (5.1) stable; urgency=medium
+
+ * Release 5.1
+
+ -- Richard Kettlewell <rjk@greenend.org.uk> Fri, 27 Jul 2018 18:34:15 +0100
+
rsbackup (5.0-2) unstable; urgency=medium
* Upstream patch to workaround sigc++ build failure on GCC 8 (Closes:
diff --git a/debian/rules b/debian/rules
index f1b973f..a14fdd9 100755
--- a/debian/rules
+++ b/debian/rules
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
INSTALL=install
-VERSION=5.0
+VERSION=5.1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
DPKG_EXPORT_BUILDFLAGS = 1
@@ -32,7 +32,7 @@ build-arch: build
build-indep: build
build:
[ -e configure ] || ./autogen.sh
- ./configure --prefix=/usr --mandir=/usr/share/man --without-lyx ${CONFIGURE_EXTRA}
+ ./configure --prefix=/usr --mandir=/usr/share/man ${CONFIGURE_EXTRA}
$(MAKE)
clean-rsbackup:
diff --git a/doc/CHANGES.html b/doc/CHANGES.html
index 9ccd666..6d4da0b 100644
--- a/doc/CHANGES.html
+++ b/doc/CHANGES.html
@@ -15,6 +15,24 @@
href="https://github.com/ewxrjk/rsbackup">rsbackup
in git</a> for detailed change history.</p>
+ <h2>Changes In rsbackup 5.1</h2>
+
+ <ul>
+
+ <li>Store directories are now normally required to be mount
+ points. See the description of <code>store</code>
+ and <code>store-pattern</code>
+ in <a href="rsbackup.5.html">rsbackup(5)</a>
+ and <code>--unmounted-store</code> in <a
+ href="rsbackup.1.html">rsbackup(1)</a> for options to
+ restore the previous behavior.
+ Fixes <a href="https://github.com/ewxrjk/rsbackup/issues/42">issue
+ #42</a>.</li>
+
+ <li>Minor build fixes.</li>
+
+ </ul>
+
<h2>Changes In rsbackup 5.0</h2>
<ul>
diff --git a/doc/rsbackup-manual.in.html b/doc/rsbackup-manual.in.html
index a1f8a3e..ae6a8e4 100644
--- a/doc/rsbackup-manual.in.html
+++ b/doc/rsbackup-manual.in.html
@@ -253,7 +253,7 @@ echo backup2 > /backup2/device-id</pre>
(usually) be owned by root and mode 0700
(i.e. <code>-rwx------</code>).
By default, <code>rsbackup</code> will insist on this,
- although you can use the <code>public</code> option to change
+ although you can use the <code>public</code> directive to change
this behaviour.</p>
<pre class=example>chmod 700 /backup1 /backup2</pre>
@@ -472,7 +472,7 @@ echo backup2 > /backup2/device-id</pre>
a host with a modern
<code>rsync</code>, or vice versa, extended attributes and ACLs
cannot be backed up at all.
- The option must then be set as follows:</p>
+ The directive must then be set as follows:</p>
<pre class=example>rsync-extra-options</pre>
diff --git a/doc/rsbackup.1 b/doc/rsbackup.1
index 285071c..13e0d3a 100644
--- a/doc/rsbackup.1
+++ b/doc/rsbackup.1
@@ -1,5 +1,5 @@
.TH rsbackup 1
-.\" Copyright (c) 2011, 2012, 2014-15, 2017 Richard Kettlewell
+.\" Copyright (c) 2011, 2012, 2014-15, 2017-18 Richard Kettlewell
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
@@ -113,6 +113,10 @@ them with the paths give in \fB\-\-store\fR options.
.IP
This option implicitly enables the \fB\-\-warn\-store\fR option.
.TP
+.B \-\-unmounted\-store \fIPATH\fR
+Equivalent to \fB\-\-store\fR except that the store does not have to
+be a mount point.
+.TP
.B \-\-verbose\fR, \fB\-v
Enable verbose mode.
Various messages will be displayed to report progress and the rsync
diff --git a/doc/rsbackup.5 b/doc/rsbackup.5
index bd02d74..c5b0de5 100644
--- a/doc/rsbackup.5
+++ b/doc/rsbackup.5
@@ -101,14 +101,20 @@ If true, backups are public.
Normally backups must only be accessible by the calling user.
This option suppresses the check.
.TP
-.B store \fIPATH\fR
+.B store \fR[\fB--mounted|--no-mounted\fR] \fIPATH\fR
A path at which a backup device may be mounted.
This can be used multiple times.
+.IP
+With the \fB--mounted\fR option (which is the default),
+\fIPATH\fR must be a mount point.
+With \fB--no-mounted\fR it need not be a mount point.
.TP
-.B store\-pattern \fIPATTERN\fR
+.B store\-pattern \fR[\fB-mounted|-nomounted\fR] \fIPATTERN\fR
A \fBglob\fR(7) pattern matching paths at which a backup device may be
mounted.
This can be used multiple times.
+.IP
+See the description of \fBstore\fR above for the meanings of the options.
.SS "Report Directives"
These are global directives that affect only the HTML report.
.TP
@@ -714,9 +720,7 @@ The name of the host.
.TP
.B PRUNE_ONDEVICE
The list of backups on the device, by age in days.
-This list excludes any that have already been scheduled for pruning,
-and includes the backup under consideration (i.e. the value of
-\fBBACKUP_AGE\fR will appear in this list).
+This list excludes any that have already been scheduled for pruning.
.TP
.B PRUNE_TOTAL
The total number of backups of this volume on any device.
diff --git a/scripts/dist b/scripts/dist
index 9fd078c..a79c0bf 100755
--- a/scripts/dist
+++ b/scripts/dist
@@ -96,11 +96,12 @@ s ./autogen.sh
s ./configure
# Build the source archive
+s make -C doc
s make -C doc html
s make distcheck
srcpkg=rsbackup # source package name
-binpkgs="rsbackup" # binary packages
+binpkgs="rsbackup rsbackup-graph" # binary packages
version=$(make echo-version) # get version number
debversion=$(dpkg-parsechangelog -ldebian/changelog -SVersion)
source=${srcpkg}-${version} # source directory
diff --git a/src/Command.cc b/src/Command.cc
index 672830b..4ab38cd 100644
--- a/src/Command.cc
+++ b/src/Command.cc
@@ -1,4 +1,4 @@
-// Copyright © 2011-16 Richard Kettlewell.
+// Copyright © 2011-18 Richard Kettlewell.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@ enum {
LOG_VERBOSITY = 266,
DUMP_CONFIG = 267,
FORGET_ONLY = 268,
+ UNMOUNTED_STORE = 269,
};
const struct option Command::options[] = {
@@ -48,6 +49,7 @@ const struct option Command::options[] = {
{ "prune", no_argument, nullptr, 'p' },
{ "prune-incomplete", no_argument, nullptr, 'P' },
{ "store", required_argument, nullptr, 's' },
+ { "unmounted-store", required_argument, nullptr, UNMOUNTED_STORE },
{ "retire-device", no_argument, nullptr, RETIRE_DEVICE },
{ "retire", no_argument, nullptr, RETIRE },
{ "config", required_argument, nullptr, 'c' },
@@ -99,6 +101,7 @@ const char *Command::helpString() {
"Additional options:\n"
" --logs all|errors|recent|latest|failed Log verbosity in report\n"
" --store, -s DIR Override directory(s) to store backups in\n"
+" --unmounted-store DIR Override directory(s) to store backups in\n"
" --config, -c PATH Set config file (default: /etc/rsbackup/config)\n"
" --wait, -w Wait until running rsbackup finishes\n"
" --force, -f Don't prompt when retiring\n"
@@ -148,6 +151,7 @@ void Command::parse(int argc, const char *const *argv) {
case 'p': prune = true; break;
case 'P': pruneIncomplete = true; break;
case 's': stores.push_back(optarg); enable_warning(WARNING_STORE); break;
+ case UNMOUNTED_STORE: unmountedStores.push_back(optarg); enable_warning(WARNING_STORE); break;
case 'c': configPath = optarg; break;
case 'w': wait = true; break;
case 'n': act = false; enable_warning(WARNING_VERBOSE); break;
diff --git a/src/Command.h b/src/Command.h
index 656ab10..e20c1d5 100644
--- a/src/Command.h
+++ b/src/Command.h
@@ -1,5 +1,5 @@
// -*-C++-*-
-// Copyright © 2011, 2012, 2014-2016 Richard Kettlewell.
+// Copyright © 2011, 2012, 2014-18 Richard Kettlewell.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -95,6 +95,12 @@ public:
/** @brief Explicitly specified stores */
std::vector<std::string> stores;
+ /** @brief Explicitly specified stores
+ *
+ * These ones don't have to be mount points.
+ */
+ std::vector<std::string> unmountedStores;
+
/** @brief Wait if lock cannot be held
*
* The default is @c false.
diff --git a/src/ConfDirective.cc b/src/ConfDirective.cc
index c10f762..7cef053 100644
--- a/src/ConfDirective.cc
+++ b/src/ConfDirective.cc
@@ -139,22 +139,43 @@ void ColorDirective::set_packed(ConfContext &cc, size_t n, int radix) const {
// Global directives ----------------------------------------------------------
+size_t parseStoreArguments(const ConfContext &cc, bool &mounted) {
+ mounted = true;
+ size_t i = 1;
+ while(i < cc.bits.size() && cc.bits[i][0] == '-') {
+ if(cc.bits[i] == "--mounted")
+ mounted = true;
+ else if(cc.bits[i] == "--no-mounted")
+ mounted = false;
+ else
+ throw SyntaxError("unrecognized store option");
+ ++i;
+ }
+ if(i >= cc.bits.size())
+ throw SyntaxError("missing store path");
+ return i;
+}
+
/** @brief The @c store directive */
static const struct StoreDirective: public ConfDirective {
- StoreDirective(): ConfDirective("store", 1, 1) {}
+ StoreDirective(): ConfDirective("store", 1, INT_MAX) {}
void set(ConfContext &cc) const override {
- cc.conf->stores[cc.bits[1]] = new Store(cc.bits[1]);
+ bool mounted;
+ size_t i = parseStoreArguments(cc, mounted);
+ cc.conf->stores[cc.bits[i]] = new Store(cc.bits[i], mounted);
}
} store_directive;
/** @brief The @c store-pattern directive */
static const struct StorePatternDirective: public ConfDirective {
- StorePatternDirective(): ConfDirective("store-pattern", 1, 1) {}
+ StorePatternDirective(): ConfDirective("store-pattern", 1, INT_MAX) {}
void set(ConfContext &cc) const override {
std::vector<std::string> files;
- globFiles(files, cc.bits[1], GLOB_NOCHECK);
+ bool mounted;
+ size_t i = parseStoreArguments(cc, mounted);
+ globFiles(files, cc.bits[i], GLOB_NOCHECK);
for(auto &file: files)
- cc.conf->stores[file] = new Store(file);
+ cc.conf->stores[file] = new Store(file, mounted);
}
} store_pattern_directive;
diff --git a/src/Store.cc b/src/Store.cc
index 5add94f..543c7cd 100644
--- a/src/Store.cc
+++ b/src/Store.cc
@@ -1,4 +1,4 @@
-// Copyright © 2011, 2012 Richard Kettlewell.
+// Copyright © 2011-13, 2015-18 Richard Kettlewell.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -34,6 +34,14 @@ void Store::identify() {
return; // already identified
if(stat(path.c_str(), &sb) < 0)
throw BadStore("store '" + path + "' does not exist");
+ if(mounted) {
+ const std::string parent_path = path + "/..";
+ struct stat parent_sb;
+ if(stat(parent_path.c_str(), &parent_sb) < 0)
+ throw FatalStoreError("cannot stat '" + parent_path);
+ if(sb.st_dev == parent_sb.st_dev)
+ throw UnavailableStore("store '" + path + "' is not mounted");
+ }
// Make sure backup devices are mounted
preDeviceAccess();
// Read the device name
diff --git a/src/Store.h b/src/Store.h
index 82a4058..d812719 100644
--- a/src/Store.h
+++ b/src/Store.h
@@ -1,5 +1,5 @@
// -*-C++-*-
-// Copyright © 2011, 2012, 2014, 2015 Richard Kettlewell.
+// Copyright © 2011, 2012, 2014, 2015, 2018 Richard Kettlewell.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -32,7 +32,8 @@ public:
/** @brief Constructor
* @param path_ Location of store
*/
- Store(const std::string &path_): path(path_) {}
+ Store(const std::string &path_, bool mounted_):
+ path(path_), mounted(mounted_) {}
/** @brief Possible states */
enum State {
@@ -43,9 +44,12 @@ public:
Enabled = 2,
};
- /** @param Location of store */
+ /** @brief Location of store */
std::string path;
+ /** @brief True if path must be a mount point */
+ bool mounted;
+
/** @param Device mounted at this store
*
* Set to null pointer before checking, or if no device is mounted here
diff --git a/src/rsbackup.cc b/src/rsbackup.cc
index 8c6c0e1..2e153bb 100644
--- a/src/rsbackup.cc
+++ b/src/rsbackup.cc
@@ -1,4 +1,4 @@
-// Copyright © 2011-15 Richard Kettlewell.
+// Copyright © 2011-18 Richard Kettlewell.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -31,6 +31,8 @@
#include <cerrno>
#include <sstream>
+static void commandLineStores(const std::vector<std::string> & stores, bool mounted);
+
int main(int argc, char **argv) {
try {
if(setlocale(LC_CTYPE, "") == nullptr)
@@ -52,16 +54,11 @@ int main(int argc, char **argv) {
}
// Override stores
- if(command.stores.size() != 0) {
+ if(command.stores.size() != 0 || command.unmountedStores.size() != 0) {
for(auto &s: config.stores)
s.second->state = Store::Disabled;
- for(auto &s: command.stores) {
- auto it = config.stores.find(s);
- if(it == config.stores.end())
- config.stores[s] = new Store(s);
- else
- it->second->state = Store::Enabled;
- }
+ commandLineStores(command.stores, true);
+ commandLineStores(command.unmountedStores, false);
}
// Take the lock, if one is defined.
@@ -194,3 +191,15 @@ int main(int argc, char **argv) {
}
exit(!!errors);
}
+
+static void commandLineStores(const std::vector<std::string> &stores,
+ bool mounted) {
+ for(auto &s: stores) {
+ auto it = config.stores.find(s);
+ if(it == config.stores.end())
+ config.stores[s] = new Store(s, mounted);
+ else
+ it->second->state = Store::Enabled;
+ }
+
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4389c81..345e91f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright © 2011-2015,17 Richard Kettlewell.
+# Copyright © 2011-2015,17-18 Richard Kettlewell.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@ EXTRA_DIST=${TESTS} setup.sh pruner.sh hook \
expect/store/overridden.html \
expect/store/duplicate.txt \
expect/store/overridden-stderr.txt \
+ expect/store/notmounted.txt \
expect/upgrade/interrupted-db.txt \
expect/upgrade/simple-db.txt \
expect/prune/createsecond.txt \
diff --git a/tests/expect/store/notmounted.txt b/tests/expect/store/notmounted.txt
new file mode 100644
index 0000000..1706f87
--- /dev/null
+++ b/tests/expect/store/notmounted.txt
@@ -0,0 +1,7 @@
+WARNING: store '<SRCDIR>/store3' is not mounted
+ERROR: no backup devices found
+WARNING: cannot backup host1:volume1 to device1 - device not available
+WARNING: cannot backup host1:volume1 to device2 - device suppressed due to --store
+WARNING: cannot backup host1:volume2 to device1 - device not available
+WARNING: cannot backup host1:volume2 to device2 - device suppressed due to --store
+WARNING: cannot backup host1:volume3 to device2 - device suppressed due to --store
diff --git a/tests/glob-store b/tests/glob-store
index 5d24749..20c7c12 100755
--- a/tests/glob-store
+++ b/tests/glob-store
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright © 2011, 2012, 2014, 2015 Richard Kettlewell.
+# Copyright © 2011, 2012, 2014, 2015, 2018 Richard Kettlewell.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ export EXPECT_STATUS=ok
sed < ${WORKSPACE}/config > ${WORKSPACE}/config.new \
'/^store /d';
-echo "store-pattern ${WORKSPACE}/store*" >>${WORKSPACE}/config.new
+echo "store-pattern --no-mounted ${WORKSPACE}/store*" >>${WORKSPACE}/config.new
mv -f ${WORKSPACE}/config.new ${WORKSPACE}/config
echo "| Create backup for everything using glob-pattern directive"
diff --git a/tests/setup.sh b/tests/setup.sh
index b521208..c82d4cc 100755
--- a/tests/setup.sh
+++ b/tests/setup.sh
@@ -1,4 +1,4 @@
-# Copyright © 2011, 2012, 2014-17 Richard Kettlewell.
+# Copyright © 2011, 2012, 2014-18 Richard Kettlewell.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -41,12 +41,12 @@ setup() {
mkdir ${WORKSPACE}/store1
echo device1 > ${WORKSPACE}/store1/device-id
- echo "store ${WORKSPACE}/store1" >> ${WORKSPACE}/config
+ echo "store --no-mounted ${WORKSPACE}/store1" >> ${WORKSPACE}/config
echo "device \"device1\"" >> ${WORKSPACE}/config
mkdir ${WORKSPACE}/store2
echo device2 > ${WORKSPACE}/store2/device-id
- echo "store ${WORKSPACE}/store2" >>${WORKSPACE}/config
+ echo "store --no-mounted ${WORKSPACE}/store2" >>${WORKSPACE}/config
echo "device device2" >> ${WORKSPACE}/config
echo "public true" >> ${WORKSPACE}/config
diff --git a/tests/store b/tests/store
index 5184631..eac4d77 100755
--- a/tests/store
+++ b/tests/store
@@ -1,5 +1,5 @@
#! /usr/bin/env bash
-# Copyright © 2011, 2012, 2014, 2015 Richard Kettlewell.
+# Copyright © 2011, 2012, 2014-15, 2018 Richard Kettlewell.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,8 +20,20 @@ setup
mv ${WORKSPACE}/store1 ${WORKSPACE}/store3
+echo "| Stores must be mount points normally"
+set +e
+STDERR=${WORKSPACE}/got/notmounted.txt RUN=store RSBACKUP_TODAY=1980-01-01 s ${RSBACKUP} --backup --store ${WORKSPACE}/store3
+status=$?
+set -e
+if [ $status != 1 ]; then
+ echo >&2 "FAILED: store mount check failed"
+ exit 1
+fi
+sed < ${WORKSPACE}/got/notmounted.txt > ${WORKSPACE}/got/notmounted-sed.txt "s,${WORKSPACE},<SRCDIR>,g"
+compare ${srcdir:-.}/expect/store/notmounted.txt ${WORKSPACE}/got/notmounted-sed.txt
+
echo "| Create backup with overridden store"
-STDERR=${WORKSPACE}/got/overridden-stderr.txt RUN=store RSBACKUP_TODAY=1980-01-01 s ${RSBACKUP} --backup --store ${WORKSPACE}/store3 --text ${WORKSPACE}/got/overridden.txt --html ${WORKSPACE}/got/overridden.html
+STDERR=${WORKSPACE}/got/overridden-stderr.txt RUN=store RSBACKUP_TODAY=1980-01-01 s ${RSBACKUP} --backup --unmounted-store ${WORKSPACE}/store3 --text ${WORKSPACE}/got/overridden.txt --html ${WORKSPACE}/got/overridden.html
exists ${WORKSPACE}/store-pre.ran
exists ${WORKSPACE}/store-post.ran
compare ${WORKSPACE}/volume1 ${WORKSPACE}/store3/host1/volume1/1980-01-01
@@ -38,7 +50,7 @@ compare ${srcdir:-.}/expect/store/overridden-stderr.txt ${WORKSPACE}/got/overrid
echo "| Create backup with overridden duplicate store"
cp -a ${WORKSPACE}/store3 ${WORKSPACE}/store1
rm -rf ${WORKSPACE}/store1/host1
-STDERR=${WORKSPACE}/got/duplicate-stderr.txt RUN=store RSBACKUP_TODAY=1980-01-01 s ${RSBACKUP} --backup --store ${WORKSPACE}/store3 --text ${WORKSPACE}/got/duplicate.txt --html ${WORKSPACE}/got/duplicate.html
+STDERR=${WORKSPACE}/got/duplicate-stderr.txt RUN=store RSBACKUP_TODAY=1980-01-01 s ${RSBACKUP} --backup --unmounted-store ${WORKSPACE}/store3 --text ${WORKSPACE}/got/duplicate.txt --html ${WORKSPACE}/got/duplicate.html
exists ${WORKSPACE}/store-pre.ran
exists ${WORKSPACE}/store-post.ran
compare ${WORKSPACE}/volume1 ${WORKSPACE}/store3/host1/volume1/1980-01-01