chiark / gitweb /
Get rid of some more unused defines and dirs
[elogind.git] / src / shared / util.c
index 6f0fb162deb137df474219ae41f89b322b2c165f..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;
@@ -1353,8 +1333,7 @@ char *cescape(const char *s) {
 
         assert(s);
 
-        /* Does C style string escaping. May be reversed with
-         * cunescape(). */
+        /* Does C style string escaping. */
 
         r = new(char, strlen(s)*4 + 1);
         if (!r)
@@ -1368,17 +1347,13 @@ char *cescape(const char *s) {
         return r;
 }
 
-static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+static int cunescape_one(const char *p, size_t length, char *ret) {
         int r = 1;
 
         assert(p);
         assert(*p);
         assert(ret);
 
-        /* Unescapes C style. Returns the unescaped character in ret,
-         * unless we encountered a \u sequence in which case the full
-         * unicode character is returned in ret_unicode, instead. */
-
         if (length != (size_t) -1 && length < 1)
                 return -EINVAL;
 
@@ -1435,92 +1410,15 @@ static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_
                 if (b < 0)
                         return -EINVAL;
 
-                /* Don't allow NUL bytes */
+                /* don't allow NUL bytes */
                 if (a == 0 && b == 0)
                         return -EINVAL;
 
-                *ret = (char) ((a << 4U) | b);
+                *ret = (char) ((a << 4) | b);
                 r = 3;
                 break;
         }
 
-        case 'u': {
-                /* C++11 style 16bit unicode */
-
-                int a[4];
-                unsigned i;
-                uint32_t c;
-
-                if (length != (size_t) -1 && length < 5)
-                        return -EINVAL;
-
-                for (i = 0; i < 4; i++) {
-                        a[i] = unhexchar(p[1 + i]);
-                        if (a[i] < 0)
-                                return a[i];
-                }
-
-                c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
-
-                /* Don't allow 0 chars */
-                if (c == 0)
-                        return -EINVAL;
-
-                if (c < 128)
-                        *ret = c;
-                else {
-                        if (!ret_unicode)
-                                return -EINVAL;
-
-                        *ret = 0;
-                        *ret_unicode = c;
-                }
-
-                r = 5;
-                break;
-        }
-
-        case 'U': {
-                /* C++11 style 32bit unicode */
-
-                int a[8];
-                unsigned i;
-                uint32_t c;
-
-                if (length != (size_t) -1 && length < 9)
-                        return -EINVAL;
-
-                for (i = 0; i < 8; i++) {
-                        a[i] = unhexchar(p[1 + i]);
-                        if (a[i] < 0)
-                                return a[i];
-                }
-
-                c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
-                    ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] <<  8U) | ((uint32_t) a[6] <<  4U) |  (uint32_t) a[7];
-
-                /* Don't allow 0 chars */
-                if (c == 0)
-                        return -EINVAL;
-
-                /* Don't allow invalid code points */
-                if (!unichar_is_valid(c))
-                        return -EINVAL;
-
-                if (c < 128)
-                        *ret = c;
-                else {
-                        if (!ret_unicode)
-                                return -EINVAL;
-
-                        *ret = 0;
-                        *ret_unicode = c;
-                }
-
-                r = 9;
-                break;
-        }
-
         case '0':
         case '1':
         case '2':
@@ -1530,8 +1428,7 @@ static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_
         case '6':
         case '7': {
                 /* octal encoding */
-                int a, b, c;
-                uint32_t m;
+                int a, b, c, m;
 
                 if (length != (size_t) -1 && length < 4)
                         return -EINVAL;
@@ -1553,11 +1450,11 @@ static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_
                         return -EINVAL;
 
                 /* Don't allow bytes above 255 */
-                m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
+                m = (a << 6) | (b << 3) | c;
                 if (m > 255)
                         return -EINVAL;
 
-                *ret = m;
+                *ret = (char) m;
                 r = 3;
                 break;
         }
@@ -1569,13 +1466,12 @@ static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *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. */
 
@@ -1583,71 +1479,47 @@ 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);
 
         for (f = s, t = r + pl; f < s + length; f++) {
                 size_t remaining;
-                uint32_t u;
-                char c;
                 int k;
 
                 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;
-                        }
-
-                        free(r);
-                        return -EINVAL;
-                }
-
-                k = cunescape_one(f + 1, remaining - 1, &c, &u);
+                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;
-                        }
-
-                        free(r);
-                        return k;
+                        /* Invalid escape code, let's take it literal then */
+                        *(t++) = '\\';
+                        continue;
                 }
 
