Merge pull request #25561 from poettering/btrfs-quota-opath-fix
tmpfiles: fix btrfs quota logic
This commit is contained in:
@@ -780,6 +780,37 @@ int fd_reopen(int fd, int flags) {
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
int fd_reopen_condition(
|
||||
int fd,
|
||||
int flags,
|
||||
int mask,
|
||||
int *ret_new_fd) {
|
||||
|
||||
int r, new_fd;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
|
||||
* flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
|
||||
* needed, but only then. */
|
||||
|
||||
r = fcntl(fd, F_GETFL);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if ((r & mask) == (flags & mask)) {
|
||||
*ret_new_fd = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
new_fd = fd_reopen(fd, flags);
|
||||
if (new_fd < 0)
|
||||
return new_fd;
|
||||
|
||||
*ret_new_fd = new_fd;
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
int read_nr_open(void) {
|
||||
_cleanup_free_ char *nr_open = NULL;
|
||||
int r;
|
||||
|
||||
@@ -108,6 +108,7 @@ static inline int make_null_stdio(void) {
|
||||
})
|
||||
|
||||
int fd_reopen(int fd, int flags);
|
||||
int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
|
||||
int read_nr_open(void);
|
||||
int fd_get_diskseq(int fd, uint64_t *ret);
|
||||
|
||||
|
||||
@@ -43,23 +43,7 @@ static int fd_get_devnum(int fd, BlockDeviceLookupFlag flags, dev_t *ret) {
|
||||
/* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
|
||||
* handing, to get the backing device node. */
|
||||
|
||||
r = fcntl(fd, F_GETFL);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (FLAGS_SET(r, O_PATH)) {
|
||||
_cleanup_close_ int regfd = -1;
|
||||
|
||||
/* The fstat() above we can execute on an O_PATH fd. But the btrfs ioctl we cannot.
|
||||
* Hence acquire a "real" fd first, without the O_PATH flag. */
|
||||
|
||||
regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (regfd < 0)
|
||||
return regfd;
|
||||
|
||||
r = btrfs_get_block_device_fd(regfd, &devnum);
|
||||
} else
|
||||
r = btrfs_get_block_device_fd(fd, &devnum);
|
||||
r = btrfs_get_block_device_fd(fd, &devnum);
|
||||
if (r == -ENOTTY) /* not btrfs */
|
||||
return -ENOTBLK;
|
||||
if (r < 0)
|
||||
@@ -288,21 +272,7 @@ int get_block_device_fd(int fd, dev_t *ret) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = fcntl(fd, F_GETFL);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
if (FLAGS_SET(r, O_PATH) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) {
|
||||
_cleanup_close_ int real_fd = -1;
|
||||
|
||||
/* The fstat() above we can execute on an O_PATH fd. But the btrfs ioctl we cannot. Hence
|
||||
* acquire a "real" fd first, without the O_PATH flag. */
|
||||
|
||||
real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC);
|
||||
if (real_fd < 0)
|
||||
return real_fd;
|
||||
r = btrfs_get_block_device_fd(real_fd, ret);
|
||||
} else
|
||||
r = btrfs_get_block_device_fd(fd, ret);
|
||||
r = btrfs_get_block_device_fd(fd, ret);
|
||||
if (r > 0)
|
||||
return 1;
|
||||
if (r != -ENOTTY) /* not btrfs */
|
||||
|
||||
+16
-13
@@ -107,19 +107,11 @@ int btrfs_subvol_make_fd(int fd, const char *subvolume) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fcntl(fd, F_GETFL);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
if (FLAGS_SET(r, O_PATH)) {
|
||||
/* An O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal with
|
||||
* O_PATH. */
|
||||
|
||||
real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
|
||||
if (real_fd < 0)
|
||||
return real_fd;
|
||||
|
||||
fd = real_fd;
|
||||
}
|
||||
/* If an O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal
|
||||
* with O_PATH. */
|
||||
fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY, O_PATH|O_DIRECTORY, &real_fd);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
strncpy(args.name, subvolume, sizeof(args.name)-1);
|
||||
|
||||
@@ -255,12 +247,17 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
|
||||
|
||||
int btrfs_get_block_device_fd(int fd, dev_t *dev) {
|
||||
struct btrfs_ioctl_fs_info_args fsi = {};
|
||||
_cleanup_close_ int regfd = -1;
|
||||
uint64_t id;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(dev);
|
||||
|
||||
fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, O_PATH, ®fd);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1768,6 +1765,7 @@ int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
|
||||
|
||||
int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) {
|
||||
_cleanup_free_ uint64_t *qgroups = NULL;
|
||||
_cleanup_close_ int real_fd = -1;
|
||||
uint64_t parent_subvol;
|
||||
bool changed = false;
|
||||
int n = 0, r;
|
||||
@@ -1811,6 +1809,11 @@ int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermed
|
||||
* qgroup that then includes all its own child subvolumes.
|
||||
*/
|
||||
|
||||
/* Turn this into a proper fd, if it is currently O_PATH */
|
||||
fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC, O_PATH, &real_fd);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (subvol_id == 0) {
|
||||
r = btrfs_is_subvol_fd(fd);
|
||||
if (r < 0)
|
||||
|
||||
@@ -483,6 +483,53 @@ TEST(fd_reopen) {
|
||||
fd1 = -1;
|
||||
}
|
||||
|
||||
TEST(fd_reopen_condition) {
|
||||
_cleanup_close_ int fd1 = -1, fd3 = -1;
|
||||
int fd2, fl;
|
||||
|
||||
/* Open without O_PATH */
|
||||
fd1 = open("/usr/", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
assert_se(fd1 >= 0);
|
||||
|
||||
fl = fcntl(fd1, F_GETFL);
|
||||
assert_se(FLAGS_SET(fl, O_DIRECTORY));
|
||||
assert_se(!FLAGS_SET(fl, O_PATH));
|
||||
|
||||
fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
|
||||
assert_se(fd2 == fd1);
|
||||
assert_se(fd3 < 0);
|
||||
|
||||
/* Switch on O_PATH */
|
||||
fd2 = fd_reopen_condition(fd1, O_DIRECTORY|O_PATH, O_DIRECTORY|O_PATH, &fd3);
|
||||
assert_se(fd2 != fd1);
|
||||
assert_se(fd3 == fd2);
|
||||
|
||||
fl = fcntl(fd2, F_GETFL);
|
||||
assert_se(FLAGS_SET(fl, O_DIRECTORY));
|
||||
assert_se(FLAGS_SET(fl, O_PATH));
|
||||
|
||||
close_and_replace(fd1, fd3);
|
||||
|
||||
fd2 = fd_reopen_condition(fd1, O_DIRECTORY|O_PATH, O_DIRECTORY|O_PATH, &fd3);
|
||||
assert_se(fd2 == fd1);
|
||||
assert_se(fd3 < 0);
|
||||
|
||||
/* Switch off O_PATH again */
|
||||
fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
|
||||
assert_se(fd2 != fd1);
|
||||
assert_se(fd3 == fd2);
|
||||
|
||||
fl = fcntl(fd2, F_GETFL);
|
||||
assert_se(FLAGS_SET(fl, O_DIRECTORY));
|
||||
assert_se(!FLAGS_SET(fl, O_PATH));
|
||||
|
||||
close_and_replace(fd1, fd3);
|
||||
|
||||
fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
|
||||
assert_se(fd2 == fd1);
|
||||
assert_se(fd3 < 0);
|
||||
}
|
||||
|
||||
TEST(take_fd) {
|
||||
_cleanup_close_ int fd1 = -1, fd2 = -1;
|
||||
int array[2] = { -1, -1 }, i = 0;
|
||||
|
||||
Reference in New Issue
Block a user