chiark / gitweb /
core: rework how we match mount units against each other
authorLennart Poettering <lennart@poettering.net>
Thu, 26 Sep 2013 18:14:24 +0000 (20:14 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 26 Sep 2013 18:20:30 +0000 (20:20 +0200)
Previously to automatically create dependencies between mount units we
matched every mount unit agains all others resulting in O(n^2)
complexity. On setups with large amounts of mount units this might make
things slow.

This change replaces the matching code to use a hashtable that is keyed
by a path prefix, and points to a set of units that require that path to
be around. When a new mount unit is installed it is hence sufficient to
simply look up this set of units via its own file system paths to know
which units to order after itself.

This patch also changes all unit types to only create automatic mount
dependencies via the RequiresMountsFor= logic, and this is exposed to
the outside to make things more transparent.

With this change we still have some O(n) complexities in place when
handling mounts, but that's currently unavoidable due to kernel APIs,
and still substantially better than O(n^2) as before.

https://bugs.freedesktop.org/show_bug.cgi?id=69740

17 files changed:
src/core/automount.c
src/core/automount.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/path.c
src/core/path.h
src/core/socket.c
src/core/socket.h
src/core/swap.c
src/core/swap.h
src/core/unit.c
src/core/unit.h
src/shared/socket-util.c
src/shared/socket-util.h

index 67623929c900788a7aa6fc0b3868277c0f56b81e..d1379e091376a5ce11ee1a4d6466c0c5f75b6f45 100644 (file)
@@ -117,42 +117,17 @@ static void automount_done(Unit *u) {
         a->tokens = NULL;
 }
 
-int automount_add_one_mount_link(Automount *a, Mount *m) {
+static int automount_add_mount_links(Automount *a) {
+        _cleanup_free_ char *parent = NULL;
         int r;
 
         assert(a);
-        assert(m);
-
-        if (UNIT(a)->load_state != UNIT_LOADED ||
-            UNIT(m)->load_state != UNIT_LOADED)
-                return 0;
-
-        if (!path_startswith(a->where, m->where))
-                return 0;
 
-        if (path_equal(a->where, m->where))
-                return 0;
-
-        r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+        r = path_get_parent(a->where, &parent);
         if (r < 0)
                 return r;
 
-        return 0;
-}
-
-static int automount_add_mount_links(Automount *a) {
-        Unit *other;
-        int r;
-
-        assert(a);
-
-        LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT]) {
-                r = automount_add_one_mount_link(a, MOUNT(other));
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
+        return unit_require_mounts_for(UNIT(a), parent);
 }
 
 static int automount_add_default_dependencies(Automount *a) {
index 0c6b8a72e922a6cdc1482730ff33a90e2632edd7..a7a25d34e0aaf1750012682779ccec37455eea6e 100644 (file)
@@ -62,8 +62,6 @@ extern const UnitVTable automount_vtable;
 
 int automount_send_ready(Automount *a, int status);
 
-int automount_add_one_mount_link(Automount *a, Mount *m);
-
 const char* automount_state_to_string(AutomountState i) _const_;
 AutomountState automount_state_from_string(const char *s) _pure_;
 
index 25bd3aae47b862a6b78860d8f5ab66ee3f63db7a..31fb7bcd3fbe8b1a6b9b0a62c146522246e1e9f0 100644 (file)
@@ -116,7 +116,7 @@ Unit.PropagateReloadTo,          config_parse_unit_deps,             UNIT_PROPAG
 Unit.ReloadPropagatedFrom,       config_parse_unit_deps,             UNIT_RELOAD_PROPAGATED_FROM,   0
 Unit.PropagateReloadFrom,        config_parse_unit_deps,             UNIT_RELOAD_PROPAGATED_FROM,   0
 Unit.PartOf,                     config_parse_unit_deps,             UNIT_PART_OF,                  0
-Unit.RequiresMountsFor,          config_parse_unit_requires_mounts_for, 0,                          offsetof(Unit, requires_mounts_for)
+Unit.RequiresMountsFor,          config_parse_unit_requires_mounts_for, 0,                          0
 Unit.StopWhenUnneeded,           config_parse_bool,                  0,                             offsetof(Unit, stop_when_unneeded)
 Unit.RefuseManualStart,          config_parse_bool,                  0,                             offsetof(Unit, refuse_manual_start)
 Unit.RefuseManualStop,           config_parse_bool,                  0,                             offsetof(Unit, refuse_manual_stop)
index 74454abe49888bce1232ce9504e0fc07a9aa589d..70ea13aa8005c3a33469e2ce8a7b4b0640e0f7a7 100644 (file)
@@ -1773,33 +1773,48 @@ int config_parse_unit_condition_null(const char *unit,
 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
 
-int config_parse_unit_requires_mounts_for(const char *unit,
-                                          const char *filename,
-                                          unsigned line,
-                                          const char *section,
-                                          const char *lvalue,
-                                          int ltype,
-                                          const char *rvalue,
-                                          void *data,
-                                          void *userdata) {
+int config_parse_unit_requires_mounts_for(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         Unit *u = userdata;
+        char *state;
+        size_t l;
+        char *w;
         int r;
-        bool empty_before;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        empty_before = !u->requires_mounts_for;
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                _cleanup_free_ char *n;
+
+                n = strndup(w, l);
+                if (!n)
+                        return log_oom();
 
-        r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
-                                   rvalue, data, userdata);
+                if (!utf8_is_valid(n)) {
+                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                                   "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
+                        continue;
+                }
 
-        /* Make it easy to find units with requires_mounts set */
-        if (empty_before && u->requires_mounts_for)
-                LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
+                r = unit_require_mounts_for(u, n);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to add required mount for, ignoring: %s", rvalue);
+                        continue;
+                }
+        }
 
         return r;
 }
index f70ff03033f85d58808ec84d40acc427a7b19ea3..bc57e4a1fd31ce1a14293ec5fc770d4afb92b38c 100644 (file)
@@ -770,6 +770,9 @@ void manager_free(Manager *m) {
         for (i = 0; i < RLIMIT_NLIMITS; i++)
                 free(m->rlimit[i]);
 
+        assert(hashmap_isempty(m->units_requiring_mounts_for));
+        hashmap_free(m->units_requiring_mounts_for);
+
         free(m);
 }
 
@@ -782,9 +785,11 @@ int manager_enumerate(Manager *m) {
         /* Let's ask every type to load all units from disk/kernel
          * that it might know */
         for (c = 0; c < _UNIT_TYPE_MAX; c++)
-                if (unit_vtable[c]->enumerate)
-                        if ((q = unit_vtable[c]->enumerate(m)) < 0)
+                if (unit_vtable[c]->enumerate) {
+                        q = unit_vtable[c]->enumerate(m);
+                        if (q < 0)
                                 r = q;
+                }
 
         manager_dispatch_load_queue(m);
         return r;
@@ -2764,6 +2769,41 @@ void manager_status_printf(Manager *m, bool ephemeral, const char *status, const
         va_end(ap);
 }
 
+int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) {
+        _cleanup_free_ char *p = NULL;
+        Unit *found;
+
+        assert(m);
+        assert(path);
+        assert(suffix);
+        assert(_found);
+
+        p = unit_name_from_path(path, suffix);
+        if (!p)
+                return -ENOMEM;
+
+        found = manager_get_unit(m, p);
+        if (!found) {
+                *_found = NULL;
+                return 0;
+        }
+
+        *_found = found;
+        return 1;
+}
+
+Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
+        char p[strlen(path)+1];
+
+        assert(m);
+        assert(path);
+
+        strcpy(p, path);
+        path_kill_slashes(p);
+
+        return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
+}
+
 void watch_init(Watch *w) {
         assert(w);
 
index 3969553e3ed791e83a3e7cdf470454cab25d1eb1..a3049b5e5b665fe7937ca4cf2a3274cdb8670732 100644 (file)
@@ -103,9 +103,6 @@ struct Manager {
          * type we maintain a per type linked list */
         LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]);
 