-                if (c != 0)
-                        /* Non-Unicode? Let's encode this directly */
-                        *(t++) = c;
-                else
-                        /* Unicode? Then let's encode this in UTF-8 */
-                        t += utf8_encode_unichar(t, u);
-
                 f += k;
+                t++;
         }
 
         *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) {
@@ -1656,7 +1528,7 @@ char *xescape(const char *s, const char *bad) {
 
         /* Escapes all chars in bad, in addition to \ and all special
          * chars, in \xFF style escaping. May be reversed with
-         * cunescape(). */
+         * cunescape. */
 
         r = new(char, strlen(s) * 4 + 1);
         if (!r)
@@ -3116,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)
@@ -3132,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);
 
@@ -3676,15 +3743,14 @@ int touch(const char *path) {
         return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
 }
 
-static char *unquote(const char *s, const char* quotes) {
+char *unquote(const char *s, const char* quotes) {
         size_t l;
         assert(s);
 
         /* This is rather stupid, simply removes the heading and
          * trailing quotes if there is one. Doesn't care about
-         * escaping or anything.
-         *
-         * DON'T USE THIS FOR NEW CODE ANYMORE!*/
+         * escaping or anything. We should make this smarter one
+         * day... */
 
         l = strlen(s);
         if (l < 2)
@@ -3696,6 +3762,36 @@ static char *unquote(const char *s, const char* quotes) {
         return strdup(s);
 }
 
+char *normalize_env_assignment(const char *s) {
+        _cleanup_free_ char *value = NULL;
+        const char *eq;
+        char *p, *name;
+
+        eq = strchr(s, '=');
+        if (!eq) {
+                char *r, *t;
+
+                r = strdup(s);
+                if (!r)
+                        return NULL;
+
+                t = strstrip(r);
+                if (t != r)
+                        memmove(r, t, strlen(t) + 1);
+
+                return r;
+        }
+
+        name = strndupa(s, eq - s);
+        p = strdupa(eq + 1);
+
+        value = unquote(strstrip(p), QUOTES);
+        if (!value)
+                return NULL;
+
+        return strjoin(strstrip(name), "=", value, NULL);
+}
+
 int wait_for_terminate(pid_t pid, siginfo_t *status) {
         siginfo_t dummy;
 
@@ -3843,7 +3939,7 @@ static char *tag_to_udev_node(const char *tagvalue, const char *by) {
         _cleanup_free_ char *t = NULL, *u = NULL;
         size_t enc_len;
 
-        u = unquote(tagvalue, QUOTES);
+        u = unquote(tagvalue, "\"\'");
         if (!u)
                 return NULL;
 
@@ -6830,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;
@@ -6932,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
@@ -7276,26 +7372,20 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                                 return -EINVAL;
                         }
 
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
+                        if (!GREEDY_REALLOC(s, allocated, sz+2))
                                 return -ENOMEM;
 
                         if (flags & UNQUOTE_CUNESCAPE) {
-                                uint32_t u;
-
-                                r = cunescape_one(*p, (size_t) -1, &c, &u);
+                                r = cunescape_one(*p, (size_t) -1, &c);
                                 if (r < 0)
                                         return -EINVAL;
 
                                 (*p) += r - 1;
+                        }
 
-                                if (c != 0)
-                                        s[sz++] = c; /* normal explicit char */
-                                else
-                                        sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
-                        } else
-                                s[sz++] = c;
-
+                        s[sz++] = c;
                         state = VALUE;
+
                         break;
 
                 case SINGLE_QUOTE:
@@ -7323,25 +7413,18 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                                 return -EINVAL;
                         }
 
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
+                        if (!GREEDY_REALLOC(s, allocated, sz+2))
                                 return -ENOMEM;
 
                         if (flags & UNQUOTE_CUNESCAPE) {
-                                uint32_t u;
-
-                                r = cunescape_one(*p, (size_t) -1, &c, &u);
+                                r = cunescape_one(*p, (size_t) -1, &c);
                                 if (r < 0)
                                         return -EINVAL;
 
                                 (*p) += r - 1;
+                        }
 
-                                if (c != 0)
-                                        s[sz++] = c;
-                                else
-                                        sz += utf8_encode_unichar(s + sz, u);
-                        } else
-                                s[sz++] = c;
-
+                        s[sz++] = c;
                         state = SINGLE_QUOTE;
                         break;
 
