chiark / gitweb /
Get rid of some more unused defines and dirs
[elogind.git] / src / shared / util.c
index d7e4318defa903c52864f0e62540035eaa5db969..72984735ced828e33c2eca18df7828fdf1db58c9 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <signal.h>
 #include <libintl.h>
+#include <locale.h>
 #include <stdio.h>
 #include <syslog.h>
 #include <sched.h>
@@ -150,27 +151,6 @@ char* endswith(const char *s, const char *postfix) {
         return (char*) s + sl - pl;
 }
 
-char* endswith_no_case(const char *s, const char *postfix) {
-        size_t sl, pl;
-
-        assert(s);
-        assert(postfix);
-
-        sl = strlen(s);
-        pl = strlen(postfix);
-
-        if (pl == 0)
-                return (char*) s + sl;
-
-        if (sl < pl)
-                return NULL;
-
-        if (strcasecmp(s + sl - pl, postfix) != 0)
-                return NULL;
-
-        return (char*) s + sl - pl;
-}
-
 char* first_word(const char *s, const char *word) {
         size_t sl, wl;
         const char *p;
@@ -1486,13 +1466,12 @@ static int cunescape_one(const char *p, size_t length, char *ret) {
         return r;
 }
 
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
         char *r, *t;
         const char *f;
         size_t pl;
 
         assert(s);
-        assert(ret);
 
         /* Undoes C style string escaping, and optionally prefixes it. */
 
@@ -1500,7 +1479,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
 
         r = new(char, pl+length+1);
         if (!r)
-                return -ENOMEM;
+                return NULL;
 
         if (prefix)
                 memcpy(r, prefix, pl);
@@ -1512,31 +1491,17 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
                 remaining = s + length - f;
                 assert(remaining > 0);
 
-                if (*f != '\\') {
-                        /* A literal literal, copy verbatim */
+                if (*f != '\\' || remaining == 1) {
+                        /* a literal literal, or a trailing backslash, copy verbatim */
                         *(t++) = *f;
                         continue;
                 }
 
-                if (remaining == 1) {
-                        if (flags & UNESCAPE_RELAX) {
-                                /* A trailing backslash, copy verbatim */
-                                *(t++) = *f;
-                                continue;
-                        }
-
-                        return -EINVAL;
-                }
-
                 k = cunescape_one(f + 1, remaining - 1, t);
                 if (k < 0) {
-                        if (flags & UNESCAPE_RELAX) {
-                                /* Invalid escape code, let's take it literal then */
-                                *(t++) = '\\';
-                                continue;
-                        }
-
-                        return k;
+                        /* Invalid escape code, let's take it literal then */
+                        *(t++) = '\\';
+                        continue;
                 }
 
                 f += k;
@@ -1544,17 +1509,17 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
         }
 
         *t = 0;
-
-        *ret = r;
-        return t - r;
+        return r;
 }
 
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
-        return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+char *cunescape_length(const char *s, size_t length) {
+        return cunescape_length_with_prefix(s, length, NULL);
 }
 