-        /* To optimize iteration of units that have requires_mounts_for set */
-        LIST_HEAD(Unit, has_requires_mounts_for);
-
         /* Units that need to be loaded */
         LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */
 
@@ -251,6 +248,11 @@ struct Manager {
 
         char *switch_root;
         char *switch_root_init;
+
+        /* This maps all possible path prefixes to the units needing
+         * them. It's a hashmap with a path string as key and a Set as
+         * value where Unit objects are contained. */
+        Hashmap *units_requiring_mounts_for;
 };
 
 int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **m);
@@ -263,6 +265,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
 Job *manager_get_job(Manager *m, uint32_t id);
 Unit *manager_get_unit(Manager *m, const char *name);
 
+int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found);
+
 int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
 
 int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret);
@@ -316,4 +320,6 @@ void manager_recheck_journal(Manager *m);
 void manager_set_show_status(Manager *m, bool b);
 void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) _printf_attr_(4,5);
 
+Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
+
 void watch_init(Watch *w);
index 78c5c1e6d6f7a523720eefccea21bd9578abc362..3d46557fb1d42864fcfac04006fb21f8ba7f1d3e 100644 (file)
@@ -157,138 +157,58 @@ _pure_ static MountParameters* get_mount_parameters(Mount *m) {
 }
 
 static int mount_add_mount_links(Mount *m) {
-        Unit *other;
-        int r;
+        _cleanup_free_ char *parent = NULL;
         MountParameters *pm;
-
-        assert(m);
-
-        pm = get_mount_parameters_fragment(m);
-
-        /* Adds in links to other mount points that might lie below or
-         * above us in the hierarchy */
-
-        LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_MOUNT]) {
-                Mount *n = MOUNT(other);
-                MountParameters *pn;
-
-                if (n == m)
-                        continue;
-
-                if (UNIT(n)->load_state != UNIT_LOADED)
-                        continue;
-
-                pn = get_mount_parameters_fragment(n);
-
-                if (path_startswith(m->where, n->where)) {
-
-                        if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
-                                return r;
-
-                        if (pn)
-                                if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
-                                        return r;
-
-                } else if (path_startswith(n->where, m->where)) {
-
-                        if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
-                                return r;
-
-                        if (pm)
-                                if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
-                                        return r;
-
-                } else if (pm && pm->what && path_startswith(pm->what, n->where)) {
-
-                        if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
-                                return r;
-
-                        if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
-                                return r;
-
-                } else if (pn && pn->what && path_startswith(pn->what, m->where)) {
-
-                        if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
-                                return r;
-
-                        if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
-                                return r;
-                }
-        }
-
-        return 0;
-}
-
-static int mount_add_swap_links(Mount *m) {
         Unit *other;
+        Iterator i;
+        Set *s;
         int r;
 
         assert(m);
 
-        LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP]) {
-                r = swap_add_one_mount_link(SWAP(other), m);
+        if (!path_equal(m->where, "/")) {
+                /* Adds in links to other mount points that might lie further
+                 * up in the hierarchy */
+                r = path_get_parent(m->where, &parent);
                 if (r < 0)
                         return r;
-        }
 
-        return 0;
-}
-
-static int mount_add_path_links(Mount *m) {
-        Unit *other;
-        int r;
-
-        assert(m);
-
-        LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH]) {
-                r = path_add_one_mount_link(PATH(other), m);
+                r = unit_require_mounts_for(UNIT(m), parent);
                 if (r < 0)
                         return r;
         }
 