@@ -7368,25 +7451,18 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                                 return -EINVAL;
                         }
 
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
+                        if (!GREEDY_REALLOC(s, allocated, sz+2))
                                 return -ENOMEM;
 
                         if (flags & UNQUOTE_CUNESCAPE) {
-                                uint32_t u;
-
-                                r = cunescape_one(*p, (size_t) -1, &c, &u);
+                                r = cunescape_one(*p, (size_t) -1, &c);
                                 if (r < 0)
                                         return -EINVAL;
 
                                 (*p) += r - 1;
+                        }
 
-                                if (c != 0)
-                                        s[sz++] = c;
-                                else
-                                        sz += utf8_encode_unichar(s + sz, u);
-                        } else
-                                s[sz++] = c;
-
+                        s[sz++] = c;
                         state = DOUBLE_QUOTE;
                         break;
 
@@ -7718,7 +7794,7 @@ int fd_setcrtime(int fd, usec_t usec) {
         return 0;
 }
 
-int chattr_fd(int fd, unsigned value, unsigned mask) {
+int chattr_fd(int fd, bool b, unsigned mask) {
         unsigned old_attr, new_attr;
 
         assert(fd >= 0);
@@ -7729,17 +7805,21 @@ int chattr_fd(int fd, unsigned value, unsigned mask) {
         if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
                 return -errno;
 
-        new_attr = (old_attr & ~mask) | (value & mask);
+        if (b)
+                new_attr = old_attr | mask;
+        else
+                new_attr = old_attr & ~mask;
+
         if (new_attr == old_attr)
                 return 0;
 
         if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
                 return -errno;
 
-        return 1;
+        return 0;
 }
 
-int chattr_path(const char *p, unsigned value, unsigned mask) {
+int chattr_path(const char *p, bool b, unsigned mask) {
         _cleanup_close_ int fd = -1;
 
         assert(p);
@@ -7751,7 +7831,29 @@ int chattr_path(const char *p, unsigned value, unsigned mask) {
         if (fd < 0)
                 return -errno;
 
-        return chattr_fd(fd, value, mask);
+        return chattr_fd(fd, b, mask);
+}
+
+int change_attr_fd(int fd, unsigned value, unsigned mask) {
+        unsigned old_attr, new_attr;
+
+        assert(fd >= 0);
+
+        if (mask == 0)
+                return 0;
+
+        if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
+                return -errno;
+
+        new_attr = (old_attr & ~mask) |(value & mask);
+
+        if (new_attr == old_attr)
+                return 0;
+
+        if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
+                return -errno;
+
+        return 0;
 }
 
 int read_attr_fd(int fd, unsigned *ret) {
@@ -8075,64 +8177,3 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
 
         return 0;
 }
-
-char *shell_maybe_quote(const char *s) {
-        const char *p;
-        char *r, *t;
-
-        assert(s);
-
-        /* Encloses a string in double quotes if necessary to make it
-         * OK as shell string. */
-
-        for (p = s; *p; p++)
-                if (*p <= ' ' ||
-                    *p >= 127 ||
-                    strchr(SHELL_NEED_QUOTES, *p))
-                        break;
-
-        if (!*p)
-                return strdup(s);
-
-        r = new(char, 1+strlen(s)*2+1+1);
-        if (!r)
-                return NULL;
-
-        t = r;
-        *(t++) = '"';
-        t = mempcpy(t, s, p - s);
-
-        for (; *p; p++) {
-
-                if (strchr(SHELL_NEED_ESCAPE, *p))
-                        *(t++) = '\\';
-
-                *(t++) = *p;
-        }
-
-        *(t++)= '"';
-        *t = 0;
-
-        return r;
-}
-
-int parse_mode(const char *s, mode_t *ret) {
-        char *x;
-        long l;
-
-        assert(s);
-        assert(ret);
-
-        errno = 0;
-        l = strtol(s, &x, 8);
-        if (errno != 0)
-                return -errno;
-
-        if (!x || x == s || *x)
-                return -EINVAL;
-        if (l < 0 || l  > 07777)
-                return -ERANGE;
-
-        *ret = (mode_t) l;
-        return 0;
-}