mirror of
https://github.com/systemd/systemd
synced 2024-10-04 15:21:01 +00:00
Merge pull request #29179 from YHNdnzj/resume-offset-btrfs
btrfs-util: introduce btrfs_get_file_physical_offset_fd
This commit is contained in:
commit
357d352cb6
|
@ -181,8 +181,8 @@ static int add_credentials_to_table(Table *t, bool encrypted) {
|
|||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine backing file system of '%s': %m", de->d_name);
|
||||
|
||||
secure = r ? "secure" : "weak"; /* ramfs is not swappable, hence "secure", everything else is "weak" */
|
||||
secure_color = r ? ansi_highlight_green() : ansi_highlight_yellow4();
|
||||
secure = r > 0 ? "secure" : "weak"; /* ramfs is not swappable, hence "secure", everything else is "weak" */
|
||||
secure_color = r > 0 ? ansi_highlight_green() : ansi_highlight_yellow4();
|
||||
}
|
||||
|
||||
j = path_join(prefix, de->d_name);
|
||||
|
|
|
@ -3792,7 +3792,7 @@ static int journal_file_warn_btrfs(JournalFile *f) {
|
|||
r = fd_is_fs_type(f->fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine if journal is on btrfs: %m");
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = read_attr_fd(f->fd, &attrs);
|
||||
|
|
|
@ -120,7 +120,7 @@ int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
|
||||
|
@ -182,7 +182,7 @@ int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
|
||||
|
@ -302,7 +302,7 @@ int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
@ -393,7 +393,7 @@ int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
@ -610,7 +610,7 @@ int btrfs_quota_enable_fd(int fd, bool b) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
|
||||
return RET_NERRNO(ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args));
|
||||
|
@ -644,7 +644,7 @@ int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
@ -1071,7 +1071,7 @@ int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupi
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
|
||||
while (btrfs_ioctl_search_args_compare(&args) <= 0) {
|
||||
|
@ -1575,7 +1575,7 @@ int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
@ -1822,7 +1822,7 @@ int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
if (r == 0)
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
|
@ -1871,3 +1871,298 @@ int btrfs_forget_device(const char *path) {
|
|||
|
||||
return RET_NERRNO(ioctl(control_fd, BTRFS_IOC_FORGET_DEV, &args));
|
||||
}
|
||||
|
||||
typedef struct BtrfsStripe {
|
||||
uint64_t devid;
|
||||
uint64_t offset;
|
||||
} BtrfsStripe;
|
||||
|
||||
typedef struct BtrfsChunk {
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
uint64_t type;
|
||||
|
||||
BtrfsStripe *stripes;
|
||||
uint16_t n_stripes;
|
||||
uint64_t stripe_len;
|
||||
} BtrfsChunk;
|
||||
|
||||
typedef struct BtrfsChunkTree {
|
||||
BtrfsChunk **chunks;
|
||||
size_t n_chunks;
|
||||
} BtrfsChunkTree;
|
||||
|
||||
static BtrfsChunk* btrfs_chunk_free(BtrfsChunk *chunk) {
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
free(chunk->stripes);
|
||||
|
||||
return mfree(chunk);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(BtrfsChunk*, btrfs_chunk_free);
|
||||
|
||||
static void btrfs_chunk_tree_done(BtrfsChunkTree *tree) {
|
||||
assert(tree);
|
||||
|
||||
FOREACH_ARRAY(i, tree->chunks, tree->n_chunks)
|
||||
btrfs_chunk_free(*i);
|
||||
}
|
||||
|
||||
static int btrfs_read_chunk_tree_fd(int fd, BtrfsChunkTree *ret) {
|
||||
|
||||
struct btrfs_ioctl_search_args search_args = {
|
||||
.key.tree_id = BTRFS_CHUNK_TREE_OBJECTID,
|
||||
|
||||
.key.min_type = BTRFS_CHUNK_ITEM_KEY,
|
||||
.key.max_type = BTRFS_CHUNK_ITEM_KEY,
|
||||
|
||||
.key.min_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID,
|
||||
.key.max_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID,
|
||||
|
||||
.key.min_offset = 0,
|
||||
.key.max_offset = UINT64_MAX,
|
||||
|
||||
.key.min_transid = 0,
|
||||
.key.max_transid = UINT64_MAX,
|
||||
};
|
||||
|
||||
_cleanup_(btrfs_chunk_tree_done) BtrfsChunkTree tree = {};
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
while (btrfs_ioctl_search_args_compare(&search_args) <= 0) {
|
||||
const struct btrfs_ioctl_search_header *sh;
|
||||
unsigned i;
|
||||
|
||||
search_args.key.nr_items = 256;
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_args) < 0)
|
||||
return -errno;
|
||||
|
||||
if (search_args.key.nr_items == 0)
|
||||
break;
|
||||
|
||||
FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, search_args) {
|
||||
_cleanup_(btrfs_chunk_freep) BtrfsChunk *chunk = NULL;
|
||||
const struct btrfs_chunk *item;
|
||||
|
||||
btrfs_ioctl_search_args_set(&search_args, sh);
|
||||
|
||||
if (sh->objectid != BTRFS_FIRST_CHUNK_TREE_OBJECTID)
|
||||
continue;
|
||||
if (sh->type != BTRFS_CHUNK_ITEM_KEY)
|
||||
continue;
|
||||
|
||||
chunk = new(BtrfsChunk, 1);
|
||||
if (!chunk)
|
||||
return -ENOMEM;
|
||||
|
||||
item = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
|
||||
|
||||
*chunk = (BtrfsChunk) {
|
||||
.offset = sh->offset,
|
||||
.length = le64toh(item->length),
|
||||
.type = le64toh(item->type),
|
||||
.n_stripes = le16toh(item->num_stripes),
|
||||
.stripe_len = le64toh(item->stripe_len),
|
||||
};
|
||||
|
||||
chunk->stripes = new(BtrfsStripe, chunk->n_stripes);
|
||||
if (!chunk->stripes)
|
||||
return -ENOMEM;
|
||||
|
||||
for (size_t j = 0; j < chunk->n_stripes; j++) {
|
||||
const struct btrfs_stripe *stripe = &item->stripe + j;
|
||||
|
||||
chunk->stripes[j] = (BtrfsStripe) {
|
||||
.devid = le64toh(stripe->devid),
|
||||
.offset = le64toh(stripe->offset),
|
||||
};
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(tree.chunks, tree.n_chunks + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
tree.chunks[tree.n_chunks++] = TAKE_PTR(chunk);
|
||||
}
|
||||
|
||||
if (!btrfs_ioctl_search_args_inc(&search_args))
|
||||
break;
|
||||
}
|
||||
|
||||
*ret = TAKE_STRUCT(tree);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BtrfsChunk* btrfs_find_chunk_from_logical_address(const BtrfsChunkTree *tree, uint64_t logical) {
|
||||
size_t min_index, max_index;
|
||||
|
||||
assert(tree);
|
||||
assert(tree->chunks || tree->n_chunks == 0);
|
||||
|
||||
if (tree->n_chunks == 0)
|
||||
return NULL;
|
||||
|
||||
/* bisection */
|
||||
min_index = 0;
|
||||
max_index = tree->n_chunks - 1;
|
||||
|
||||
while (min_index <= max_index) {
|
||||
size_t mid = (min_index + max_index) / 2;
|
||||
|
||||
if (logical < tree->chunks[mid]->offset) {
|
||||
if (mid < 1)
|
||||
return NULL;
|
||||
|
||||
max_index = mid - 1;
|
||||
} else if (logical >= tree->chunks[mid]->offset + tree->chunks[mid]->length)
|
||||
min_index = mid + 1;
|
||||
else
|
||||
return tree->chunks[mid];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int btrfs_is_nocow_fd(int fd) {
|
||||
struct statfs sfs;
|
||||
unsigned flags;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstatfs(fd, &sfs) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!is_fs_type(&sfs, BTRFS_SUPER_MAGIC))
|
||||
return -ENOTTY;
|
||||
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)
|
||||
return -errno;
|
||||
|
||||
return FLAGS_SET(flags, FS_NOCOW_FL) && !FLAGS_SET(flags, FS_COMPR_FL);
|
||||
}
|
||||
|
||||
int btrfs_get_file_physical_offset_fd(int fd, uint64_t *ret) {
|
||||
|
||||
struct btrfs_ioctl_search_args search_args = {
|
||||
.key.min_type = BTRFS_EXTENT_DATA_KEY,
|
||||
.key.max_type = BTRFS_EXTENT_DATA_KEY,
|
||||
|
||||
.key.min_offset = 0,
|
||||
.key.max_offset = UINT64_MAX,
|
||||
|
||||
.key.min_transid = 0,
|
||||
.key.max_transid = UINT64_MAX,
|
||||
};
|
||||
|
||||
_cleanup_(btrfs_chunk_tree_done) BtrfsChunkTree tree = {};
|
||||
uint64_t subvol_id;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
r = stat_verify_regular(&st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = btrfs_is_nocow_fd(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot get physical address for btrfs extent: CoW enabled");
|
||||
|
||||
r = btrfs_subvol_get_id_fd(fd, &subvol_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = btrfs_read_chunk_tree_fd(fd, &tree);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
search_args.key.tree_id = subvol_id;
|
||||
search_args.key.min_objectid = search_args.key.max_objectid = st.st_ino;
|
||||
|
||||
while (btrfs_ioctl_search_args_compare(&search_args) <= 0) {
|
||||
const struct btrfs_ioctl_search_header *sh;
|
||||
unsigned i;
|
||||
|
||||
search_args.key.nr_items = 256;
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_args) < 0)
|
||||
return -errno;
|
||||
|
||||
if (search_args.key.nr_items == 0)
|
||||
break;
|
||||
|
||||
FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, search_args) {
|
||||
const struct btrfs_file_extent_item *item;
|
||||
uint64_t logical_offset;
|
||||
BtrfsChunk *chunk;
|
||||
|
||||
btrfs_ioctl_search_args_set(&search_args, sh);
|
||||
|
||||
if (sh->type != BTRFS_EXTENT_DATA_KEY)
|
||||
continue;
|
||||
|
||||
if (sh->objectid != st.st_ino)
|
||||
continue;
|
||||
|
||||
item = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
|
||||
|
||||
if (!IN_SET(item->type, BTRFS_FILE_EXTENT_REG, BTRFS_FILE_EXTENT_PREALLOC))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot get physical address for btrfs extent: invalid type %" PRIu8,
|
||||
item->type);
|
||||
|
||||
if (item->compression != 0 || item->encryption != 0 || item->other_encoding != 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot get physical address for btrfs extent: has incompatible property");
|
||||
|
||||
logical_offset = le64toh(item->disk_bytenr);
|
||||
if (logical_offset == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot get physical address for btrfs extent: failed to get logical offset");
|
||||
|
||||
chunk = btrfs_find_chunk_from_logical_address(&tree, logical_offset);
|
||||
if (!chunk)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot get physical address for btrfs extent: no matching chunk found");
|
||||
|
||||
if ((chunk->type & BTRFS_BLOCK_GROUP_PROFILE_MASK) != 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot get physical address for btrfs extent: unsupported profile");
|
||||
|
||||
uint64_t relative_chunk, relative_stripe, stripe_nr;
|
||||
uint16_t stripe_index;
|
||||
|
||||
assert(logical_offset >= chunk->offset);
|
||||
assert(chunk->n_stripes > 0);
|
||||
assert(chunk->stripe_len > 0);
|
||||
|
||||
relative_chunk = logical_offset - chunk->offset;
|
||||
stripe_nr = relative_chunk / chunk->stripe_len;
|
||||
relative_stripe = relative_chunk - stripe_nr * chunk->stripe_len;
|
||||
stripe_index = stripe_nr % chunk->n_stripes;
|
||||
|
||||
*ret = chunk->stripes[stripe_index].offset +
|
||||
stripe_nr / chunk->n_stripes * chunk->stripe_len +
|
||||
relative_stripe;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!btrfs_ioctl_search_args_inc(&search_args))
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
|
|
@ -145,3 +145,5 @@ static inline bool btrfs_might_be_subvol(const struct stat *st) {
|
|||
}
|
||||
|
||||
int btrfs_forget_device(const char *path);
|
||||
|
||||
int btrfs_get_file_physical_offset_fd(int fd, uint64_t *ret);
|
||||
|
|
|
@ -298,7 +298,7 @@ static int image_make(
|
|||
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r) {
|
||||
if (r > 0) {
|
||||
BtrfsSubvolInfo info;
|
||||
|
||||
/* It's a btrfs subvolume */
|
||||
|
|
|
@ -221,6 +221,10 @@ executables += [
|
|||
'sources' : files('test-btrfs.c'),
|
||||
'type' : 'manual',
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-btrfs-physical-offset.c'),
|
||||
'type' : 'manual',
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-cap-list.c') +
|
||||
generated_gperf_headers,
|
||||
|
|
33
src/test/test-btrfs-physical-offset.c
Normal file
33
src/test/test-btrfs-physical-offset.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "btrfs-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "log.h"
|
||||
#include "memory-util.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
uint64_t offset;
|
||||
int r;
|
||||
|
||||
assert(argc == 2);
|
||||
assert(!isempty(argv[1]));
|
||||
|
||||
fd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
log_error_errno(fd, "Failed to open '%s': %m", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = btrfs_get_file_physical_offset_fd(fd, &offset);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to get physical offset of '%s': %m", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("%" PRIu64 "\n", offset / page_size());
|
||||
return EXIT_SUCCESS;
|
||||
}
|
1
test/TEST-83-BTRFS/Makefile
Symbolic link
1
test/TEST-83-BTRFS/Makefile
Symbolic link
|
@ -0,0 +1 @@
|
|||
../TEST-01-BASIC/Makefile
|
0
test/TEST-83-BTRFS/deny-list-ubuntu-ci
Normal file
0
test/TEST-83-BTRFS/deny-list-ubuntu-ci
Normal file
25
test/TEST-83-BTRFS/test.sh
Executable file
25
test/TEST-83-BTRFS/test.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -e
|
||||
|
||||
TEST_DESCRIPTION="test btrfs-util"
|
||||
|
||||
TEST_NO_NSPAWN=1
|
||||
FSTYPE=btrfs
|
||||
IMAGE_NAME="btrfs"
|
||||
TEST_FORCE_NEWIMAGE=1
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
if ! command -v btrfs >/dev/null || ! command -v mkfs.btrfs >/dev/null; then
|
||||
echo "TEST: $TEST_DESCRIPTION [SKIPPED]: btrfs not supported by host" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
test_append_files() {
|
||||
install_btrfs
|
||||
image_install sync
|
||||
}
|
||||
|
||||
do_test "$@"
|
8
test/units/testsuite-83.service
Normal file
8
test/units/testsuite-83.service
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Unit]
|
||||
Description=TEST-83-BTRFS
|
||||
|
||||
[Service]
|
||||
ExecStartPre=rm -f /failed /testok
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Type=oneshot
|
25
test/units/testsuite-83.sh
Executable file
25
test/units/testsuite-83.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
TEST_BTRFS_OFFSET=/usr/lib/systemd/tests/unit-tests/manual/test-btrfs-physical-offset
|
||||
|
||||
SWAPFILE=/var/tmp/swapfile
|
||||
|
||||
btrfs filesystem mkswapfile -s 10m "$SWAPFILE"
|
||||
sync -f "$SWAPFILE"
|
||||
|
||||
offset_btrfs_progs="$(btrfs inspect-internal map-swapfile -r "$SWAPFILE")"
|
||||
echo "btrfs-progs: $offset_btrfs_progs"
|
||||
|
||||
offset_btrfs_util="$("$TEST_BTRFS_OFFSET" "$SWAPFILE")"
|
||||
echo "btrfs-util: $offset_btrfs_util"
|
||||
|
||||
(( offset_btrfs_progs == offset_btrfs_util ))
|
||||
|
||||
rm -f "$SWAPFILE"
|
||||
|
||||
/usr/lib/systemd/tests/unit-tests/manual/test-btrfs
|
||||
|
||||
touch /testok
|
Loading…
Reference in a new issue