stat-util: unify code that checks whether something is a regular file

Let's add a common implementation for regular file checks, that are
careful to return the right error code (EISDIR/EISLNK/EBADFD) when we
are encountering a wrong file node.
This commit is contained in:
Lennart Poettering 2018-02-19 18:01:05 +01:00
parent 9c66f52813
commit 3cc4411403
8 changed files with 73 additions and 58 deletions

View File

@ -232,23 +232,18 @@ int btrfs_subvol_get_read_only_fd(int fd) {
}
int btrfs_reflink(int infd, int outfd) {
struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
/* Make sure we invoke the ioctl on a regular file, so that no
* device driver accidentally gets it. */
/* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
if (fstat(outfd, &st) < 0)
return -errno;
if (!S_ISREG(st.st_mode))
return -EINVAL;
r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
r = fd_verify_regular(outfd);
if (r < 0)
return r;
if (ioctl(outfd, BTRFS_IOC_CLONE, infd) < 0)
return -errno;
return 0;
@ -261,21 +256,17 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
.src_length = sz,
.dest_offset = out_offset,
};
struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
assert(sz > 0);
if (fstat(outfd, &st) < 0)
return -errno;
if (!S_ISREG(st.st_mode))
return -EINVAL;
r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
r = fd_verify_regular(outfd);
if (r < 0)
return r;
if (ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args) < 0)
return -errno;
return 0;
@ -760,15 +751,13 @@ int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQu
}
int btrfs_defrag_fd(int fd) {
struct stat st;
int r;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
if (!S_ISREG(st.st_mode))
return -EINVAL;
r = fd_verify_regular(fd);
if (r < 0)
return r;
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
return -errno;

View File

@ -269,3 +269,32 @@ int path_is_temporary_fs(const char *path) {
return fd_is_temporary_fs(fd);
}
int stat_verify_regular(const struct stat *st) {
assert(st);
/* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
* code. */
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (!S_ISREG(st->st_mode))
return -EBADFD;
return 0;
}
int fd_verify_regular(int fd) {
struct stat st;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
return stat_verify_regular(&st);
}

View File

@ -75,3 +75,6 @@ int path_is_temporary_fs(const char *path);
* signed/unsigned comparison, because the magic can be 32 bit unsigned.
*/
#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
int stat_verify_regular(const struct stat *st);
int fd_verify_regular(int fd);

View File

@ -37,6 +37,7 @@
#include "import-common.h"
#include "missing.h"
#include "ratelimit.h"
#include "stat-util.h"
#include "string-util.h"
#include "util.h"
@ -319,8 +320,9 @@ int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType
if (fstat(sfd, &e->st) < 0)
return -errno;
if (!S_ISREG(e->st.st_mode))
return -ENOTTY;
r = stat_verify_regular(&e->st);
if (r < 0)
return r;
/* Try to take a reflink snapshot of the file, if we can t make the export atomic */
tfd = reflink_snapshot(sfd, path);

View File

@ -42,6 +42,7 @@
#include "random-util.h"
#include "sd-event.h"
#include "set.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "xattr-util.h"
@ -643,6 +644,8 @@ static int journal_file_verify_header(JournalFile *f) {
}
static int journal_file_fstat(JournalFile *f) {
int r;
assert(f);
assert(f->fd >= 0);
@ -652,10 +655,9 @@ static int journal_file_fstat(JournalFile *f) {
f->last_stat_usec = now(CLOCK_MONOTONIC);
/* Refuse dealing with with files that aren't regular */
if (S_ISDIR(f->last_stat.st_mode))
return -EISDIR;
if (!S_ISREG(f->last_stat.st_mode))
return -EBADFD;
r = stat_verify_regular(&f->last_stat);
if (r < 0)
return r;
/* Refuse appending to files that are already deleted */
if (f->last_stat.st_nlink <= 0)

View File

@ -1303,14 +1303,10 @@ static int add_any_file(
r = log_debug_errno(errno, "Failed to fstat file '%s': %m", path);
goto finish;
}
if (S_ISDIR(st.st_mode)) {
log_debug("Uh, file '%s' is a directory? Refusing.", path);
r = -EISDIR;
goto finish;
}
if (!S_ISREG(st.st_mode)) {
log_debug("Uh, file '%s' is not a regular file? Refusing.", path);
r = -EBADFD;
r = stat_verify_regular(&st);
if (r < 0) {
log_debug_errno(r, "Refusing to open '%s', as it is not a regular file.", path);
goto finish;
}
@ -2074,14 +2070,9 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
goto fail;
}
if (S_ISDIR(st.st_mode)) {
r = -EISDIR;
r = stat_verify_regular(&st);
if (r < 0)
goto fail;
}
if (!S_ISREG(st.st_mode)) {
r = -EBADFD;
goto fail;
}
r = add_any_file(j, fds[i], NULL);
if (r < 0)

View File

@ -1284,10 +1284,10 @@ static int unit_file_load(
info->type = UNIT_FILE_TYPE_MASKED;
return 0;
}
if (S_ISDIR(st.st_mode))
return -EISDIR;
if (!S_ISREG(st.st_mode))
return -ENOTTY;
r = stat_verify_regular(&st);
if (r < 0)
return r;
f = fdopen(fd, "re");
if (!f)
@ -2163,12 +2163,9 @@ int unit_file_link(
if (lstat(full, &st) < 0)
return -errno;
if (S_ISLNK(st.st_mode))
return -ELOOP;
if (S_ISDIR(st.st_mode))
return -EISDIR;
if (!S_ISREG(st.st_mode))
return -ENOTTY;
r = stat_verify_regular(&st);
if (r < 0)
return r;
q = in_search_path(&paths, *i);
if (q < 0)

View File

@ -27,6 +27,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "loop-util.h"
#include "stat-util.h"
int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
const struct loop_info64 info = {
@ -37,7 +38,7 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
_cleanup_free_ char *loopdev = NULL;
struct stat st;
LoopDevice *d;
int nr;
int nr, r;
assert(fd >= 0);
assert(ret);
@ -69,8 +70,9 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
return 0;
}
if (!S_ISREG(st.st_mode))
return -EINVAL;
r = stat_verify_regular(&st);
if (r < 0)
return r;
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
if (control < 0)