]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udevadm: cleanup-db: don't delete information for kept db entries 22202/head
authorMartin Wilck <mwilck@suse.com>
Thu, 20 Jan 2022 13:31:45 +0000 (14:31 +0100)
committerMartin Wilck <mwilck@suse.com>
Fri, 21 Jan 2022 12:32:20 +0000 (13:32 +0100)
devices with the db_persist property won't be deleted during database
cleanup. This applies to dm and md devices in particular.
For such devices, we should also keep the files under /run/udev/links,
/run/udev/tags, and /run/udev/watch, to make sure that after restart,
udevd has the same information about the devices as it did before
the cleanup.

If we don't do this, a lower-priority device that is discovered in
the coldplug phase may take over symlinks from a device that persisted.
Not removing the watches also enables udevd to resume watching a device
after restart.

Signed-off-by: Martin Wilck <mwilck@suse.com>
src/udev/udevadm-info.c

index cb5e74a1b61f59860c3b1e117d558cf98da13340..018a52ada80da6eac682ce85bf414701274072a3 100644 (file)
@@ -256,6 +256,62 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
         }
 }
 
+/*
+ * Assume that dir is a directory with file names matching udev data base
+ * entries for devices in /run/udev/data (such as "b8:16"), and removes
+ * all files except those that haven't been deleted in /run/udev/data
+ * (i.e. they were skipped during db cleanup because of the db_persist flag).
+ * Returns true if the directory is empty after cleanup.
+ */
+static bool cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
+        unsigned int kept = 0;
+
+        assert(dir && datadir);
+
+        FOREACH_DIRENT_ALL(dent, dir, break) {
+                struct stat data_stats, link_stats;
+
+                if (dot_or_dot_dot(dent->d_name))
+                        continue;
+                if (fstatat(dirfd(dir), dent->d_name, &link_stats, AT_SYMLINK_NOFOLLOW) < 0) {
+                        if (errno != ENOENT)
+                                kept++;
+                        continue;
+                }
+
+                if (fstatat(dirfd(datadir), dent->d_name, &data_stats, 0) < 0)
+                        (void) unlinkat(dirfd(dir), dent->d_name,
+                                        S_ISDIR(link_stats.st_mode) ? AT_REMOVEDIR : 0);
+                else
+                        /* The entry still exists under /run/udev/data */
+                        kept++;
+        }
+
+        return kept == 0;
+}
+
+static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
+
+        assert(dir && datadir);
+
+        FOREACH_DIRENT_ALL(dent, dir, break) {
+                struct stat stats;
+
+                if (dot_or_dot_dot(dent->d_name))
+                        continue;
+                if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
+                        continue;
+                if (S_ISDIR(stats.st_mode)) {
+                        _cleanup_closedir_ DIR *dir2 = NULL;
+
+                        dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+                        if (dir2 && cleanup_dir_after_db_cleanup(dir2, datadir))
+                                (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+                } else
+                        (void) unlinkat(dirfd(dir), dent->d_name, 0);
+        }
+}
+
 static void cleanup_db(void) {
         _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
 
@@ -265,11 +321,11 @@ static void cleanup_db(void) {
 
         dir2 = opendir("/run/udev/links");
         if (dir2)
-                cleanup_dir(dir2, 0, 2);
+                cleanup_dirs_after_db_cleanup(dir2, dir1);
 
         dir3 = opendir("/run/udev/tags");
         if (dir3)
-                cleanup_dir(dir3, 0, 2);
+                cleanup_dirs_after_db_cleanup(dir3, dir1);
 
         dir4 = opendir("/run/udev/static_node-tags");
         if (dir4)
@@ -277,7 +333,7 @@ static void cleanup_db(void) {
 
         dir5 = opendir("/run/udev/watch");
         if (dir5)
-                cleanup_dir(dir5, 0, 1);
+                cleanup_dir_after_db_cleanup(dir5, dir1);
 }
 
 static int query_device(QueryType query, sd_device* device) {