-        return 0;
-}
-
-static int mount_add_automount_links(Mount *m) {
-        Unit *other;
-        int r;
-
-        assert(m);
-
-        LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT]) {
-                r = automount_add_one_mount_link(AUTOMOUNT(other), m);
+        /* Adds in links to other mount points that might be needed
+         * for the source path (if this is a bind mount) to be
+         * available. */
+        pm = get_mount_parameters_fragment(m);
+        if (pm && path_is_absolute(pm->what)) {
+                r = unit_require_mounts_for(UNIT(m), pm->what);
                 if (r < 0)
                         return r;
         }
 
-        return 0;
-}
+        /* Adds in links to other units that use this path or paths
+         * further down in the hierarchy */
+        s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where);
+        SET_FOREACH(other, s, i) {
 
-static int mount_add_socket_links(Mount *m) {
-        Unit *other;
-        int r;
+                if (other->load_state != UNIT_LOADED)
+                        continue;
 
-        assert(m);
+                if (other == UNIT(m))
+                        continue;
 
-        LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET]) {
-                r = socket_add_one_mount_link(SOCKET(other), m);
+                r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true);
                 if (r < 0)
                         return r;
-        }
 
-        return 0;
-}
-
-static int mount_add_requires_mounts_links(Mount *m) {
-        Unit *other;
-        int r;
-
-        assert(m);
-
-        LIST_FOREACH(has_requires_mounts_for, other, UNIT(m)->manager->has_requires_mounts_for) {
-                r = unit_add_one_mount_link(other, m);
-                if (r < 0)
-                        return r;
+                if (UNIT(m)->fragment_path) {
+                        /* If we have fragment configuration, then make this dependency required */
+                        r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return 0;
@@ -567,8 +487,9 @@ static int mount_fix_timeouts(Mount *m) {
 }
 
 static int mount_verify(Mount *m) {
+        _cleanup_free_ char *e = NULL;
         bool b;
-        char *e;
+
         assert(m);
 
         if (UNIT(m)->load_state != UNIT_LOADED)
@@ -577,12 +498,11 @@ static int mount_verify(Mount *m) {
         if (!m->from_fragment && !m->from_proc_self_mountinfo)
                 return -ENOENT;
 
-        if (!(e = unit_name_from_path(m->where, ".mount")))
+        e = unit_name_from_path(m->where, ".mount");
+        if (!e)
                 return -ENOMEM;
 
         b = unit_has_name(UNIT(m), e);
-        free(e);
-
         if (!b) {
                 log_error_unit(UNIT(m)->id,
                                "%s's Where setting doesn't match unit name. Refusing.",
@@ -646,26 +566,6 @@ static int mount_add_extras(Mount *m) {
         if (r < 0)
                 return r;
 
-        r = mount_add_socket_links(m);
-        if (r < 0)
-                return r;
-
-        r = mount_add_swap_links(m);
-        if (r < 0)
-                return r;
-
-        r = mount_add_path_links(m);
-        if (r < 0)
-                return r;
-
-        r = mount_add_requires_mounts_links(m);
-        if (r < 0)
-                return r;
-
-        r = mount_add_automount_links(m);
-        if (r < 0)
-                return r;
-
         r = mount_add_quota_links(m);
         if (r < 0)
                 return r;
@@ -1650,79 +1550,56 @@ fail:
 static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
         int r = 0;
         unsigned i;
-        char *device, *path, *options, *options2, *fstype, *d, *p, *o;
 
         assert(m);
 
         rewind(m->proc_self_mountinfo);
 
         for (i = 1;; i++) {
+                _cleanup_free_ char *device = NULL, *path = NULL, *options = NULL, *options2 = NULL, *fstype = NULL, *d = NULL, *p = NULL, *o = NULL;
                 int k;
 
-                device = path = options = options2 = fstype = d = p = o = NULL;
-
-                if ((k = fscanf(m->proc_self_mountinfo,
-                                "%*s "       /* (1) mount id */
-                                "%*s "       /* (2) parent id */
-                                "%*s "       /* (3) major:minor */
-                                "%*s "       /* (4) root */
-                                "%ms "       /* (5) mount point */
-                                "%ms"        /* (6) mount options */
-                                "%*[^-]"     /* (7) optional fields */
-                                "- "         /* (8) separator */
-                                "%ms "       /* (9) file system type */
-                                "%ms"        /* (10) mount source */
-                                "%ms"        /* (11) mount options 2 */
-                                "%*[^\n]",   /* some rubbish at the end */
-                                &path,
-                                &options,
-                                &fstype,
-                                &device,
-                                &options2)) != 5) {
-
-                        if (k == EOF)
-                                break;
-
+                k = fscanf(m->proc_self_mountinfo,
+                           "%*s "       /* (1) mount id */
+                           "%*s "       /* (2) parent id */
+                           "%*s "       /* (3) major:minor */
+                           "%*s "       /* (4) root */
+                           "%ms "       /* (5) mount point */
+                           "%ms"        /* (6) mount options */
+                           "%*[^-]"     /* (7) optional fields */
+                           "- "         /* (8) separator */
+                           "%ms "       /* (9) file system type */
+                           "%ms"        /* (10) mount source */
+                           "%ms"        /* (11) mount options 2 */
+                           "%*[^\n]",   /* some rubbish at the end */
+                           &path,
+                           &options,
+                           &fstype,
+                           &device,
+                           &options2);
+
+                if (k == EOF)
+                        break;
+
+                if (k != 5) {
                         log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
-                        goto clean_up;
+                        continue;
                 }
 
                 o = strjoin(options, ",", options2, NULL);
-                if (!o) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                if (!o)
+                        return log_oom();
 
-                if (!(d = cunescape(device)) ||
-                    !(p = cunescape(path))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                d = cunescape(device);
+                p = cunescape(path);
+                if (!d || !p)
+                        return log_oom();
 
-                if ((k = mount_add_one(m, d, p, o, fstype, 0, set_flags)) < 0)
+                k = mount_add_one(m, d, p, o, fstype, 0, set_flags);
+                if (k < 0)
                         r = k;
-
-clean_up:
-                free(device);
-                free(path);
-                free(options);
-                free(options2);
-                free(fstype);
-                free(d);
-                free(p);
-                free(o);
         }
 
-finish:
-        free(device);
-        free(path);
-        free(options);
-        free(options2);
-        free(fstype);
-        free(d);
-        free(p);
-        free(o);
-
         return r;
 }
 
index 8a09deb4ff5bbc3fc48ec0c6d49f981141052e23..99e2fedf2989a36095538051f854cb9994ee8b57 100644 (file)
@@ -241,10 +241,6 @@ static bool path_spec_check_good(PathSpec *s, bool initial) {
         return good;
 }
 
-static bool path_spec_startswith(PathSpec *s, const char *what) {
-        return path_startswith(s->path, what);
-}
-
 static void path_spec_mkdir(PathSpec *s, mode_t mode) {
         int r;
 
@@ -301,38 +297,14 @@ static void path_done(Unit *u) {
         path_free_specs(p);
 }
 
-int path_add_one_mount_link(Path *p, Mount *m) {
+static int path_add_mount_links(Path *p) {
         PathSpec *s;
         int r;
 
         assert(p);
-        assert(m);
-
-        if (UNIT(p)->load_state != UNIT_LOADED ||
-            UNIT(m)->load_state != UNIT_LOADED)
-                return 0;
 
         LIST_FOREACH(spec, s, p->specs) {
-                if (!path_spec_startswith(s, m->where))
-                        continue;
-
-                r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
-                                              UNIT(m), true);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-static int path_add_mount_links(Path *p) {
-        Unit *other;
-        int r;
-
-        assert(p);
-
-        LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) {
-                r = path_add_one_mount_link(p, MOUNT(other));
+                r = unit_require_mounts_for(UNIT(p), s->path);
                 if (r < 0)
                         return r;
         }
index 6adab5897d1f95edc93fb9a030d9c603f05d46c3..dec3df70351ea9f6a8b59dde6ce6aea4e6f56fb2 100644 (file)
@@ -90,10 +90,6 @@ struct Path {
         PathResult result;
 };
 
-/* Called from the mount code figure out if a mount is a dependency of
- * any of the paths of this path object */
-int path_add_one_mount_link(Path *p, Mount *m);
-
 void path_free_specs(Path *p);
 
 extern const UnitVTable path_vtable;
index 190b36c3cab4477d8e8b5c6e8c3b02b1f17c3425..6c0ac1a89864fefebff852f8e16de8990f434dda 100644 (file)
@@ -258,53 +258,24 @@ static int socket_verify(Socket *s) {
         return 0;
 }
 
-static bool socket_needs_mount(Socket *s, const char *prefix) {
+static int socket_add_mount_links(Socket *s) {
         SocketPort *p;
-
-        assert(s);
-
-        LIST_FOREACH(port, p, s->ports) {
-
-                if (p->type == SOCKET_SOCKET) {
-                        if (socket_address_needs_mount(&p->address, prefix))
-                                return true;
-                } else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) {
-                        if (path_startswith(p->path, prefix))
-                                return true;
-                }
-        }
-
-        return false;
-}
-
-int socket_add_one_mount_link(Socket *s, Mount *m) {
         int r;
 
         assert(s);
-        assert(m);
 
-        if (UNIT(s)->load_state != UNIT_LOADED ||
-            UNIT(m)->load_state != UNIT_LOADED)
-                return 0;
-
-        if (!socket_needs_mount(s, m->where))
-                return 0;
-
-        r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
+        LIST_FOREACH(port, p, s->ports) {
+                const char *path = NULL;
 
-static int socket_add_mount_links(Socket *s) {
-        Unit *other;
-        int r;
+                if (p->type == SOCKET_SOCKET)
+                        path = socket_address_get_path(&p->address);
+                else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL)
+                        path = p->path;
 
-        assert(s);
+                if (!path)
+                        continue;
 
-        LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) {
-                r = socket_add_one_mount_link(s, MOUNT(other));
+                r = unit_require_mounts_for(UNIT(s), path);
                 if (r < 0)
                         return r;
         }
index 57333226f5f2565cae0a29800b8c3a09f1d6b279..3d7eadc9fe618508975c4d561280c45ee09ebbbe 100644 (file)
@@ -156,10 +156,6 @@ struct Socket {
 /* Called from the service code when collecting fds */
 int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
 
-/* Called from the mount code figure out if a mount is a dependency of
- * any of the sockets of this socket */
-int socket_add_one_mount_link(Socket *s, Mount *m);
-
 /* Called from the service code when a per-connection service ended */
 void socket_connection_unref(Socket *s);
 
index dc6731ab30e00643bf7003090eea7178d9ce18e4..a68ab7cdf802a749af33db5d659c8b5206f6738c 100644 (file)
@@ -137,42 +137,6 @@ static void swap_done(Unit *u) {
         unit_unwatch_timer(u, &s->timer_watch);
 }
 
-int swap_add_one_mount_link(Swap *s, Mount *m) {
-         int r;
-
-        assert(s);
-        assert(m);
-
-        if (UNIT(s)->load_state != UNIT_LOADED ||
-            UNIT(m)->load_state != UNIT_LOADED)
-                return 0;
-
-        if (is_device_path(s->what))
-                return 0;
-
-        if (!path_startswith(s->what, m->where))
-                return 0;
-
-        r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
-static int swap_add_mount_links(Swap *s) {
-        Unit *other;
-        int r;
-
-        assert(s);
-
-        LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
-                if ((r = swap_add_one_mount_link(s, MOUNT(other))) < 0)
-                        return r;
-
-        return 0;
-}
-
 static int swap_add_device_links(Swap *s) {
         SwapParameters *p;
 
@@ -300,11 +264,11 @@ static int swap_load(Unit *u) {
                         if ((r = unit_set_description(u, s->what)) < 0)
                                 return r;
 
-                r = swap_add_device_links(s);
+                r = unit_require_mounts_for(UNIT(s), s->what);
                 if (r < 0)
                         return r;
 
-                r = swap_add_mount_links(s);
+                r = swap_add_device_links(s);
                 if (r < 0)
                         return r;
 
index 7e48c0ea3bb2827757d778d170a5d88cbf25a63c..dd89535895dd53e5af86cb71a90088e56a0387f0 100644 (file)
@@ -107,8 +107,6 @@ struct Swap {
 
 extern const UnitVTable swap_vtable;
 
-int swap_add_one_mount_link(Swap *s, Mount *m);
-
 int swap_dispatch_reload(Manager *m);
 int swap_fd_event(Manager *m, int events);
 
index ab313b9b91b6faa4e8bfdd53a82c9c3e331d2e23..4b9771076aa36539a7ff1423094b19322c299c37 100644 (file)
@@ -374,6 +374,34 @@ static void unit_remove_transient(Unit *u) {
         }
 }
 
+static void unit_free_requires_mounts_for(Unit *u) {
+        char **j;
+
+        STRV_FOREACH(j, u->requires_mounts_for) {
+                char s[strlen(*j) + 1];
+
+                PATH_FOREACH_PREFIX_MORE(s, *j) {
+                        char *y;
+                        Set *x;
+
+                        x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y);
+                        if (!x)
+                                continue;
+
+                        set_remove(x, u);
+
+                        if (set_isempty(x)) {
+                                hashmap_remove(u->manager->units_requiring_mounts_for, y);
+                                free(y);
+                                set_free(x);
+                        }
+                }
+        }
+
+        strv_free(u->requires_mounts_for);
+        u->requires_mounts_for = NULL;
+}
+
 void unit_free(Unit *u) {
         UnitDependency d;
         Iterator i;
@@ -390,6 +418,8 @@ void unit_free(Unit *u) {
                 if (UNIT_VTABLE(u)->done)
                         UNIT_VTABLE(u)->done(u);
 
+        unit_free_requires_mounts_for(u);
+
         SET_FOREACH(t, u->names, i)
                 hashmap_remove_value(u->manager->units, t, u);
 
@@ -408,11 +438,6 @@ void unit_free(Unit *u) {
         for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
                 bidi_set_free(u, u->dependencies[d]);
 
-        if (u->requires_mounts_for) {
-                LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
-                strv_free(u->requires_mounts_for);
-        }
-
         if (u->type != _UNIT_TYPE_INVALID)
                 LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
 
@@ -2659,40 +2684,39 @@ void unit_ref_unset(UnitRef *ref) {
         ref->unit = NULL;
 }
 
-int unit_add_one_mount_link(Unit *u, Mount *m) {
+int unit_add_mount_links(Unit *u) {
         char **i;
+        int r;
 
         assert(u);
-        assert(m);
-
-        if (u->load_state != UNIT_LOADED ||
-            UNIT(m)->load_state != UNIT_LOADED)
-                return 0;
 
         STRV_FOREACH(i, u->requires_mounts_for) {
+                char prefix[strlen(*i) + 1];
 
-                if (UNIT(m) == u)
-                        continue;
+                PATH_FOREACH_PREFIX_MORE(prefix, *i) {
+                        Unit *m;
 
-                if (!path_startswith(*i, m->where))
-                        continue;
-
-                return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
-        }
-
-        return 0;
-}
+                        r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+                        if (m == u)
+                                continue;
 
-int unit_add_mount_links(Unit *u) {
-        Unit *other;
-        int r;
+                        if (m->load_state != UNIT_LOADED)
+                                continue;
 
-        assert(u);
+                        r = unit_add_dependency(u, UNIT_AFTER, m, true);
+                        if (r < 0)
+                                return r;
 
-        LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) {
-                r = unit_add_one_mount_link(u, MOUNT(other));
-                if (r < 0)
-                        return r;
+                        if (m->fragment_path) {
+                                r = unit_add_dependency(u, UNIT_REQUIRES, m, true);
+                                if (r < 0)
+                                        return r;
+                        }
+                }
         }
 
         return 0;
@@ -3012,6 +3036,86 @@ int unit_kill_context(
         return wait_for_exit;
 }
 
+int unit_require_mounts_for(Unit *u, const char *path) {
+        char prefix[strlen(path) + 1], *p;
+        int r;
+
+        assert(u);
+        assert(path);
+
+        /* Registers a unit for requiring a certain path and all its
+         * prefixes. We keep a simple array of these paths in the
+         * unit, since its usually short. However, we build a prefix
+         * table for all possible prefixes so that new appearing mount
+         * units can easily determine which units to make themselves a
+         * dependency of. */
+
+        p = strdup(path);
+        if (!p)
+                return -ENOMEM;
+
+        path_kill_slashes(p);
+
+        if (!path_is_absolute(p)) {
+                free(p);
+                return -EINVAL;
+        }
+
+        if (!path_is_safe(p)) {
+                free(p);
+                return -EPERM;
+        }
+
+        if (strv_contains(u->requires_mounts_for, p)) {
+                free(p);
+                return 0;
+        }
+
+        r = strv_push(&u->requires_mounts_for, p);
+        if (r < 0) {
+                free(p);
+                return r;
+        }
+
+        PATH_FOREACH_PREFIX_MORE(prefix, p) {
+                Set *x;
+
+                x = hashmap_get(u->manager->units_requiring_mounts_for, prefix);
+                if (!x) {
+                        char *q;
+
+                        if (!u->manager->units_requiring_mounts_for) {
+                                u->manager->units_requiring_mounts_for = hashmap_new(string_hash_func, string_compare_func);
+                                if (!u->manager->units_requiring_mounts_for)
+                                        return -ENOMEM;
+                        }
+
+                        q = strdup(prefix);
+                        if (!q)
+                                return -ENOMEM;
+
+                        x = set_new(NULL, NULL);
+                        if (!x) {
+                                free(q);
+                                return -ENOMEM;
+                        }
+
+                        r = hashmap_put(u->manager->units_requiring_mounts_for, q, x);
+                        if (r < 0) {
+                                free(q);
+                                set_free(x);
+                                return r;
+                        }
+                }
+
+                r = set_put(x, u);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
         [UNIT_ACTIVE] = "active",
         [UNIT_RELOADING] = "reloading",
index 0caea183c479ef2cd39460fd60dfefdb5f277948..6dd750f8c2102ac667808622e221360064a848e5 100644 (file)
@@ -173,7 +173,6 @@ struct Unit {
 
         /* Counterparts in the cgroup filesystem */
         char *cgroup_path;
-        bool cgroup_realized;
         CGroupControllerMask cgroup_mask;
 
         UnitRef slice;
@@ -255,6 +254,8 @@ struct Unit {
         bool no_gc:1;
 
         bool in_audit:1;
+
+        bool cgroup_realized:1;
 };
 
 struct UnitStatusMessageFormats {
@@ -589,7 +590,6 @@ void unit_ref_unset(UnitRef *ref);
 #define UNIT_DEREF(ref) ((ref).unit)
 #define UNIT_ISSET(ref) (!!(ref).unit)
 
-int unit_add_one_mount_link(Unit *u, Mount *m);
 int unit_add_mount_links(Unit *u);
 
 int unit_exec_context_defaults(Unit *u, ExecContext *c);
@@ -609,6 +609,8 @@ int unit_kill_context(Unit *u, KillContext *c, bool sigkill, pid_t main_pid, pid
 
 int unit_make_transient(Unit *u);
 
+int unit_require_mounts_for(Unit *u, const char *path);
+
 const char *unit_active_state_to_string(UnitActiveState i) _const_;
 UnitActiveState unit_active_state_from_string(const char *s) _pure_;
 
index c583d3dfea3d8902262de499e646fcb08ad296d3..9224208244a2ed1e269c2aac0d10cdd475831f15 100644 (file)
@@ -486,16 +486,16 @@ bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
         return socket_address_equal(a, &b);
 }
 
-bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
+const char* socket_address_get_path(const SocketAddress *a) {
         assert(a);
 
         if (socket_address_family(a) != AF_UNIX)
-                return false;
+                return NULL;
 
         if (a->sockaddr.un.sun_path[0] == 0)
-                return false;
+                return NULL;
 
-        return path_startswith(a->sockaddr.un.sun_path, prefix);
+        return a->sockaddr.un.sun_path;
 }
 
 bool socket_ipv6_is_supported(void) {
index 7829a337fc643955f1d264ba5e281dd670e8c997..e0b85adf9f94d6cd56b337722b2c141d36b97cde 100644 (file)
@@ -92,7 +92,7 @@ int make_socket_fd(const char* address, int flags);
 
 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
 
-bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
+const char* socket_address_get_path(const SocketAddress *a);
 
 const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
 SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;