Merge pull request #10357 from poettering/import-fs
machinectl import-fs command and other fixes
This commit is contained in:
+24
-95
@@ -870,96 +870,6 @@ int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, u
|
||||
return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
|
||||
}
|
||||
|
||||
int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
|
||||
struct btrfs_ioctl_vol_args args = {};
|
||||
char p[SYS_BLOCK_PATH_MAX("/loop/backing_file")], q[DEV_NUM_PATH_MAX];
|
||||
_cleanup_free_ char *backing = NULL;
|
||||
_cleanup_close_ int loop_fd = -1, backing_fd = -1;
|
||||
struct stat st;
|
||||
dev_t dev = 0;
|
||||
int r;
|
||||
|
||||
/* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */
|
||||
if (!FILE_SIZE_VALID(new_size))
|
||||
return -EINVAL;
|
||||
|
||||
/* btrfs cannot handle file systems < 16M, hence use this as minimum */
|
||||
if (new_size < 16*1024*1024)
|
||||
new_size = 16*1024*1024;
|
||||
|
||||
r = btrfs_get_block_device_fd(fd, &dev);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -ENODEV;
|
||||
|
||||
xsprintf_sys_block_path(p, "/loop/backing_file", dev);
|
||||
r = read_one_line_file(p, &backing);
|
||||
if (r == -ENOENT)
|
||||
return -ENODEV;
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (isempty(backing) || !path_is_absolute(backing))
|
||||
return -ENODEV;
|
||||
|
||||
backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
if (backing_fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fstat(backing_fd, &st) < 0)
|
||||
return -errno;
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return -ENODEV;
|
||||
|
||||
if (new_size == (uint64_t) st.st_size)
|
||||
return 0;
|
||||
|
||||
if (grow_only && new_size < (uint64_t) st.st_size)
|
||||
return -EINVAL;
|
||||
|
||||
xsprintf_dev_num_path(q, "block", dev);
|
||||
loop_fd = open(q, O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
if (loop_fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
|
||||
return -EINVAL;
|
||||
|
||||
if (new_size < (uint64_t) st.st_size) {
|
||||
/* Decrease size: first decrease btrfs size, then shorten loopback */
|
||||
if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (ftruncate(backing_fd, new_size) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
if (new_size > (uint64_t) st.st_size) {
|
||||
/* Increase size: first enlarge loopback, then increase btrfs size */
|
||||
if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Make sure the free disk space is correctly updated for both file systems */
|
||||
(void) fsync(fd);
|
||||
(void) fsync(backing_fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return btrfs_resize_loopback_fd(fd, new_size, grow_only);
|
||||
}
|
||||
|
||||
int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
|
||||
assert(ret);
|
||||
|
||||
@@ -1503,7 +1413,12 @@ static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_s
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
|
||||
static int subvol_snapshot_children(
|
||||
int old_fd,
|
||||
int new_fd,
|
||||
const char *subvolume,
|
||||
uint64_t old_subvol_id,
|
||||
BtrfsSnapshotFlags flags) {
|
||||
|
||||
struct btrfs_ioctl_search_args args = {
|
||||
.key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
|
||||
@@ -1683,7 +1598,14 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
|
||||
int btrfs_subvol_snapshot_fd_full(
|
||||
int old_fd,
|
||||
const char *new_path,
|
||||
BtrfsSnapshotFlags flags,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_close_ int new_fd = -1;
|
||||
const char *subvolume;
|
||||
int r;
|
||||
@@ -1711,7 +1633,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
|
||||
r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
|
||||
r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK, progress_path, progress_bytes, userdata);
|
||||
if (r < 0)
|
||||
goto fallback_fail;
|
||||
|
||||
@@ -1748,7 +1670,14 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
|
||||
return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
|
||||
}
|
||||
|
||||
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
|
||||
int btrfs_subvol_snapshot_full(
|
||||
const char *old_path,
|
||||
const char *new_path,
|
||||
BtrfsSnapshotFlags flags,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_close_ int old_fd = -1;
|
||||
|
||||
assert(old_path);
|
||||
@@ -1758,7 +1687,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnaps
|
||||
if (old_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
|
||||
return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
|
||||
}
|
||||
|
||||
int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
|
||||
|
||||
+10
-5
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "copy.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct BtrfsSubvolInfo {
|
||||
@@ -61,14 +62,18 @@ int btrfs_quota_scan_start(int fd);
|
||||
int btrfs_quota_scan_wait(int fd);
|
||||
int btrfs_quota_scan_ongoing(int fd);
|
||||
|
||||
int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
|
||||
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
|
||||
|
||||
int btrfs_subvol_make(const char *path);
|
||||
int btrfs_subvol_make_fd(int fd, const char *subvolume);
|
||||
|
||||
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
|
||||
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
|
||||
int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
|
||||
return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int btrfs_subvol_snapshot_full(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
|
||||
return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags);
|
||||
int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
|
||||
|
||||
+94
-24
@@ -90,7 +90,9 @@ int copy_bytes_full(
|
||||
uint64_t max_bytes,
|
||||
CopyFlags copy_flags,
|
||||
void **ret_remains,
|
||||
size_t *ret_remains_size) {
|
||||
size_t *ret_remains_size,
|
||||
copy_progress_bytes_t progress,
|
||||
void *userdata) {
|
||||
|
||||
bool try_cfr = true, try_sendfile = true, try_splice = true;
|
||||
int r, nonblock_pipe = -1;
|
||||
@@ -161,8 +163,6 @@ int copy_bytes_full(
|
||||
return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
|
||||
}
|
||||
}
|
||||
|
||||
log_debug_errno(r, "Reflinking didn't work, falling back to non-reflink copying: %m");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,10 +308,17 @@ int copy_bytes_full(
|
||||
}
|
||||
|
||||
next:
|
||||
if (progress) {
|
||||
r = progress(n, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (max_bytes != (uint64_t) -1) {
|
||||
assert(max_bytes >= (uint64_t) n);
|
||||
max_bytes -= n;
|
||||
}
|
||||
|
||||
/* sendfile accepts at most SSIZE_MAX-offset bytes to copy,
|
||||
* so reduce our maximum by the amount we already copied,
|
||||
* but don't go below our copy buffer size, unless we are
|
||||
@@ -363,7 +370,9 @@ static int fd_copy_regular(
|
||||
const char *to,
|
||||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags) {
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_bytes_t progress,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_close_ int fdf = -1, fdt = -1;
|
||||
struct timespec ts[2];
|
||||
@@ -381,7 +390,7 @@ static int fd_copy_regular(
|
||||
if (fdt < 0)
|
||||
return -errno;
|
||||
|
||||
r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
|
||||
r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress, userdata);
|
||||
if (r < 0) {
|
||||
(void) unlinkat(dt, to, 0);
|
||||
return r;
|
||||
@@ -483,7 +492,11 @@ static int fd_copy_directory(
|
||||
unsigned depth_left,
|
||||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags) {
|
||||
CopyFlags copy_flags,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_close_ int fdf = -1, fdt = -1;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
@@ -524,6 +537,8 @@ static int fd_copy_directory(
|
||||
r = 0;
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
const char *child_display_path = NULL;
|
||||
_cleanup_free_ char *dp = NULL;
|
||||
struct stat buf;
|
||||
int q;
|
||||
|
||||
@@ -535,6 +550,17 @@ static int fd_copy_directory(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (progress_path) {
|
||||
if (display_path)
|
||||
child_display_path = dp = strjoin(display_path, "/", de->d_name);
|
||||
else
|
||||
child_display_path = de->d_name;
|
||||
|
||||
r = progress_path(child_display_path, &buf, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (S_ISDIR(buf.st_mode)) {
|
||||
/*
|
||||
* Don't descend into directories on other file systems, if this is requested. We do a simple
|
||||
@@ -566,9 +592,9 @@ static int fd_copy_directory(
|
||||
continue;
|
||||
}
|
||||
|
||||
q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags);
|
||||
q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, child_display_path, progress_path, progress_bytes, userdata);
|
||||
} else if (S_ISREG(buf.st_mode))
|
||||
q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
|
||||
q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, progress_bytes, userdata);
|
||||
else if (S_ISLNK(buf.st_mode))
|
||||
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
|
||||
else if (S_ISFIFO(buf.st_mode))
|
||||
@@ -606,7 +632,18 @@ static int fd_copy_directory(
|
||||
return r;
|
||||
}
|
||||
|
||||
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
int copy_tree_at_full(
|
||||
int fdf,
|
||||
const char *from,
|
||||
int fdt,
|
||||
const char *to,
|
||||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
struct stat st;
|
||||
|
||||
assert(from);
|
||||
@@ -616,9 +653,9 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t overr
|
||||
return -errno;
|
||||
|
||||
if (S_ISREG(st.st_mode))
|
||||
return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
|
||||
return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, progress_bytes, userdata);
|
||||
else if (S_ISDIR(st.st_mode))
|
||||
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags);
|
||||
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, progress_path, progress_bytes, userdata);
|
||||
else if (S_ISLNK(st.st_mode))
|
||||
return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
|
||||
else if (S_ISFIFO(st.st_mode))
|
||||
@@ -629,11 +666,14 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t overr
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
|
||||
}
|
||||
int copy_directory_fd_full(
|
||||
int dirfd,
|
||||
const char *to,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
|
||||
struct stat st;
|
||||
|
||||
assert(dirfd >= 0);
|
||||
@@ -645,10 +685,17 @@ int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags);
|
||||
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, progress_path, progress_bytes, userdata);
|
||||
}
|
||||
|
||||
int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
|
||||
int copy_directory_full(
|
||||
const char *from,
|
||||
const char *to,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
struct stat st;
|
||||
|
||||
assert(from);
|
||||
@@ -660,10 +707,16 @@ int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags);
|
||||
return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, progress_path, progress_bytes, userdata);
|
||||
}
|
||||
|
||||
int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
|
||||
int copy_file_fd_full(
|
||||
const char *from,
|
||||
int fdt,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_close_ int fdf = -1;
|
||||
int r;
|
||||
|
||||
@@ -674,7 +727,7 @@ int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
|
||||
if (fdf < 0)
|
||||
return -errno;
|
||||
|
||||
r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
|
||||
r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress_bytes, userdata);
|
||||
|
||||
(void) copy_times(fdf, fdt);
|
||||
(void) copy_xattr(fdf, fdt);
|
||||
@@ -682,7 +735,16 @@ int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
|
||||
int copy_file_full(
|
||||
const char *from,
|
||||
const char *to,
|
||||
int flags,
|
||||
mode_t mode,
|
||||
unsigned chattr_flags,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
int fdt = -1, r;
|
||||
|
||||
assert(from);
|
||||
@@ -697,7 +759,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
|
||||
if (chattr_flags != 0)
|
||||
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
|
||||
|
||||
r = copy_file_fd(from, fdt, copy_flags);
|
||||
r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
|
||||
if (r < 0) {
|
||||
close(fdt);
|
||||
(void) unlink(to);
|
||||
@@ -712,7 +774,15 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
|
||||
int copy_file_atomic_full(
|
||||
const char *from,
|
||||
const char *to,
|
||||
mode_t mode,
|
||||
unsigned chattr_flags,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(unlink_and_freep) char *t = NULL;
|
||||
_cleanup_close_ int fdt = -1;
|
||||
int r;
|
||||
@@ -745,7 +815,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned cha
|
||||
if (chattr_flags != 0)
|
||||
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
|
||||
|
||||
r = copy_file_fd(from, fdt, copy_flags);
|
||||
r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+42
-10
@@ -1,9 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef enum CopyFlags {
|
||||
@@ -13,16 +15,46 @@ typedef enum CopyFlags {
|
||||
COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
|
||||
} CopyFlags;
|
||||
|
||||
int copy_file_fd(const char *from, int to, CopyFlags copy_flags);
|
||||
int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
|
||||
int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
|
||||
int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
|
||||
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
|
||||
int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags);
|
||||
int copy_directory(const char *from, const char *to, CopyFlags copy_flags);
|
||||
int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size);
|
||||
static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
|
||||
return copy_bytes_full(fdf, fdt, max_bytes, copy_flags, NULL, NULL);
|
||||
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
|
||||
typedef int (*copy_progress_path_t)(const char *path, const struct stat *st, void *userdata);
|
||||
|
||||
int copy_file_fd_full(const char *from, int to, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_file_fd(const char *from, int to, CopyFlags copy_flags) {
|
||||
return copy_file_fd_full(from, to, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_file_full(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
|
||||
return copy_file_full(from, to, open_flags, mode, chattr_flags, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_file_atomic_full(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
|
||||
return copy_file_atomic_full(from, to, mode, chattr_flags, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
|
||||
return copy_directory_fd_full(dirfd, to, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_directory_full(const char *from, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
|
||||
return copy_directory_full(from, to, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
|
||||
return copy_bytes_full(fdf, fdt, max_bytes, copy_flags, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_times(int fdf, int fdt);
|
||||
int copy_xattr(int fdf, int fdt);
|
||||
|
||||
+1
-1
@@ -648,7 +648,7 @@ int fd_duplicate_data_fd(int fd) {
|
||||
|
||||
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size);
|
||||
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
|
||||
if (r < 0 && r != -EAGAIN)
|
||||
return r; /* If we get EAGAIN it could be because of the source or because of
|
||||
* the destination fd, we can't know, as sendfile() and friends won't
|
||||
|
||||
@@ -750,6 +750,9 @@ const char *last_path_component(const char *path) {
|
||||
|
||||
unsigned l, k;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
l = k = strlen(path);
|
||||
if (l == 0) /* special case — an empty string */
|
||||
return path;
|
||||
@@ -766,6 +769,37 @@ const char *last_path_component(const char *path) {
|
||||
return path + k;
|
||||
}
|
||||
|
||||
int path_extract_filename(const char *p, char **ret) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
const char *c, *e = NULL, *q;
|
||||
|
||||
/* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
|
||||
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
|
||||
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
c = last_path_component(p);
|
||||
|
||||
for (q = c; *q != 0; q++)
|
||||
if (*q != '/')
|
||||
e = q + 1;
|
||||
|
||||
if (!e) /* no valid character? */
|
||||
return -EINVAL;
|
||||
|
||||
a = strndup(c, e - c);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!filename_is_valid(a))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = TAKE_PTR(a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool filename_is_valid(const char *p) {
|
||||
const char *e;
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
|
||||
|
||||
char* dirname_malloc(const char *path);
|
||||
const char *last_path_component(const char *path);
|
||||
int path_extract_filename(const char *p, char **ret);
|
||||
|
||||
bool filename_is_valid(const char *p) _pure_;
|
||||
bool path_is_valid(const char *p) _pure_;
|
||||
|
||||
@@ -22,3 +22,11 @@ static inline void rm_rf_physical_and_free(char *p) {
|
||||
free(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free);
|
||||
|
||||
/* Similar as above, but also has magic btrfs subvolume powers */
|
||||
static inline void rm_rf_subvolume_and_free(char *p) {
|
||||
PROTECT_ERRNO;
|
||||
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||
free(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free);
|
||||
|
||||
@@ -296,3 +296,26 @@ int fd_verify_regular(int fd) {
|
||||
|
||||
return stat_verify_regular(&st);
|
||||
}
|
||||
|
||||
int stat_verify_directory(const struct stat *st) {
|
||||
assert(st);
|
||||
|
||||
if (S_ISLNK(st->st_mode))
|
||||
return -ELOOP;
|
||||
|
||||
if (!S_ISDIR(st->st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_verify_directory(int fd) {
|
||||
struct stat st;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
return stat_verify_directory(&st);
|
||||
}
|
||||
|
||||
@@ -59,3 +59,6 @@ int path_is_temporary_fs(const char *path);
|
||||
|
||||
int stat_verify_regular(const struct stat *st);
|
||||
int fd_verify_regular(int fd);
|
||||
|
||||
int stat_verify_directory(const struct stat *st);
|
||||
int fd_verify_directory(int fd);
|
||||
|
||||
Reference in New Issue
Block a user