summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--src/tmpfiles/tmpfiles.c42
2 files changed, 42 insertions, 2 deletions
diff --git a/TODO b/TODO
index 95a806be65..f1a0bcf493 100644
--- a/TODO
+++ b/TODO
@@ -78,8 +78,6 @@ Features:
* remove any syslog support from log.c -- we probably can't do this before split-off udev is gone for good
-* tmpfiles: when traversing the tree, check for bind mount points with nametohandle()
-
* fedora: connect the timer units of a service to the service via Also= in [Install], and maybe introduce timers.target
* fedora: F20: go timer units all the way, leave cron.daily for cron
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index b93d8988fc..4a76a2f778 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -213,6 +213,42 @@ static bool unix_socket_alive(const char *fn) {
return true;
}
+static int dir_is_mount_point(DIR *d, const char *subdir) {
+ struct file_handle *h;
+ int mount_id_parent, mount_id;
+ int r_p, r;
+
+ h = alloca(MAX_HANDLE_SZ);
+
+ h->handle_bytes = MAX_HANDLE_SZ;
+ r_p = name_to_handle_at(dirfd(d), ".", h, &mount_id_parent, 0);
+ if (r_p < 0)
+ r_p = -errno;
+
+ h->handle_bytes = MAX_HANDLE_SZ;
+ r = name_to_handle_at(dirfd(d), subdir, h, &mount_id, 0);
+ if (r < 0)
+ r = -errno;
+
+ /* got no handle; make no assumptions, return error */
+ if (r_p < 0 && r < 0)
+ return r_p;
+
+ /* got both handles; if they differ, it is a mount point */
+ if (r_p >= 0 && r >= 0)
+ return mount_id_parent != mount_id;
+
+ /* got only one handle; assume different mount points if one
+ * of both queries was not supported by the filesystem */
+ if (r_p == -ENOSYS || r_p == -ENOTSUP || r == -ENOSYS || r == -ENOTSUP)
+ return true;
+
+ /* return error */
+ if (r_p < 0)
+ return r_p;
+ return r;
+}
+
static int dir_cleanup(
Item *i,
const char *p,
@@ -252,6 +288,12 @@ static int dir_cleanup(
if (s.st_dev != rootdev)
continue;
+ /* Try to detect bind mounts of the same filesystem instance; they
+ * do not differ in device major/minors. This type of query is not
+ * supported on all kernels or filesystem types though. */
+ if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0)
+ continue;
+
/* Do not delete read-only files owned by root */
if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
continue;