mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
repart: support "nodatacow" in btrfs subvolumes
In btrfs-progs 6.15 it is planned to add a new parameter in mkfs.btrfs --inode-flags, that can set attributes for subvolumes, directories, and files. The current supported attributes are "nodatacow", to disable CoW, and "nodatasum", to disable the checksum. This commit extend the "Subvolunes=" option to understand the "nodatacow" flag for subvolums only. If RepartOffline is enabled it will build the image without loopback devices, using the correct --inode-flags parameters. If RepartOffline is disabled it will use loopback devices and set the btrfs attributes accordingly. Signed-off-by: Alberto Planas <aplanas@suse.com>
This commit is contained in:
committed by
Yu Watanabe
parent
eeedea94da
commit
ab1f4e506f
@@ -612,6 +612,10 @@
|
||||
<entry><literal>ro</literal></entry>
|
||||
<entry>Make this subvolume read-only.</entry>
|
||||
</row>
|
||||
<row id='C'>
|
||||
<entry><literal>nodatacow</literal></entry>
|
||||
<entry>Disable data CoW for this subvolume.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "basic-forward.h"
|
||||
|
||||
typedef enum BtrfsSubvolFlags {
|
||||
BTRFS_SUBVOL_RO = 1 << 0,
|
||||
BTRFS_SUBVOL_NODATACOW = 1 << 1,
|
||||
_BTRFS_SUBVOL_FLAGS_MASK = BTRFS_SUBVOL_NODATACOW|BTRFS_SUBVOL_RO,
|
||||
_BTRFS_SUBVOL_FLAGS_INVALID = -EINVAL,
|
||||
_BTRFS_SUBVOL_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
|
||||
} BtrfsSubvolFlags;
|
||||
|
||||
int btrfs_validate_subvolume_name(const char *name);
|
||||
|
||||
int btrfs_subvol_make(int dir_fd, const char *path);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "log.h"
|
||||
#include "mkdir.h"
|
||||
#include "path-util.h"
|
||||
@@ -202,7 +203,7 @@ int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, g
|
||||
return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
|
||||
}
|
||||
|
||||
int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes) {
|
||||
int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, Hashmap *subvolumes) {
|
||||
_cleanup_free_ char *pp = NULL, *bn = NULL;
|
||||
_cleanup_close_ int dfd = -EBADF;
|
||||
int r;
|
||||
@@ -237,10 +238,17 @@ int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mod
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
XOpenFlags flags = 0;
|
||||
if (hashmap_contains(subvolumes, p)) {
|
||||
flags = XO_SUBVOLUME;
|
||||
if ((PTR_TO_INT(hashmap_get(subvolumes, p)) & BTRFS_SUBVOL_NODATACOW))
|
||||
flags |= XO_NOCOW;
|
||||
}
|
||||
|
||||
_cleanup_close_ int nfd = xopenat_full(
|
||||
dfd, bn,
|
||||
O_DIRECTORY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC,
|
||||
path_strv_contains(subvolumes, p) ? XO_SUBVOLUME : 0,
|
||||
flags,
|
||||
m);
|
||||
if (nfd == -EEXIST)
|
||||
return 0;
|
||||
|
||||
@@ -22,7 +22,7 @@ static inline int mkdir_parents(const char *path, mode_t mode) {
|
||||
int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
|
||||
int mkdir_p(const char *path, mode_t mode);
|
||||
int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
|
||||
int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes);
|
||||
int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, Hashmap *subvolumes);
|
||||
static inline int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) {
|
||||
return mkdir_p_root_full(root, p, uid, gid, m, USEC_INFINITY, NULL);
|
||||
}
|
||||
|
||||
@@ -332,33 +332,29 @@ static void copy_files_free_many(CopyFiles *f, size_t n) {
|
||||
free(f);
|
||||
}
|
||||
|
||||
typedef enum SubvolumeFlags {
|
||||
SUBVOLUME_RO = 1 << 0,
|
||||
_SUBVOLUME_FLAGS_MASK = SUBVOLUME_RO,
|
||||
_SUBVOLUME_FLAGS_INVALID = -EINVAL,
|
||||
_SUBVOLUME_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
|
||||
} SubvolumeFlags;
|
||||
|
||||
static SubvolumeFlags subvolume_flags_from_string_one(const char *s) {
|
||||
static BtrfsSubvolFlags subvolume_flags_from_string_one(const char *s) {
|
||||
/* This is a bitmask (i.e. not dense), hence we don't use the "string-table.h" stuff here. */
|
||||
|
||||
assert(s);
|
||||
|
||||
if (streq(s, "ro"))
|
||||
return SUBVOLUME_RO;
|
||||
return BTRFS_SUBVOL_RO;
|
||||
|
||||
return _SUBVOLUME_FLAGS_INVALID;
|
||||
if (streq(s, "nodatacow"))
|
||||
return BTRFS_SUBVOL_NODATACOW;
|
||||
|
||||
return _BTRFS_SUBVOL_FLAGS_INVALID;
|
||||
}
|
||||
|
||||
static SubvolumeFlags subvolume_flags_from_string(const char *s) {
|
||||
SubvolumeFlags flags = 0;
|
||||
static BtrfsSubvolFlags subvolume_flags_from_string(const char *s) {
|
||||
BtrfsSubvolFlags flags = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *f = NULL;
|
||||
SubvolumeFlags ff;
|
||||
BtrfsSubvolFlags ff;
|
||||
|
||||
r = extract_first_word(&s, &f, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
@@ -378,7 +374,7 @@ static SubvolumeFlags subvolume_flags_from_string(const char *s) {
|
||||
|
||||
typedef struct Subvolume {
|
||||
char *path;
|
||||
SubvolumeFlags flags;
|
||||
BtrfsSubvolFlags flags;
|
||||
} Subvolume;
|
||||
|
||||
static Subvolume* subvolume_free(Subvolume *s) {
|
||||
@@ -2327,7 +2323,7 @@ static int config_parse_subvolumes(
|
||||
}
|
||||
|
||||
if (f) {
|
||||
SubvolumeFlags flags = subvolume_flags_from_string(f);
|
||||
BtrfsSubvolFlags flags = subvolume_flags_from_string(f);
|
||||
if (flags == -EBADRQC) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown subvolume flag in subvolume, ignoring: %s", f);
|
||||
continue;
|
||||
@@ -5973,7 +5969,7 @@ static int make_copy_files_denylist(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_subvolume_path(const char *path, Set **subvolumes) {
|
||||
static int add_subvolume_path(const char *path, BtrfsSubvolFlags flags, Hashmap **subvolumes) {
|
||||
_cleanup_free_ struct stat *st = NULL;
|
||||
int r;
|
||||
|
||||
@@ -5990,63 +5986,71 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_copy_source), path);
|
||||
|
||||
r = set_ensure_consume(subvolumes, &inode_hash_ops, TAKE_PTR(st));
|
||||
r = hashmap_ensure_put(subvolumes, &inode_hash_ops, st, INT_TO_PTR(flags));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
static int make_subvolumes_hashmap(const Partition *p, Hashmap **ret) {
|
||||
_cleanup_hashmap_free_ Hashmap *hashmap = NULL;
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes)
|
||||
if (strv_extend(&subvolumes, subvolume->path) < 0)
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
path = strdup(subvolume->path);
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
if (p->suppressing) {
|
||||
char **suppressing;
|
||||
r = hashmap_ensure_put(&hashmap, &path_hash_ops_free, path, INT_TO_PTR(subvolume->flags));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = make_subvolumes_strv(p->suppressing, &suppressing);
|
||||
TAKE_PTR(path);
|
||||
}
|
||||
|
||||
if (p->suppressing) {
|
||||
Hashmap *suppressing;
|
||||
|
||||
r = make_subvolumes_hashmap(p->suppressing, &suppressing);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_consume(&subvolumes, suppressing, /* filter_duplicates= */ true);
|
||||
r = hashmap_merge(hashmap, suppressing);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(subvolumes);
|
||||
*ret = TAKE_PTR(hashmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_subvolumes_set(
|
||||
static int make_subvolumes_by_source_inode_hashmap(
|
||||
const Partition *p,
|
||||
const char *source,
|
||||
const char *target,
|
||||
Set **ret) {
|
||||
Hashmap **ret) {
|
||||
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
_cleanup_set_free_ Set *subvolumes = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *hashmap = NULL;
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(target);
|
||||
assert(ret);
|
||||
|
||||
r = make_subvolumes_strv(p, &paths);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(subvolume, paths) {
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
const char *s = path_startswith(*subvolume, target);
|
||||
const char *s = path_startswith(subvolume->path, target);
|
||||
if (!s)
|
||||
continue;
|
||||
|
||||
@@ -6054,12 +6058,24 @@ static int make_subvolumes_set(
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
r = add_subvolume_path(path, &subvolumes);
|
||||
r = add_subvolume_path(path, subvolume->flags, &hashmap);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(subvolumes);
|
||||
if (p->suppressing) {
|
||||
Hashmap *suppressing;
|
||||
|
||||
r = make_subvolumes_by_source_inode_hashmap(p->suppressing, source, target, &suppressing);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_merge(hashmap, suppressing);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(hashmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6127,13 +6143,13 @@ static int file_is_denylisted(const char *source, Hashmap *denylist) {
|
||||
}
|
||||
|
||||
static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *subvolumes = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(root);
|
||||
|
||||
r = make_subvolumes_strv(p, &subvolumes);
|
||||
r = make_subvolumes_hashmap(p, &subvolumes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -6177,7 +6193,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
|
||||
FOREACH_ARRAY(line, copy_files, n_copy_files) {
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *subvolumes_by_source_inode = NULL;
|
||||
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
|
||||
usec_t ts = epoch_or_infinity();
|
||||
|
||||
@@ -6187,7 +6203,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
r = make_subvolumes_set(p, line->source, line->target, &subvolumes_by_source_inode);
|
||||
r = make_subvolumes_by_source_inode_hashmap(p, line->source, line->target, &subvolumes_by_source_inode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -6302,14 +6318,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
}
|
||||
|
||||
static int do_make_directories(Partition *p, const char *root) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *subvolumes = NULL;
|
||||
_cleanup_free_ char **override_dirs = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(root);
|
||||
|
||||
r = make_subvolumes_strv(p, &subvolumes);
|
||||
r = make_subvolumes_hashmap(p, &subvolumes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -6355,7 +6371,7 @@ static int make_subvolumes_read_only(Partition *p, const char *root) {
|
||||
int r;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
if (!FLAGS_SET(subvolume->flags, SUBVOLUME_RO))
|
||||
if (!FLAGS_SET(subvolume->flags, BTRFS_SUBVOL_RO))
|
||||
continue;
|
||||
|
||||
path = path_join(root, subvolume->path);
|
||||
@@ -6623,7 +6639,7 @@ static int append_btrfs_subvols(char ***l, OrderedHashmap *subvolumes, const cha
|
||||
if (streq_ptr(subvolume->path, default_subvolume) && !strextend(&s, "default"))
|
||||
return log_oom();
|
||||
|
||||
if (FLAGS_SET(subvolume->flags, SUBVOLUME_RO) && !strextend_with_separator(&s, "-", "ro"))
|
||||
if (FLAGS_SET(subvolume->flags, BTRFS_SUBVOL_RO) && !strextend_with_separator(&s, "-", "ro"))
|
||||
return log_oom();
|
||||
|
||||
if (!strextend_with_separator(&s, ":", subvolume->path))
|
||||
@@ -6637,6 +6653,28 @@ static int append_btrfs_subvols(char ***l, OrderedHashmap *subvolumes, const cha
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_btrfs_inode_flags(char ***l, OrderedHashmap *subvolumes) {
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
assert(l);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, subvolumes) {
|
||||
if (!FLAGS_SET(subvolume->flags, BTRFS_SUBVOL_NODATACOW))
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *s = strjoin("nodatacow:", subvolume->path);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend_many(l, "--inode-flags", s);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
|
||||
_cleanup_strv_free_ char **sv = NULL;
|
||||
int r;
|
||||
@@ -6655,10 +6693,18 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = append_btrfs_inode_flags(&sv, p->subvolumes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p->suppressing) {
|
||||
r = append_btrfs_subvols(&sv, p->suppressing->subvolumes, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = append_btrfs_inode_flags(&sv, p->suppressing->subvolumes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "btrfs.h"
|
||||
#include "chattr-util.h"
|
||||
#include "copy.h"
|
||||
#include "dirent-util.h"
|
||||
@@ -891,7 +892,7 @@ static int fd_copy_tree_generic(
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
Hashmap *denylist,
|
||||
Set *subvolumes,
|
||||
Hashmap *subvolumes,
|
||||
HardlinkContext *hardlink_context,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
@@ -1111,7 +1112,7 @@ static int fd_copy_directory(
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
Hashmap *denylist,
|
||||
Set *subvolumes,
|
||||
Hashmap *subvolumes,
|
||||
HardlinkContext *hardlink_context,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
@@ -1160,9 +1161,16 @@ static int fd_copy_directory(
|
||||
|
||||
exists = r >= 0;
|
||||
|
||||
XOpenFlags flags = copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0;
|
||||
if (hashmap_contains(subvolumes, st)) {
|
||||
flags |= XO_SUBVOLUME;
|
||||
if ((PTR_TO_INT(hashmap_get(subvolumes, st)) & BTRFS_SUBVOL_NODATACOW))
|
||||
flags |= XO_NOCOW;
|
||||
}
|
||||
|
||||
fdt = xopenat_lock_full(dt, to,
|
||||
O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|(exists ? 0 : O_CREAT|O_EXCL),
|
||||
(copy_flags & COPY_MAC_CREATE ? XO_LABEL : 0)|(set_contains(subvolumes, st) ? XO_SUBVOLUME : 0),
|
||||
flags,
|
||||
st->st_mode & 07777,
|
||||
copy_flags & COPY_LOCK_BSD ? LOCK_BSD : LOCK_NONE,
|
||||
LOCK_EX);
|
||||
@@ -1331,7 +1339,7 @@ static int fd_copy_tree_generic(
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
Hashmap *denylist,
|
||||
Set *subvolumes,
|
||||
Hashmap *subvolumes,
|
||||
HardlinkContext *hardlink_context,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
@@ -1380,7 +1388,7 @@ int copy_tree_at_full(
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
Hashmap *denylist,
|
||||
Set *subvolumes,
|
||||
Hashmap *subvolumes,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
||||
@@ -80,11 +80,11 @@ static inline int copy_file_atomic(const char *from, const char *to, mode_t mode
|
||||
return copy_file_atomic_full(from, to, mode, 0, 0, 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, Hashmap *denylist, Set *subvolumes, 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, Hashmap *denylist, Set *subvolumes) {
|
||||
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, Hashmap *denylist, Hashmap *subvolumes, 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, Hashmap *denylist, Hashmap *subvolumes) {
|
||||
return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, denylist, subvolumes, 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, Hashmap *denylist, Set *subvolumes) {
|
||||
static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist, Hashmap *subvolumes) {
|
||||
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, subvolumes, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user