]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/fs-util.c
util-lib: rename fd_check_fstype to fd_is_fs_type
[thirdparty/systemd.git] / src / basic / fs-util.c
index 0a3e983631b09082260f056e16544dc7de2cfb18..475400177a696449898b384b9d85e1d361a5d146 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <errno.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <linux/magic.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -104,7 +105,6 @@ int rmdir_parents(const char *path, const char *stop) {
         return 0;
 }
 
-
 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
         struct stat buf;
         int ret;
@@ -308,7 +308,7 @@ int fd_warn_permissions(const char *path, int fd) {
         if (st.st_mode & 0002)
                 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
 
-        if (getpid() == 1 && (st.st_mode & 0044) != 0044)
+        if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
                 log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
 
         return 0;
@@ -324,7 +324,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
                 mkdir_parents(path, 0755);
 
         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
-                        (mode == 0 || mode == MODE_INVALID) ? 0644 : mode);
+                  IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
         if (fd < 0)
                 return -errno;
 
@@ -359,22 +359,25 @@ int touch(const char *path) {
 }
 
 int symlink_idempotent(const char *from, const char *to) {
-        _cleanup_free_ char *p = NULL;
         int r;
 
         assert(from);
         assert(to);
 
         if (symlink(from, to) < 0) {
+                _cleanup_free_ char *p = NULL;
+
                 if (errno != EEXIST)
                         return -errno;
 
                 r = readlink_malloc(to, &p);
-                if (r < 0)
+                if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
+                        return -EEXIST;
+                if (r < 0) /* Any other error? In that case propagate it as is */
                         return r;
 
-                if (!streq(p, from))
-                        return -EINVAL;
+                if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */
+                        return -EEXIST;
         }
 
         return 0;
@@ -446,6 +449,7 @@ int mkfifo_atomic(const char *path, mode_t mode) {
 
 int get_files_in_directory(const char *path, char ***list) {
         _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
         size_t bufsize = 0, n = 0;
         _cleanup_strv_free_ char **l = NULL;
 
@@ -459,16 +463,7 @@ int get_files_in_directory(const char *path, char ***list) {
         if (!d)
                 return -errno;
 
-        for (;;) {
-                struct dirent *de;
-
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno > 0)
-                        return -errno;
-                if (!de)
-                        break;
-
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 dirent_ensure_type(d, de);
 
                 if (!dirent_is_file(de))
@@ -514,7 +509,7 @@ static int getenv_tmp_dir(const char **ret_path) {
                         r = -ENOTDIR;
                         goto next;
                 }
-                if (!path_is_safe(e)) {
+                if (!path_is_normalized(e)) {
                         r = -EPERM;
                         goto next;
                 }
@@ -602,6 +597,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
         _cleanup_close_ int fd = -1;
         unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+        bool exists = true;
         char *todo;
         int r;
 
@@ -665,9 +661,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 todo += m;
 
+                /* Empty? Then we reached the end. */
+                if (isempty(first))
+                        break;
+
                 /* Just a single slash? Then we reached the end. */
-                if (isempty(first) || path_equal(first, "/"))
+                if (path_equal(first, "/")) {
+                        /* Preserve the trailing slash */
+                        if (!strextend(&done, "/", NULL))
+                                return -ENOMEM;
+
                         break;
+                }
 
                 /* Just a dot? Then let's eat this up. */
                 if (path_equal(first, "/."))
@@ -707,13 +712,35 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 /* Otherwise let's see what this is. */
                 child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
-                if (child < 0)
+                if (child < 0) {
+
+                        if (errno == ENOENT &&
+                            (flags & CHASE_NONEXISTENT) &&
+                            (isempty(todo) || path_is_normalized(todo))) {
+
+                                /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
+                                 * what we got so far. But don't allow this if the remaining path contains "../ or "./"
+                                 * or something else weird. */
+
+                                if (!strextend(&done, first, todo, NULL))
+                                        return -ENOMEM;
+
+                                exists = false;
+                                break;
+                        }
+
                         return -errno;
+                }
 
                 if (fstat(child, &st) < 0)
                         return -errno;
+                if ((flags & CHASE_NO_AUTOFS) &&
+                    fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
+                        return -EREMOTE;
 
                 if (S_ISLNK(st.st_mode)) {
+                        char *joined;
+
                         _cleanup_free_ char *destination = NULL;
 
                         /* This is a symlink, in this case read the destination. But let's make sure we don't follow
@@ -737,9 +764,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                 if (fd < 0)
                                         return -errno;
 
-                                free_and_replace(buffer, destination);
-
-                                todo = buffer;
                                 free(done);
 
                                 /* Note that we do not revalidate the root, we take it as is. */
@@ -751,19 +775,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                                 return -ENOMEM;
                                 }
 
-                        } else {
-                                char *joined;
-
-                                /* A relative destination. If so, this is what we'll prefix what's left to do with what
-                                 * we just read, and start the loop again, but remain in the current directory. */
-
+                                /* Prefix what's left to do with what we just read, and start the loop again, but
+                                 * remain in the current directory. */
+                                joined = strjoin(destination, todo);
+                        } else
                                 joined = strjoin("/", destination, todo);
-                                if (!joined)
-                                        return -ENOMEM;
+                        if (!joined)
+                                return -ENOMEM;
 
-                                free(buffer);
-                                todo = buffer = joined;
-                        }
+                        free(buffer);
+                        todo = buffer = joined;
 
                         continue;
                 }
@@ -790,8 +811,25 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return -ENOMEM;
         }
 
-        *ret = done;
-        done = NULL;
+        if (ret) {
+                *ret = done;
+                done = NULL;
+        }
 
-        return 0;
+        return exists;
+}
+
+int access_fd(int fd, int mode) {
+        char p[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
+        int r;
+
+        /* Like access() but operates on an already open fd */
+
+        xsprintf(p, "/proc/self/fd/%i", fd);
+
+        r = access(p, mode);
+        if (r < 0)
+                r = -errno;
+
+        return r;
 }