IncludeExcludeMountTypeOptions
From RdiffBackupWiki
It would be nice to be able to do something like:
--exclude-mount-type nfs
or
--include-mount-type ext3 --exclude '''
This would make it easy to back up just the local filesystems, for example.
Best.
rpeterso at mtholyoke dot edu
How do we find the type of a file system though? Running "mount" and trying to read the output wouldn't be very reliable/portable.
We can find the type of a file system using the statfs() system call. It is not supported by Python natively (at least as of May 2006), so code to call it will need to be added to rdiff-backup's cmodule.c
This patch implements this feature on BSD. The cmodule.c section will need
to be extended to support Linux and Windows. It handles options of the
style:
--include-mount-type3Dufs --exclude-mount-type3Dnfs
--exclude-mount-type=3Diso9660
It does not handle wildcards or lists of filetypes.
diff -Nur rdiff-backup-cvs/rdiff_backup/Main.py rdiff-backup-fstypebsd/rdiff_backup/Main.py
--- rdiff-backup-cvs/rdiff_backup/Main.py 2006-01-31 22:00:24.000000000 -0500
+++ rdiff-backup-fstypebsd/rdiff_backup/Main.py 2006-08-21 22:32:57.000000000 -0400
@@ -64,13 +64,13 @@
"compare-hash-at-time=", "compare-full", "compare-full-at-time=",
"create-full-path", "current-time=", "exclude=",
"exclude-device-files", "exclude-fifos", "exclude-filelist=",
- "exclude-symbolic-links", "exclude-sockets",
+ "exclude-symbolic-links", "exclude-sockets", "exclude-mount-type=",
"exclude-filelist-stdin", "exclude-globbing-filelist=",
"exclude-globbing-filelist-stdin", "exclude-mirror=",
"exclude-other-filesystems", "exclude-regexp=",
"exclude-special-files", "force", "group-mapping-file=",
"include=", "include-filelist=", "include-filelist-stdin",
- "include-globbing-filelist=",
+ "include-globbing-filelist=", "include-mount-type=",
"include-globbing-filelist-stdin", "include-regexp=",
"include-special-files", "include-symbolic-links",
"list-at-time=", "list-changed-since=", "list-increments",
@@ -107,6 +107,7 @@
opt == "--exclude-device-files" or
opt == "--exclude-fifos" or
opt == "--exclude-other-filesystems" or
+ opt == "--exclude-mount-type" or
opt == "--exclude-regexp" or
opt == "--exclude-special-files" or
opt == "--exclude-sockets" or
@@ -144,7 +145,10 @@
select_opts.append(("--include-globbing-filelist",
"standard input"))
select_files.append(sys.stdin)
- elif opt == "--include-regexp": select_opts.append((opt, arg))
+ elif opt == "--include-regexp":
+ select_opts.append((opt, arg))
+ elif opt == "--include-mount-type":
+ select_opts.append((opt, arg))
elif opt == "--list-at-time":
restore_timestr, action = arg, "list-at-time"
elif opt == "--list-changed-since":
diff -Nur rdiff-backup-cvs/rdiff_backup/cmodule.c rdiff-backup-fstypebsd/rdiff_backup/cmodule.c
--- rdiff-backup-cvs/rdiff_backup/cmodule.c 2005-09-07 13:33:25.000000000 -0400
+++ rdiff-backup-fstypebsd/rdiff_backup/cmodule.c 2006-08-21 22:20:07.000000000 -0400
@@ -27,6 +27,11 @@
#include <unistd.h>
#include <errno.h>
+#if defined(BSD) || defined(__APPLE__) /** For statfs on BSD/Apple **/
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <string.h>
+#endif
/* Some of the following code to define major/minor taken from code by
* Jörg Schilling's star archiver.
@@ -74,6 +79,7 @@
static PyObject *long2str(PyObject *self, PyObject *args);
static PyObject *str2long(PyObject *self, PyObject *args);
static PyObject *my_sync(PyObject *self, PyObject *args);
+char *get_mounttype(char *filename, long int mode);
/* Turn a stat structure into a python dictionary. The preprocessor
stuff taken from Python's posixmodule.c */
@@ -82,7 +88,7 @@
PyObject *args;
{
PyObject *size, *inode, *mtime, *atime, *ctime, *devloc, *return_val;
- char *filename, filetype[5];
+ char *filename, *mounttype, filetype[5];
STRUCT_STAT sbuf;
long int mode, perms;
int res;
@@ -125,6 +131,17 @@
ctime = PyInt_FromLong((long)sbuf.st_ctime);
#endif
+ mounttype = get_mounttype(filename, mode);
+
+ if (mounttype == NULL) {
+ if (errno == ENOENT || errno == ENOTDIR) {
+ return Py_BuildValue("{s:s}", "type", NULL);
+ } else {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
+ return NULL;
+ }
+ }
+
/* Build return dictionary from stat struct */
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISSOCK(mode) || S_ISFIFO(mode)) {
/* Regular files, directories, sockets, and fifos */
@@ -132,7 +149,7 @@
else if S_ISDIR(mode) strcpy(filetype, "dir");
else if S_ISSOCK(mode) strcpy(filetype, "sock");
else strcpy(filetype, "fifo");
- return_val = Py_BuildValue("{s:s,s:O,s:l,s:l,s:l,s:O,s:O,s:l,s:O,s:O,s:O}",
+ return_val = Py_BuildValue("{s:s,s:O,s:l,s:l,s:l,s:O,s:O,s:s,s:l,s:O,s:O,s:O}",
"type", filetype,
"size", size,
"perms", perms,
@@ -140,6 +157,7 @@
"gid", (long)sbuf.st_gid,
"inode", inode,
"devloc", devloc,
+ "mounttype", mounttype,
"nlink", (long)sbuf.st_nlink,
"mtime", mtime,
"atime", atime,
@@ -153,7 +171,7 @@
return_val = NULL;
} else {
linkname[len_link] = '\0';
- return_val = Py_BuildValue("{s:s,s:O,s:l,s:l,s:l,s:O,s:O,s:l,s:s}",
+ return_val = Py_BuildValue("{s:s,s:O,s:l,s:l,s:l,s:O,s:O,s:s,s:l,s:s}",
"type", "sym",
"size", size,
"perms", perms,
@@ -161,6 +179,7 @@
"gid", (long)sbuf.st_gid,
"inode", inode,
"devloc", devloc,
+ "mounttype", mounttype,
"nlink", (long)sbuf.st_nlink,
"linkname", linkname);
}
@@ -177,7 +196,7 @@
int minor_num = (int)(minor(devnums));
if S_ISCHR(mode) strcpy(devtype, "c");
else strcpy(devtype, "b");
- return_val = Py_BuildValue("{s:s,s:O,s:l,s:l,s:l,s:O,s:O,s:l,s:N}",
+ return_val = Py_BuildValue("{s:s,s:O,s:l,s:l,s:l,s:O,s:O,s:s,s:l,s:N}",
"type", "dev",
"size", size,
"perms", perms,
@@ -185,6 +204,7 @@
"gid", (long)sbuf.st_gid,
"inode", inode,
"devloc", devloc,
+ "mounttype", mounttype,
"nlink", (long)sbuf.st_nlink,
"devnums", Py_BuildValue("(s,O,i)", devtype,
major_num, minor_num));
@@ -194,6 +214,9 @@
PyErr_SetString(UnknownFileTypeError, filename);
return_val = NULL;
}
+
+ free(mounttype);
+
Py_DECREF(size);
Py_DECREF(inode);
Py_DECREF(devloc);
@@ -401,6 +424,60 @@
}
#endif /* HAVE_LCHOWN */
+/* ------------- Get mount filesystem type from statfs -------------------- */
+
+#if defined(BSD) || defined(__APPLE__) /* FreeBSD, NetBSD, OpenBSD, Apple/Darwin */
+char *get_mounttype(char *filename, long int mode) {
+ char *stat_filename, *mounttype;
+ struct statfs sfsbuf;
+ int res, pos;
+
+ mounttype = malloc(MFSNAMELEN);
+
+ if (S_ISLNK(mode)) {
+ /* statfs() on links is unreliable, so do it on the directory */
+ pos = strlen(filename) - strlen(strrchr(filename, '/')) + 1;
+ stat_filename = malloc(pos + 1);
+ strncpy(stat_filename, filename, pos);
+ stat_filename[pos] = '\0';
+ } else {
+ stat_filename = filename;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ res = statfs(stat_filename, &sfsbuf);
+ Py_END_ALLOW_THREADS
+
+ if (S_ISLNK(mode)) {
+ free(stat_filename);
+ }
+
+ if (res != 0) {
+ free(mounttype);
+ return NULL;
+ }
+
+ strcpy(mounttype, sfsbuf.f_fstypename);
+ return mounttype;
+}
+#elif defined(MS_WINDOWS)
+char *get_mounttype(char *filename, long int mode) {
+ char *mounttype;
+
+ mounttype = strdup("ntfs");
+
+ return mounttype;
+}
+#else /* Linux */
+char *get_mounttype(char *filename, long int mode) {
+ char *mounttype;
+
+ mounttype = strdup("ext3");
+
+ return mounttype;
+}
+#endif
+
/* ------------- Python export lists -------------------------------- */
static PyMethodDef CMethods[] = {
diff -Nur rdiff-backup-cvs/rdiff_backup/rpath.py rdiff-backup-fstypebsd/rdiff_backup/rpath.py
--- rdiff-backup-cvs/rdiff_backup/rpath.py 2006-01-13 00:29:47.000000000 -0500
+++ rdiff-backup-fstypebsd/rdiff_backup/rpath.py 2006-08-21 22:16:22.000000000 -0400
@@ -310,6 +310,7 @@
elif key == 'atime' and not Globals.preserve_atime: pass
elif key == 'ctime': pass
elif key == 'nlink': pass
+ elif key == 'mounttype': pass
elif key == 'size' and not self.isreg(): pass
elif key == 'ea' and not Globals.eas_active: pass
elif key == 'acl' and not Globals.acls_active: pass
@@ -348,6 +349,7 @@
pass # Special files may be replaced with empty regular files
elif key == 'atime' and not Globals.preserve_atime: pass
elif key == 'ctime': pass
+ elif key == 'mounttype': pass
elif key == 'devloc' or key == 'nlink': pass
elif key == 'size' and not self.isreg(): pass
elif key == 'inode': pass
@@ -386,6 +388,7 @@
elif key == 'type' and not compare_type: pass
elif key == 'atime' and not Globals.preserve_atime: pass
elif key == 'ctime': pass
+ elif key == 'mounttype': pass
elif key == 'devloc' or key == 'nlink': pass
elif key == 'size' and (not self.isreg() or not compare_size): pass
elif key == 'inode' and (not self.isreg() or not compare_inodes):
@@ -534,6 +537,10 @@
"""Device number file resides on"""
return self.data['devloc']
+ def getmounttype(self):
+ """Filesystem type file resides on"""
+ return self.data['mounttype']
+
def getnumlinks(self):
"""Number of places inode is linked to"""
if self.data.has_key('nlink'): return self.data['nlink']
diff -Nur rdiff-backup-cvs/rdiff_backup/selection.py rdiff-backup-fstypebsd/rdiff_backup/selection.py
--- rdiff-backup-cvs/rdiff_backup/selection.py 2006-01-09 23:56:44.000000000 -0500
+++ rdiff-backup-fstypebsd/rdiff_backup/selection.py 2006-08-21 22:15:39.000000000 -0400
@@ -257,6 +257,8 @@
self.add_selection_func(self.regexp_get_sf(arg, 0))
elif opt == "--exclude-special-files":
self.add_selection_func(self.special_get_sf(0))
+ elif opt == "--exclude-mount-type":
+ self.add_selection_func(self.mounttype_get_sf(arg, 0))
elif opt == "--include":
self.add_selection_func(self.glob_get_sf(arg, 1))
elif opt == "--include-filelist":
@@ -274,6 +276,8 @@
self.add_selection_func(self.special_get_sf(1))
elif opt == "--include-symbolic-links":
self.add_selection_func(self.symlinks_get_sf(1))
+ elif opt == "--include-mount-type":
+ self.add_selection_func(self.mounttype_get_sf(arg, 1))
else: assert 0, "Bad selection option %s" % opt
except SelectError, e: self.parse_catch_error(e)
assert filelists_index == len(filelists)
@@ -451,6 +455,16 @@
sel_func.name = "Match other filesystems"
return sel_func
+ def mounttype_get_sf(self, type, include):
+ """Return selection function matching mount filesystem type"""
+ assert include == 0 or include == 1
+ def sel_func(rp):
+ if rp.getmounttype() == type: return include
+ else: return None
+ sel_func.exclude = not include
+ sel_func.name = "Match filesystem type"
+ return sel_func
+
def regexp_get_sf(self, regexp_string, include):
"""Return selection function given by regexp_string"""
assert include == 0 or include == 1