-int cunescape(const char *s, UnescapeFlags flags, char **ret) {
-        return cunescape_length(s, strlen(s), flags, ret);
+char *cunescape(const char *s) {
+        assert(s);
+
+        return cunescape_length(s, strlen(s));
 }
 
 char *xescape(const char *s, const char *bad) {
@@ -3023,14 +2988,101 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         return 0;
 }
 
-bool is_temporary_fs(const struct statfs *s) {
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+        _cleanup_closedir_ DIR *d = NULL;
+        int ret = 0;
+
+        assert(fd >= 0);
+
+        /* This returns the first error we run into, but nevertheless
+         * tries to go on. This closes the passed fd. */
+
+        d = fdopendir(fd);
+        if (!d) {
+                safe_close(fd);
+
+                return errno == ENOENT ? 0 : -errno;
+        }
+
+        for (;;) {
+                struct dirent *de;
+                bool is_dir, keep_around;
+                struct stat st;
+                int r;
+
+                errno = 0;
+                de = readdir(d);
+                if (!de) {
+                        if (errno != 0 && ret == 0)
+                                ret = -errno;
+                        return ret;
+                }
+
+                if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+                        continue;
+
+                if (de->d_type == DT_UNKNOWN ||
+                    honour_sticky ||
+                    (de->d_type == DT_DIR && root_dev)) {
+                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        is_dir = S_ISDIR(st.st_mode);
+                        keep_around =
+                                honour_sticky &&
+                                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                (st.st_mode & S_ISVTX);
+                } else {
+                        is_dir = de->d_type == DT_DIR;
+                        keep_around = false;
+                }
+
+                if (is_dir) {
+                        int subdir_fd;
+
+                        /* if root_dev is set, remove subdirectories only, if device is same as dir */
+                        if (root_dev && st.st_dev != root_dev->st_dev)
+                                continue;
+
+                        subdir_fd = openat(fd, de->d_name,
+                                           O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                        if (subdir_fd < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
+                        if (r < 0 && ret == 0)
+                                ret = r;
+
+                        if (!keep_around)
+                                if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+                                        if (ret == 0 && errno != ENOENT)
+                                                ret = -errno;
+                                }
+
+                } else if (!only_dirs && !keep_around) {
+
+                        if (unlinkat(fd, de->d_name, 0) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                        }
+                }
+        }
+}
+
+_pure_ static int is_temporary_fs(struct statfs *s) {
         assert(s);
 
         return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
                F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
 }
 
-int fd_is_temporary_fs(int fd) {
+int is_fd_on_temporary_fs(int fd) {
         struct statfs s;
 
         if (fstatfs(fd, &s) < 0)
@@ -3039,6 +3091,114 @@ int fd_is_temporary_fs(int fd) {
         return is_temporary_fs(&s);
 }
 
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+        struct statfs s;
+
+        assert(fd >= 0);
+
+        if (fstatfs(fd, &s) < 0) {
+                safe_close(fd);
+                return -errno;
+        }
+
+        /* We refuse to clean disk file systems with this call. This
+         * is extra paranoia just to be sure we never ever remove
+         * non-state data */
+        if (!is_temporary_fs(&s)) {
+                log_error("Attempted to remove disk file system, and we can't allow that.");
+                safe_close(fd);
+                return -EPERM;
+        }
+
+        return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
+}
+
+static int file_is_priv_sticky(const char *p) {
+        struct stat st;
+
+        assert(p);
+
+        if (lstat(p, &st) < 0)
+                return -errno;
+
+        return
+                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                (st.st_mode & S_ISVTX);
+}
+
+static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
+        int fd, r;
+        struct statfs s;
+
+        assert(path);
+
+        /* We refuse to clean the root file system with this
+         * call. This is extra paranoia to never cause a really
+         * seriously broken system. */
+        if (path_equal(path, "/")) {
+                log_error("Attempted to remove entire root file system, and we can't allow that.");
+                return -EPERM;
+        }
+
+        fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+        if (fd < 0) {
+
+                if (errno != ENOTDIR && errno != ELOOP)
+                        return -errno;
+
+                if (!dangerous) {
+                        if (statfs(path, &s) < 0)
+                                return -errno;
+
+                        if (!is_temporary_fs(&s)) {
+                                log_error("Attempted to remove disk file system, and we can't allow that.");
+                                return -EPERM;
+                        }
+                }
+
+                if (delete_root && !only_dirs)
+                        if (unlink(path) < 0 && errno != ENOENT)
+                                return -errno;
+
+                return 0;
+        }
+
+        if (!dangerous) {
+                if (fstatfs(fd, &s) < 0) {
+                        safe_close(fd);
+                        return -errno;
+                }
+
+                if (!is_temporary_fs(&s)) {
+                        log_error("Attempted to remove disk file system, and we can't allow that.");
+                        safe_close(fd);
+                        return -EPERM;
+                }
+        }
+
+        r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
+        if (delete_root) {
+
+                if (honour_sticky && file_is_priv_sticky(path) > 0)
+                        return r;
+
+                if (rmdir(path) < 0 && errno != ENOENT) {
+                        if (r == 0)
+                                r = -errno;
+                }
+        }
+
+        return r;
+}
+
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
+}
+
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+}
+
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         assert(path);
 
@@ -6766,9 +6926,9 @@ int umount_recursive(const char *prefix, int flags) {
                                 continue;
                         }
 
-                        r = cunescape(path, UNESCAPE_RELAX, &p);
-                        if (r < 0)
-                                return r;
+                        p = cunescape(path);
+                        if (!p)
+                                return -ENOMEM;
 
                         if (!path_startswith(p, prefix))
                                 continue;
@@ -6868,9 +7028,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
                                 continue;
                         }
 
-                        r = cunescape(path, UNESCAPE_RELAX, &p);
-                        if (r < 0)
-                                return r;
+                        p = cunescape(path);
+                        if (!p)
+                                return -ENOMEM;
 
                         /* Let's ignore autofs mounts.  If they aren't
                          * triggered yet, we want to avoid triggering