diff --git a/direct.c b/direct.c index cf53657..67bed20 100644 --- a/direct.c +++ b/direct.c @@ -129,7 +129,7 @@ direct_opendir (struct ovl_layer *l, const char *path) cleanup_close int cleanup_fd = -1; DIR *dp = NULL; - cleanup_fd = TEMP_FAILURE_RETRY (openat (l->fd, path, O_DIRECTORY)); + cleanup_fd = TEMP_FAILURE_RETRY (safe_openat (l->fd, path, O_DIRECTORY, 0)); if (cleanup_fd < 0) return NULL; @@ -151,7 +151,7 @@ direct_closedir (void *dirp) static int direct_openat (struct ovl_layer *l, const char *path, int flags, mode_t mode) { - return TEMP_FAILURE_RETRY (openat (l->fd, path, flags, mode)); + return TEMP_FAILURE_RETRY (safe_openat (l->fd, path, flags, mode)); } static ssize_t diff --git a/main.c b/main.c index 56fa7ac..d83ac3f 100644 --- a/main.c +++ b/main.c @@ -494,7 +494,7 @@ set_fd_opaque (int fd) return -1; } create_opq_whiteout: - opq_whiteout_fd = TEMP_FAILURE_RETRY (openat (fd, OPAQUE_WHITEOUT, O_CREAT|O_WRONLY|O_NONBLOCK, 0700)); + opq_whiteout_fd = TEMP_FAILURE_RETRY (safe_openat (fd, OPAQUE_WHITEOUT, O_CREAT|O_WRONLY|O_NONBLOCK, 0700)); return (opq_whiteout_fd >= 0 || ret == 0) ? 0 : -1; } @@ -2445,7 +2445,7 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct if (ret < 0) goto out; - ret = dfd = TEMP_FAILURE_RETRY (openat (lo->workdir_fd, wd_tmp_file_name, O_RDONLY)); + ret = dfd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, O_RDONLY, 0)); if (ret < 0) goto out; @@ -2645,11 +2645,11 @@ copyup (struct ovl_data *lo, struct ovl_node *node) goto success; } - ret = sfd = node->layer->ds->openat (node->layer, node->path, O_RDONLY|O_NONBLOCK, 0755); + ret = sfd = node->layer->ds->openat (node->layer, node->path, O_RDONLY|O_NONBLOCK, 0); if (sfd < 0) goto exit; - ret = dfd = TEMP_FAILURE_RETRY (openat (lo->workdir_fd, wd_tmp_file_name, O_CREAT|O_WRONLY, st.st_mode)); + ret = dfd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, O_CREAT|O_WRONLY, st.st_mode)); if (dfd < 0) goto exit; @@ -2865,7 +2865,7 @@ empty_dirfd (int fd) { int dfd; - dfd = openat (dirfd (dp), dent->d_name, O_DIRECTORY); + dfd = safe_openat (dirfd (dp), dent->d_name, O_DIRECTORY, 0); if (dfd < 0) return -1; @@ -2893,7 +2893,7 @@ empty_dir (struct ovl_layer *l, const char *path) cleanup_close int cleanup_fd = -1; int ret; - cleanup_fd = TEMP_FAILURE_RETRY (openat (l->fd, path, O_DIRECTORY)); + cleanup_fd = TEMP_FAILURE_RETRY (safe_openat (l->fd, path, O_DIRECTORY, 0)); if (cleanup_fd < 0) return -1; @@ -3171,7 +3171,7 @@ direct_create_file (struct ovl_layer *l, int dirfd, const char *path, uid_t uid, /* try to create directly the file if it doesn't need to be chowned. */ if (uid == lo->uid && gid == lo->gid) { - ret = TEMP_FAILURE_RETRY (openat (get_upper_layer (lo)->fd, path, flags, mode)); + ret = TEMP_FAILURE_RETRY (safe_openat (get_upper_layer (lo)->fd, path, flags, mode)); if (ret >= 0) return ret; /* if it fails (e.g. there is a whiteout) then fallback to create it in @@ -3180,7 +3180,7 @@ direct_create_file (struct ovl_layer *l, int dirfd, const char *path, uid_t uid, sprintf (wd_tmp_file_name, "%lu", get_next_wd_counter ()); - fd = TEMP_FAILURE_RETRY (openat (lo->workdir_fd, wd_tmp_file_name, flags, mode)); + fd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, flags, mode)); if (fd < 0) return -1; if (uid != lo->uid || gid != lo->gid) @@ -3590,7 +3590,7 @@ ovl_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, stru switch (mode & S_IFMT) { case S_IFREG: - cleaned_up_fd = fd = TEMP_FAILURE_RETRY (openat (dirfd, node->path, O_NOFOLLOW|O_NONBLOCK|(to_set & FUSE_SET_ATTR_SIZE ? O_WRONLY : 0))); + cleaned_up_fd = fd = TEMP_FAILURE_RETRY (safe_openat (dirfd, node->path, O_NOFOLLOW|O_NONBLOCK|(to_set & FUSE_SET_ATTR_SIZE ? O_WRONLY : 0), 0)); if (fd < 0) { fuse_reply_err (req, errno); @@ -3599,7 +3599,7 @@ ovl_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, stru break; case S_IFDIR: - cleaned_up_fd = fd = TEMP_FAILURE_RETRY (openat (dirfd, node->path, O_NOFOLLOW|O_NONBLOCK)); + cleaned_up_fd = fd = TEMP_FAILURE_RETRY (safe_openat (dirfd, node->path, O_NOFOLLOW|O_NONBLOCK, 0)); if (fd < 0) { if (errno != ELOOP) @@ -3611,7 +3611,7 @@ ovl_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, stru break; case S_IFLNK: - cleaned_up_fd = TEMP_FAILURE_RETRY (openat (dirfd, node->path, O_PATH|O_NOFOLLOW|O_NONBLOCK)); + cleaned_up_fd = TEMP_FAILURE_RETRY (safe_openat (dirfd, node->path, O_PATH|O_NOFOLLOW|O_NONBLOCK, 0)); if (cleaned_up_fd < 0) { fuse_reply_err (req, errno); @@ -3981,7 +3981,7 @@ ovl_rename_exchange (fuse_req_t req, fuse_ino_t parent, const char *name, if (pnode == NULL) goto error; - ret = TEMP_FAILURE_RETRY (openat (node_dirfd (pnode), pnode->path, O_DIRECTORY)); + ret = TEMP_FAILURE_RETRY (safe_openat (node_dirfd (pnode), pnode->path, O_DIRECTORY, 0)); if (ret < 0) goto error; srcfd = ret; @@ -3990,7 +3990,7 @@ ovl_rename_exchange (fuse_req_t req, fuse_ino_t parent, const char *name, if (destpnode == NULL) goto error; - ret = TEMP_FAILURE_RETRY (openat (node_dirfd (destpnode), destpnode->path, O_DIRECTORY)); + ret = TEMP_FAILURE_RETRY (safe_openat (node_dirfd (destpnode), destpnode->path, O_DIRECTORY, 0)); if (ret < 0) goto error; destfd = ret; @@ -4106,7 +4106,7 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name, if (pnode == NULL) goto error; - ret = TEMP_FAILURE_RETRY (openat (node_dirfd (pnode), pnode->path, O_DIRECTORY)); + ret = TEMP_FAILURE_RETRY (safe_openat (node_dirfd (pnode), pnode->path, O_DIRECTORY, 0)); if (ret < 0) goto error; srcfd = ret; @@ -4115,7 +4115,7 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name, if (destpnode == NULL) goto error; - ret = TEMP_FAILURE_RETRY (openat (node_dirfd (destpnode), destpnode->path, O_DIRECTORY)); + ret = TEMP_FAILURE_RETRY (safe_openat (node_dirfd (destpnode), destpnode->path, O_DIRECTORY, 0)); if (ret < 0) goto error; destfd = ret; @@ -4622,7 +4622,7 @@ direct_fsync (struct ovl_layer *l, int fd, const char *path, int datasync) if (fd < 0) { - cfd = openat (l->fd, path, O_NOFOLLOW|O_DIRECTORY); + cfd = safe_openat (l->fd, path, O_NOFOLLOW|O_DIRECTORY, 0); if (cfd < 0) return cfd; fd = cfd; @@ -4813,7 +4813,7 @@ ovl_fallocate (fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t len } dirfd = node_dirfd (node); - fd = openat (dirfd, node->path, O_NONBLOCK|O_NOFOLLOW|O_WRONLY, 0755); + fd = safe_openat (dirfd, node->path, O_NONBLOCK|O_NOFOLLOW|O_WRONLY, 0); if (fd < 0) { fuse_reply_err (req, errno); @@ -4878,7 +4878,7 @@ ovl_copy_file_range (fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fus return; } - fd_dest = TEMP_FAILURE_RETRY (openat (node_dirfd (dnode), dnode->path, O_NONBLOCK|O_NOFOLLOW|O_WRONLY)); + fd_dest = TEMP_FAILURE_RETRY (safe_openat (node_dirfd (dnode), dnode->path, O_NONBLOCK|O_NOFOLLOW|O_WRONLY, 0)); if (fd_dest < 0) { fuse_reply_err (req, errno); diff --git a/utils.c b/utils.c index 2f65918..43073f1 100644 --- a/utils.c +++ b/utils.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(expression) \ @@ -35,6 +39,62 @@ __result; })) #endif +#ifndef RESOLVE_IN_ROOT +# define RESOLVE_IN_ROOT 0x10 +#endif +#ifndef __NR_openat2 +# define __NR_openat2 437 +#endif + +/* List of all valid flags for the open/openat flags argument: */ +#define VALID_OPEN_FLAGS \ + (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \ + O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | O_SYNC | O_DSYNC | \ + FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ + O_NOATIME | O_CLOEXEC | O_PATH | O_TMPFILE) + +static int +syscall_openat2 (int dirfd, const char *path, uint64_t flags, uint64_t mode, uint64_t resolve) +{ + struct openat2_open_how + { + uint64_t flags; + uint64_t mode; + uint64_t resolve; + } + how = + { + .flags = flags & VALID_OPEN_FLAGS, + .mode = (flags & O_CREAT) ? (mode & 07777) : 0, + .resolve = resolve, + }; + + return (int) syscall (__NR_openat2, dirfd, path, &how, sizeof (how), 0); +} + +int +safe_openat (int dirfd, const char *pathname, int flags, mode_t mode) +{ + static bool openat2_supported = true; + + if (openat2_supported) + { + int ret; + + ret = syscall_openat2 (dirfd, pathname, flags, mode, RESOLVE_IN_ROOT); + if (ret < 0) + { + if (errno == ENOSYS) + openat2_supported = false; + if (errno == ENOSYS || errno == EINVAL) + goto fallback; + } + return ret; + } + fallback: + return openat (dirfd, pathname, flags, mode); +} + int file_exists_at (int dirfd, const char *pathname) { @@ -148,7 +208,7 @@ open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd, { out[0] = '\0'; - *fd = l->ds->openat (l, path, O_NONBLOCK|O_NOFOLLOW|flags, 0755); + *fd = l->ds->openat (l, path, O_NONBLOCK|O_NOFOLLOW|flags, 0); if (*fd < 0 && (errno == ELOOP || errno == EISDIR || errno == ENXIO)) { strconcat3 (out, PATH_MAX, l->path, "/", path); diff --git a/utils.h b/utils.h index 3cdae2e..b447df9 100644 --- a/utils.h +++ b/utils.h @@ -53,4 +53,6 @@ int open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int * void statx_to_stat (struct statx *stx, struct stat *st); # endif +int safe_openat (int dirfd, const char *pathname, int flags, mode_t mode); + #endif