diff options
Diffstat (limited to 'shared/systemd/src/basic/fs-util.c')
| -rw-r--r-- | shared/systemd/src/basic/fs-util.c | 115 |
1 files changed, 52 insertions, 63 deletions
diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index 75df913a..e50252cb 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -123,7 +123,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */ return -errno; - /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fallback to the racy TOCTOU + /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */ if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) @@ -234,6 +234,7 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { bool do_chown, do_chmod; struct stat st; + int r; /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no * point in time the access mode is above the old access mode under the old ownership or the new @@ -264,65 +265,23 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { if (do_chown && do_chmod) { mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ - if (((minimal ^ st.st_mode) & 07777) != 0) - if (fchmod_opath(fd, minimal & 07777) < 0) - return -errno; + if (((minimal ^ st.st_mode) & 07777) != 0) { + r = fchmod_opath(fd, minimal & 07777); + if (r < 0) + return r; + } } if (do_chown) if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0) return -errno; - if (do_chmod) - if (fchmod_opath(fd, mode & 07777) < 0) - return -errno; - - return do_chown || do_chmod; -} - -int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid) { - bool do_chown, do_chmod; - struct stat st; - - assert(path); - - /* Change ownership and access mode of the specified path, see description of fchmod_and_chown(). - * Should only be used on trusted paths. */ - - if (lstat(path, &st) < 0) - return -errno; - - do_chown = - (uid != UID_INVALID && st.st_uid != uid) || - (gid != GID_INVALID && st.st_gid != gid); - - do_chmod = - !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */ - ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) || - do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown() - * modifies the access mode too */ - - if (mode == MODE_INVALID) - mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */ - else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0) - return -EINVAL; /* insist on the right file type if it was specified */ - - if (do_chown && do_chmod) { - mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ - - if (((minimal ^ st.st_mode) & 07777) != 0) - if (chmod(path, minimal & 07777) < 0) - return -errno; + if (do_chmod) { + r = fchmod_opath(fd, mode & 07777); + if (r < 0) + return r; } - if (do_chown) - if (lchown(path, uid, gid) < 0) - return -errno; - - if (do_chmod) - if (chmod(path, mode & 07777) < 0) - return -errno; - return do_chown || do_chmod; } @@ -358,6 +317,25 @@ int fchmod_opath(int fd, mode_t m) { return 0; } +int futimens_opath(int fd, const struct timespec ts[2]) { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + /* Similar to fchmod_path() but for futimens() */ + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } + + return 0; +} + int stat_warn_permissions(const char *path, const struct stat *st) { assert(path); assert(st); @@ -771,7 +749,7 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), "Detected unsafe path transition %s %s %s during canonicalization of %s.", - n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path); + strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path); } static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { @@ -784,7 +762,7 @@ static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), "Detected autofs mount point %s during canonicalization of %s.", - n1, path); + strna(n1), path); } int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { @@ -1304,16 +1282,25 @@ int chase_symlinks_and_stat( 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) - return -errno; + if (access(p, mode) < 0) { + if (errno != ENOENT) + return -errno; - return r; + /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's + * make things debuggable and distinguish the two. */ + + if (proc_mounted() == 0) + return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot + * environment. */ + + return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ + } + + return 0; } void unlink_tempfilep(char (*p)[]) { @@ -1434,6 +1421,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { return 0; } +#if 0 /* NM_IGNORED */ int fsync_directory_of_file(int fd) { _cleanup_free_ char *path = NULL; _cleanup_close_ int dfd = -1; @@ -1447,9 +1435,9 @@ int fsync_directory_of_file(int fd) { if (r < 0) { log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", fd, - r == -EOPNOTSUPP ? ", ignoring" : ""); + r == -ENOSYS ? ", ignoring" : ""); - if (r == -EOPNOTSUPP) + if (r == -ENOSYS) /* If /proc is not available, we're most likely running in some * chroot environment, and syncing the directory is not very * important in that case. Let's just silently do nothing. */ @@ -1470,6 +1458,7 @@ int fsync_directory_of_file(int fd) { return 0; } +#endif /* NM_IGNORED */ int fsync_full(int fd) { int r, q; @@ -1587,7 +1576,7 @@ static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { d = opendir(p); if (!d) { - if (errno == ENOENT) /* Doesn't have slaves */ + if (errno == ENOENT) /* Doesn't have underlying devices */ return false; return -errno; @@ -1603,7 +1592,7 @@ static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { if (errno != 0) return -errno; - break; /* No more slaves */ + break; /* No more underlying devices */ } q = path_join(p, de->d_name); |