Compare commits

...

23 commits

Author SHA1 Message Date
David Sterba d61f8c4c95 btrfs-progs: build: use thread-safe profile generation flags
The CI lcov generation fails due to:

Processing ./common/device-utils.gcda
geninfo: ERROR: Unexpected negative count '-6' for /home/runner/work/btrfs-progs/btrfs-progs/common/device-utils.h:69.
	Perhaps you need to compile with '-fprofile-update=atomic
	(use "geninfo --ignore-errors negative ..." to bypass this error)

Use a safer way to gather the profile stats.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 05:44:26 +02:00
David Sterba 1dfd256a55 btrfs-progs: ci: fix lcov warnings on unexecuted blocks
There's a report on the CI after base ubuntu image update:

geninfo: WARNING:
/home/runner/work/btrfs-progs/btrfs-progs/common/device-scan.c:429:
unexecuted block on non-branch line with non-zero hit count.  Use
"geninfo --rc geninfo_unexecuted_blocks=1 to set count to zero.
	(use "geninfo --ignore-errors gcov,gcov ..." to suppress this warning)

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 05:14:44 +02:00
David Sterba 91a20bc76b btrfs-progs: tests: fix fssum ASAN memory leak reports
Free memory after errors in sum(), this is reported by gcc 13 on the CI.

This was reproduced by:

$ make D=asan TEST=019-receive-clones-on-mounted-subvol test-misc

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 05:14:25 +02:00
Qu Wenruo 917a7c2f41 btrfs-progs: tests: add test case for basic checksum conversion
Test the following:

- Create a btrfs with crc32c csum
- Populate the filesystem
- Convert the filesystem to the following csums:
  * xxhash
  * blake2
  * sha256
  * crc32c

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 03:31:20 +02:00
Qu Wenruo ef53fbdc8e btrfs-progs: change-csum: fix the wrong metadata space reservation
[BUG]
'btrfstune --csum' would always fail for a newly created btrfs:

  # truncate -s 1G test.img
  # ./mkfs.btrfs -f test.img
  # ./btrsftune --csum xxhash test.img
  WARNING: Experimental build with unstable or unfinished features
  WARNING: Switching checksums is experimental, do not use for valuable data!

  Proceed to switch checksums
  ERROR: failed to start transaction: Unknown error -28
  ERROR: failed to start transaction: No space left on device
  ERROR: failed to generate new data csums: No space left on device
  ERROR: btrfstune failed

[CAUSE]
After commit e79f18a4a7 ("btrfs-progs: introduce a basic metadata free
space reservation check"), btrfs_start_transaction() would check the
metadata space.

But at the time of introduction of csum conversion, the parameter for
btrfs_start_transaction() was incorrect.

The 2nd parameter is the *number* of items to be added (if we're
deleting items, just pass 1).
However commit 08a3bd7694 ("btrfs-progs: tune: add the ability to
generate new data checksums") is using the item size, not the number of
items to be added.

This means we're passing a number 8 * nodesize times larger than the
original size, no wonder we would error out with -ENOSPC.

[FIX]
Use proper calcuation to convert the new csum item size to number of
leaves needed, and double it just in case.

Pull-request: #820
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 03:29:36 +02:00
David Sterba 29f382042b btrfs-progs: inspect list-chunks: better sorting, updated output
Enhance the sorting capabilities of 'inspect list-chunks' to allow
multiple keys. Drop the gaps, this works only for pstart and it's hard
to make it work with arbitrary sort keys.

Usage is printed by default, assuming this is an interesting info and
even if it slows down the output (due to extra lookups) it's more
convenient to print it rather than not.

The options related to usage and empty were removed.

Output changes:

- rename Number to PNumber, meaning physical number on the device
- print Devid, device number, can be also sort key

Examples:

btrfs inspect list-chunks /mnt
btrfs inspect list-chunks --sort length,usage
btrfs inspect list-chunks --sort lstart

Depending on the sort key order, the output can be wild, for that the
PNumber and LNumber give some hint where the chunks lie in their space.

Example output:

$ sudo ./btrfs inspect list-chunks --sort length,usage /
Devid PNumber      Type/profile    PStart    Length      PEnd LNumber    LStart Usage%
----- ------- ----------------- --------- --------- --------- ------- --------- ------
    1       7       Data/single   1.52GiB  16.00MiB   1.54GiB      69 191.68GiB  86.04
    1       3     System/DUP    117.00MiB  32.00MiB 149.00MiB      40 140.17GiB   0.05
    1       2     System/DUP     85.00MiB  32.00MiB 117.00MiB      39 140.17GiB   0.05
    1      15       Data/single   8.04GiB  64.00MiB   8.10GiB      61 188.60GiB  94.46
    1       1       Data/single   1.00MiB  84.00MiB  85.00MiB      68 191.60GiB  74.24
    1       5   Metadata/DUP    341.00MiB 192.00MiB 533.00MiB      60 188.41GiB  82.58
    1       4   Metadata/DUP    149.00MiB 192.00MiB 341.00MiB      59 188.41GiB  82.58
    1      20   Metadata/DUP      9.29GiB 256.00MiB   9.54GiB      38 139.92GiB  57.76
    1      19   Metadata/DUP      9.04GiB 256.00MiB   9.29GiB      37 139.92GiB  57.76
    1      22   Metadata/DUP      9.79GiB 256.00MiB  10.04GiB      25 113.15GiB  57.93
    1      21   Metadata/DUP      9.54GiB 256.00MiB   9.79GiB      24 113.15GiB  57.93
    1      46   Metadata/DUP     29.29GiB 256.00MiB  29.54GiB      43 142.71GiB  62.38

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 03:07:23 +02:00
David Sterba 7c10c72134 btrfs-progs: update sorting API
Add parsing of user defined sorting specification and some helpers.

Example usage:

  sortdef = "key1,key2,key3"
  do {
    id = compare_parse_key_to_id(&comp, sortdef)
    if (id < 0) return error;
    compare_add_sort_id(&comp, id)
  } while(id >= 0);

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-19 03:07:22 +02:00
David Sterba 410d82d7b1 btrfs-progs: tests: fix shellcheck reports in cli-tests
- variable quoting
- cd error handling
- `` to $()
- command output instead of command (008-subvolume-get-set-default)

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-18 00:11:11 +02:00
David Sterba 7535c35128 btrfs-progs: fi df: extended information about profile types
There's more information available in sysfs
(/sys/fs/btrfs/FSID/allocation) that we can print in 'fi df'. This is
still meant for debugging or deeper analysis of the filesystem, the
values need to be correctly interpreted with respect to the profiles,
persistence and other conditonal features.

The extended output is not printed by default and for now is behind the
verbosity options:

$ btrfs -vv fi df /mnt
Data, single: total=47.06GiB, used=25.32GiB
System, DUP: total=32.00MiB, used=16.00KiB
Metadata, DUP: total=1.44GiB, used=961.20MiB
GlobalReserve, single: total=125.62MiB, used=0.00B
Data:
  bg_reclaim_threshold                   0%
  bytes_may_use                     8.00KiB
  bytes_pinned                        0.00B
  bytes_readonly                   64.00KiB
  bytes_reserved                      0.00B
  bytes_used                       25.32GiB
  bytes_zone_unusable                 0.00B
  chunk_size                       10.00GiB
  disk_total                       47.06GiB
  disk_used                        25.32GiB
  total_bytes                      47.06GiB
Metadata:
  bg_reclaim_threshold                   0%
  bytes_may_use                   126.62MiB
  bytes_pinned                        0.00B
  bytes_readonly                      0.00B
  bytes_reserved                      0.00B
  bytes_used                      961.20MiB
  bytes_zone_unusable                 0.00B
  chunk_size                      256.00MiB
  disk_total                        2.88GiB
  disk_used                         1.88GiB
  total_bytes                       1.44GiB
System:
  bg_reclaim_threshold                   0%
  bytes_may_use                       0.00B
  bytes_pinned                        0.00B
  bytes_readonly                      0.00B
  bytes_reserved                      0.00B
  bytes_used                       16.00KiB
  bytes_zone_unusable                 0.00B
  chunk_size                       32.00MiB
  disk_total                       64.00MiB
  disk_used                        32.00KiB
  total_bytes                      32.00MiB

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 23:09:23 +02:00
David Sterba 1487e61a2f btrfs-progs: docs: clarify receive --dump encoding
[ci skip]

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 22:53:58 +02:00
David Sterba 8b240d0625 btrfs-progs: receive dump: fix formatting of encoded write message
The separator of key=value is only one or more space character, the
'encoded_write' also uses ',' which is inconsistent with the rest.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 22:43:53 +02:00
David Sterba 751c57f950 btrfs-progs: tests: update misc/054 to test escaping of clone and xattrs
Add cases for clone source path and xattr name and value escapin in
receive dump.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 22:40:05 +02:00
David Sterba 7420ee7721 btrfs-progs: receive dump: encode clone path and xattr name/value
The xattr names are user strings but still can potentially contain
special characters (as reported). There doesn't seem to be a restriction
on the name defined.

The xattr values care length-encoded byte arrays so escaping needs be
done.

The clone source is a path and by mistake lacked the encoding.

Issue: #818
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 22:40:05 +02:00
David Sterba a35409328e btrfs-progs: use strncpy_null everywhere
Use the safe version of strncpy that makes sure the string is
terminated.

To be noted:

- the conversion in scrub path handling was skipped
- sizes of device paths in some ioctl related structures is
  BTRFS_DEVICE_PATH_NAME_MAX + 1

Recently gcc 13.3 started to detect problems with our use of strncpy
potentially lacking the null terminator, warnings like:

cmds/inspect.c: In function ‘cmd_inspect_logical_resolve’:
cmds/inspect.c:294:33: warning: ‘__builtin_strncpy’ specified bound 4096 equals destination size [-Wstringop-truncation]
  294 |                                 strncpy(mount_path, mounted, PATH_MAX);
      |                                 ^

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:53:04 +02:00
David Sterba 6ed16f7b09 btrfs-progs: rename and move __strncpy_null to string-utils
Now that there's only __strncpy_null we can drop the underscore and move
it to string-utils as it's a generic string function rather than
something for paths.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
David Sterba 4168fa41cc btrfs-progs: use explicit length parameters for string copy
The macro strncpy_null uses sizeof the first argument for the length,
but there are no checks and this works only for buffers with static
length, i.e. not pointers. This is error prone.  Use the open coded
variant that makes the sizeof visible.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
David Sterba 22b3246ef9 btrfs-progs: copy entire label buffer to target buffers
The label is of a fixed size 256 bytes and expects the zero terminator.
Using __strncpy_null is correct as it makes sure there's always the zero
termination but the argument passed in skips the last character.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
David Sterba 9506275242 btrfs-progs: build: add kernel-shared nested directories to tags
The tags can't find structures defined in kernel-shared/uapi/*.h due to
missing path.

Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
Qu Wenruo 12358fa061 btrfs-progs: mkfs-tests: ensure regular builds won't enable rst feature
This test case will check the following behviour:

- Regular zoned supported data/metadata profiles
  Should success

- RST with zoned feature
  Should fail for non-experimental builds

- zoned with data profiles that only RST supports
  Should fail for non-experimental builds.

  Meanwhile for experimental builds it should success and enable RST
  feature automatically (verified in mkfs/030)

Pull-request: #813
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
Qu Wenruo c600fcca81 btrfs-progs: move RST back to experimental
Currently raid-stripe-tree feature is still experimental as it requires
a BTRFS_DEBUG kernel to recognize it.  To avoid confusion move it back
to experimental so regular users won't incorrectly set it.

And since RST is no longer supported by default, also change the RST
profile detection so that for non-experimental build we won't enable RST
according to the data profiles.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
Qu Wenruo 2781f669e3 btrfs-progs: tests: hide RST related mkfs tests behind experimental builds
Currently we unconditionally run mkfs/029 and mkfs/030 as we export
RST feature through mkfs.btrfs as supported features.

But considering the mkfs.btrfs features don't match kernel support (only
a BTRFS_DEBUG kernel can support that), we're going to hide RST behind
experimental builds.

In that case RST related tests cases also need to be behind experimental
builds as regular builds of mkfs.btrfs will no longer support RST
feature.

Introduce two helpers:

- _test_config()
  To verify if certain config is set to 1

- check_experimental_build()
  A wrapper of "_test_config EXPERIMENTAL", and skip the test if
  btrfs-progs has no experimental features.

So that test cases can be skipped for non-experimental builds.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
Qu Wenruo cc2230e412 btrfs-progs: convert: remove raid-stripe-tree support
The raid-stripe-tree (RST) feature is for zoned devices to support extra
data profiles, and is not yet a stable feature (still requires
CONFIG_BTRFS_DEBUG enabled kernel to support it).

Furthermore the supported filesystems (ext*, reiserfs and ntfs) don't
even support zoned devices, and even we force RST support for
btrfs-convert, we would only create an empty tree for RST, as btrfs
convert would only result SINGLE data profile with SINGLE/DUP metadata
profile, neither needs RST at all.

Enabling RST for btrfs-convert would only cause problems for false test
failures as we incorrectly allow RST feature for btrfs-convert.

Fixes the problem by removing raid-stripe-tree support from
btrfs-convert and remove the test cases support for RST.

This patch is mostly reverting commit 346a381923 ("btrfs-progs:
convert: add raid-stripe-tree to allowed features"), but keeps the test
infrastructure to support bgt features for convert.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
Qu Wenruo 9cfa509133 btrfs-progs: tests: dump dmesg if a mount fails
[BUG]
There is a random failure for misc/028 in github CI, but unable to
reproduce locally.

[EXTRA DUMP]
The failure is at mount time, which is mostly out of our control (kernel
is provided by the CI image), but at least we can do extra kernel dump
if a mount fails.

This dump is done inside run_check() where if we detects there is a
"mount" word inside the command string.

The detection is not the best, it may detect other commands that
contains the word "mount", but it should be good enough to detect real
mount failure.

Pull-request: #815
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2024-06-17 21:52:48 +02:00
52 changed files with 622 additions and 312 deletions

View file

@ -44,7 +44,7 @@ jobs:
- name: Generate lcov results
run: |
lcov -c -d . -o lcov-info
genhtml -o lcov-out lcov-info
genhtml --rc geninfo_unexecuted_blocks=1 -o lcov-out lcov-info
- name: Save lcov results
uses: actions/upload-artifact@v4
with:

View file

@ -66,6 +66,11 @@ A subvolume is made read-only after the receiving process finishes successfully
dump the stream metadata, one line per operation
Does not require the *path* parameter. The filesystem remains unchanged.
Each stream command is on one line in the form of *key=value* and separated
by one or more spaces. Values that contain special characters (like
paths or extended attributes) are encoded in C-like way, e.g. *'\\n'* or
octal escape sequence like *'\\NNN'* where N is the char value. Same encoding
as is used in */proc* files.
-q|--quiet
(deprecated) alias for global *-q* option

View file

@ -314,8 +314,8 @@ ifeq ("$(origin D)", "command line")
endif
ifneq (,$(findstring gcov,$(D)))
DEBUG_CFLAGS_INTERNAL += -fprofile-arcs -ftest-coverage --coverage
DEBUG_LDFLAGS_INTERNAL += -fprofile-generate --coverage
DEBUG_CFLAGS_INTERNAL += -fprofile-arcs -fprofile-update=atomic -ftest-coverage --coverage
DEBUG_LDFLAGS_INTERNAL += -fprofile-generate -fprofile-update=atomic --coverage
endif
ifneq (,$(findstring verbose,$(D)))
@ -878,6 +878,7 @@ tags: FORCE
@echo " [TAGS] $(TAGS_CMD)"
$(Q)$(TAGS_CMD) *.[ch] image/*.[ch] convert/*.[ch] mkfs/*.[ch] \
check/*.[ch] kernel-lib/*.[ch] kernel-shared/*.[ch] \
kernel-shared/*/*.[ch] \
cmds/*.[ch] common/*.[ch] tune/*.[ch] \
libbtrfsutil/*.[ch]

View file

@ -1398,7 +1398,7 @@ int main(int argc, char **argv)
inode = arg_strtou64(optarg);
break;
case 'f':
strncpy(field, optarg, FIELD_BUF_LEN);
strncpy_null(field, optarg, FIELD_BUF_LEN);
break;
case 'x':
file_extent = arg_strtou64(optarg);

View file

@ -169,7 +169,7 @@ static int cmd_device_add(const struct cmd_struct *cmd,
}
memset(&ioctl_args, 0, sizeof(ioctl_args));
strncpy_null(ioctl_args.name, path);
strncpy_null(ioctl_args.name, path, sizeof(ioctl_args.name));
res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
if (res < 0) {
error("error adding device '%s': %m", path);
@ -287,7 +287,7 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
} else if (strcmp(argv[i], "missing") == 0 ||
cancel ||
path_is_block_device(argv[i]) == 1) {
strncpy_null(argv2.name, argv[i]);
strncpy_null(argv2.name, argv[i], sizeof(argv2.name));
} else {
error("not a block device: %s", argv[i]);
ret++;
@ -312,7 +312,7 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
continue;
}
memset(&arg, 0, sizeof(arg));
strncpy_null(arg.name, argv[i]);
strncpy_null(arg.name, argv[i], sizeof(arg.name));
res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
}
@ -396,7 +396,7 @@ static int btrfs_forget_devices(const char *path)
memset(&args, 0, sizeof(args));
if (path)
strncpy_null(args.name, path);
strncpy_null(args.name, path, sizeof(args.name));
ret = ioctl(fd, BTRFS_IOC_FORGET_DEV, &args);
if (ret)
ret = -errno;
@ -557,7 +557,7 @@ static int cmd_device_ready(const struct cmd_struct *cmd, int argc, char **argv)
}
memset(&args, 0, sizeof(args));
strncpy_null(args.name, path);
strncpy_null(args.name, path, sizeof(args.name));
ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
if (ret < 0) {
error("unable to determine if device '%s' is ready for mount: %m",
@ -799,9 +799,8 @@ static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
char path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
int err2;
strncpy(path, (char *)di_args[i].path,
BTRFS_DEVICE_PATH_NAME_MAX);
path[BTRFS_DEVICE_PATH_NAME_MAX] = 0;
strncpy_null(path, (char *)di_args[i].path,
BTRFS_DEVICE_PATH_NAME_MAX + 1);
args.devid = di_args[i].devid;
args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;

View file

@ -31,6 +31,7 @@
#include <limits.h>
#include <dirent.h>
#include <stdbool.h>
#include <ctype.h>
#include <uuid/uuid.h>
#include "libbtrfsutil/btrfsutil.h"
#include "kernel-lib/list.h"
@ -53,6 +54,7 @@
#include "common/device-utils.h"
#include "common/open-utils.h"
#include "common/parse-utils.h"
#include "common/sysfs-utils.h"
#include "common/string-utils.h"
#include "common/filesystem-utils.h"
#include "common/format-output.h"
@ -81,6 +83,41 @@ static const char * const cmd_filesystem_df_usage[] = {
NULL
};
static void print_df_by_type(int fd, unsigned int unit_mode) {
static const char *files[] = {
"bg_reclaim_threshold",
"bytes_may_use",
"bytes_pinned",
"bytes_readonly",
"bytes_reserved",
"bytes_used",
"bytes_zone_unusable",
"chunk_size",
"disk_total",
"disk_used",
"total_bytes",
};
char path[PATH_MAX] = { 0 };
const char *types[] = { "data", "metadata", "mixed", "system" };
u64 tmp;
int ret;
for (int ti = 0; ti < ARRAY_SIZE(types); ti++) {
for (int i = 0; i < ARRAY_SIZE(files); i++) {
path_cat3_out(path, "allocation", types[ti], files[i]);
ret = sysfs_read_fsid_file_u64(fd, path, &tmp);
if (ret < 0)
continue;
if (i == 0)
pr_verbose(LOG_INFO, "%c%s:\n", toupper(types[ti][0]), types[ti] + 1);
if (strcmp(files[i], "bg_reclaim_threshold") == 0)
pr_verbose(LOG_INFO, " %-24s %14llu%%\n", files[i], tmp);
else
pr_verbose(LOG_INFO, " %-24s %16s\n", files[i], pretty_size_mode(tmp, unit_mode));
}
}
}
static void print_df_text(int fd, struct btrfs_ioctl_space_args *sargs, unsigned unit_mode)
{
u64 i;
@ -100,6 +137,7 @@ static void print_df_text(int fd, struct btrfs_ioctl_space_args *sargs, unsigned
(ok ? ", zone_unusable=" : ""),
(ok ? pretty_size_mode(unusable, unit_mode) : ""));
}
print_df_by_type(fd, unit_mode);
}
static const struct rowspec filesystem_df_rowspec[] = {
@ -1441,7 +1479,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
memset(&args, 0, sizeof(args));
if (devid == (u64)-1) {
/* Ok to copy the string verbatim. */
strncpy_null(args.name, amount);
strncpy_null(args.name, amount, sizeof(args.name));
} else {
/* The implicit devid 1 needs to be adjusted. */
snprintf(args.name, sizeof(args.name) - 1, "%llu:%s", devid, amount);

View file

@ -258,7 +258,7 @@ static int cmd_inspect_logical_resolve(const struct cmd_struct *cmd,
if (name[0] == 0) {
path_ptr[-1] = '\0';
path_fd = fd;
strncpy(mount_path, full_path, PATH_MAX);
strncpy_null(mount_path, full_path, PATH_MAX);
} else {
char *mounted = NULL;
char subvol[PATH_MAX];
@ -291,7 +291,7 @@ static int cmd_inspect_logical_resolve(const struct cmd_struct *cmd,
continue;
}
strncpy(mount_path, mounted, PATH_MAX);
strncpy_null(mount_path, mounted, PATH_MAX);
free(mounted);
path_fd = btrfs_open_dir(mount_path);
@ -718,7 +718,7 @@ struct list_chunks_entry {
u64 flags;
u64 lnumber;
u64 used;
u32 number;
u64 pnumber;
};
struct list_chunks_ctx {
@ -727,7 +727,19 @@ struct list_chunks_ctx {
struct list_chunks_entry *stats;
};
static int cmp_cse_devid_start(const void *va, const void *vb)
static int cmp_cse_devid(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
if (a->devid < b->devid)
return -1;
if (a->devid > b->devid)
return 1;
return 0;
}
static int cmp_cse_devid_pstart(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
@ -739,38 +751,37 @@ static int cmp_cse_devid_start(const void *va, const void *vb)
if (a->start < b->start)
return -1;
if (a->start == b->start) {
error(
"chunks start on same offset in the same device: devid %llu start %llu",
a->devid, a->start);
if (a->start == b->start)
return 0;
}
return 1;
}
static int cmp_cse_devid_lstart(const void *va, const void *vb)
static int cmp_cse_pstart(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
if (a->devid < b->devid)
if (a->start < b->start)
return -1;
if (a->devid > b->devid)
return 1;
if (a->start == b->start)
return 0;
return 1;
}
static int cmp_cse_lstart(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
if (a->lstart < b->lstart)
return -1;
if (a->lstart == b->lstart) {
error(
"chunks logically start on same offset in the same device: devid %llu start %llu",
a->devid, a->lstart);
if (a->lstart == b->lstart)
return 0;
}
return 1;
}
/* Compare entries by usage percent, descending. */
static int cmp_cse_devid_usage(const void *va, const void *vb)
static int cmp_cse_usage(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
@ -785,7 +796,7 @@ static int cmp_cse_devid_usage(const void *va, const void *vb)
return 0;
}
static int cmp_cse_length_physical(const void *va, const void *vb)
static int cmp_cse_length(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
@ -794,173 +805,126 @@ static int cmp_cse_length_physical(const void *va, const void *vb)
return -1;
if (a->length > b->length)
return 1;
if (a->start < b->start)
return -1;
if (a->start > b->start)
return 1;
return 0;
}
static int cmp_cse_length_logical(const void *va, const void *vb)
{
const struct list_chunks_entry *a = va;
const struct list_chunks_entry *b = vb;
if (a->length < b->length)
return -1;
if (a->length > b->length)
return 1;
if (a->lstart < b->lstart)
return -1;
if (a->lstart > b->lstart)
return 1;
return 0;
}
static int print_list_chunks(struct list_chunks_ctx *ctx, const char* sortmode,
unsigned unit_mode, bool with_usage, bool with_empty)
static int print_list_chunks(struct list_chunks_ctx *ctx, const char *sortmode,
unsigned unit_mode)
{
u64 devid;
struct list_chunks_entry e;
struct string_table *table;
int col_count, col;
int i;
int chidx;
u64 lastend;
u64 number;
u32 gaps;
u32 tabidx;
static const struct sortdef sortit[] = {
{ .name = "pstart", .comp = (sort_cmp_t)cmp_cse_devid_start,
.desc = "sort by physical srart offset, group by device" },
{ .name = "lstart", .comp = (sort_cmp_t)cmp_cse_devid_lstart,
.desc = "sort by logical offset" },
{ .name = "usage", .comp = (sort_cmp_t)cmp_cse_devid_usage,
.desc = "sort by chunk usage" },
{ .name = "length_p", .comp = (sort_cmp_t)cmp_cse_length_physical,
.desc = "sort by lentgh, secondary by physical offset" },
{ .name = "length_l", .comp = (sort_cmp_t)cmp_cse_length_logical,
.desc = "sort by lentgh, secondary by logical offset" },
};
enum {
CHUNK_SORT_PSTART,
CHUNK_SORT_LSTART,
CHUNK_SORT_USAGE,
CHUNK_SORT_LENGTH_P,
CHUNK_SORT_LENGTH_L,
CHUNK_SORT_LENGTH,
CHUNK_SORT_DEFAULT = CHUNK_SORT_PSTART
};
unsigned int sort_mode;
static const struct sortdef sortit[] = {
{ .name = "devid", .comp = (sort_cmp_t)cmp_cse_devid,
.desc = "sort by device id (default, with pstart)",
.id = CHUNK_SORT_PSTART
},
{ .name = "pstart", .comp = (sort_cmp_t)cmp_cse_pstart,
.desc = "sort by physical start offset",
.id = CHUNK_SORT_PSTART
},
{ .name = "lstart", .comp = (sort_cmp_t)cmp_cse_lstart,
.desc = "sort by logical offset",
.id = CHUNK_SORT_LSTART
},
{ .name = "usage", .comp = (sort_cmp_t)cmp_cse_usage,
.desc = "sort by chunk usage",
.id = CHUNK_SORT_USAGE
},
{ .name = "length", .comp = (sort_cmp_t)cmp_cse_length,
.desc = "sort by lentgh",
.id = CHUNK_SORT_LENGTH
},
SORTDEF_END
};
const char *tmp;
struct compare comp;
int id;
if (!sortmode) {
sort_mode = CHUNK_SORT_DEFAULT;
sortmode = "pstart";
} else if (strcmp(sortmode, "pstart") == 0) {
sort_mode = CHUNK_SORT_PSTART;
} else if (strcmp(sortmode, "lstart") == 0) {
sort_mode = CHUNK_SORT_LSTART;
} else if (strcmp(sortmode, "usage") == 0) {
sort_mode = CHUNK_SORT_USAGE;
with_usage = true;
} else if (strcmp(sortmode, "length_p") == 0) {
sort_mode = CHUNK_SORT_LENGTH_P;
} else if (strcmp(sortmode, "length_l") == 0) {
sort_mode = CHUNK_SORT_LENGTH_L;
} else {
error("unknown sort mode: %s", sortmode);
exit(1);
}
compare_init(&comp, sortit);
tmp = sortmode;
do {
id = compare_parse_key_to_id(&comp, &tmp);
if (id == -1) {
error("unknown sort key: %s", tmp);
return 1;
}
compare_add_sort_id(&comp, id);
} while (id >= 0);
/*
* Chunks are sorted logically as found by the ioctl, we need to sort
* them once to find the physical ordering. This is the default mode.
*/
qsort(ctx->stats, ctx->length, sizeof(ctx->stats[0]), cmp_cse_devid_start);
qsort(ctx->stats, ctx->length, sizeof(ctx->stats[0]), cmp_cse_devid_pstart);
devid = 0;
number = 0;
gaps = 0;
lastend = 0;
for (i = 0; i < ctx->length; i++) {
e = ctx->stats[i];
if (e.devid != devid) {
devid = e.devid;
number = 0;
}
ctx->stats[i].number = number;
number++;
if (with_empty && sort_mode == CHUNK_SORT_PSTART && e.start != lastend)
gaps++;
lastend = e.start + e.length;
ctx->stats[i].pnumber = number++;
}
compare_init(&comp, sortit);
compare_add_sort_key(&comp, sortmode);
qsort_r(ctx->stats, ctx->length, sizeof(ctx->stats[0]), (sort_r_cmp_t)compare_cmp_multi,
&comp);
/* Skip additonal sort if nothing defined by user. */
if (comp.count > 0)
qsort_r(ctx->stats, ctx->length, sizeof(ctx->stats[0]),
(sort_r_cmp_t)compare_cmp_multi, &comp);
/* Optional usage, two rows for header and separator, gaps */
table = table_create(7 + (int)with_usage, 2 + ctx->length + gaps);
col_count = 9;
/* Two rows for header and separator. */
table = table_create(col_count, 2 + ctx->length);
if (!table) {
error_msg(ERROR_MSG_MEMORY, NULL);
return 1;
}
devid = 0;
/* Print header */
col = 0;
tabidx = 0;
chidx = 1;
table_printf(table, col++, tabidx, ">Devid");
table_printf(table, col++, tabidx, ">PNumber");
table_printf(table, col++, tabidx, ">Type/profile");
table_printf(table, col++, tabidx, ">PStart");
table_printf(table, col++, tabidx, ">Length");
table_printf(table, col++, tabidx, ">PEnd");
table_printf(table, col++, tabidx, ">LNumber");
table_printf(table, col++, tabidx, ">LStart");
table_printf(table, col++, tabidx, ">Usage%%");
for (int j = 0; j < col_count; j++)
table_printf(table, j, tabidx + 1, "*-");
tabidx = 2;
devid = 0;
for (i = 0; i < ctx->length; i++) {
e = ctx->stats[i];
/* TODO: print header and devid */
if (e.devid != devid) {
int j;
if (e.devid != devid)
devid = e.devid;
table_printf(table, 0, tabidx, ">Number");
table_printf(table, 1, tabidx, ">Type/profile");
table_printf(table, 2, tabidx, ">PStart");
table_printf(table, 3, tabidx, ">Length");
table_printf(table, 4, tabidx, ">PEnd");
table_printf(table, 5, tabidx, ">LNumber");
table_printf(table, 6, tabidx, ">LStart");
if (with_usage) {
table_printf(table, 7, tabidx, ">Usage%%");
table_printf(table, 7, tabidx + 1, "*-");
}
for (j = 0; j < 7; j++)
table_printf(table, j, tabidx + 1, "*-");
chidx = 1;
lastend = 0;
tabidx += 2;
}
if (with_empty && sort_mode == CHUNK_SORT_PSTART && e.start != lastend) {
table_printf(table, 0, tabidx, ">-");
table_printf(table, 1, tabidx, ">%s", "empty");
table_printf(table, 2, tabidx, ">-");
table_printf(table, 3, tabidx, ">%s",
pretty_size_mode(e.start - lastend, unit_mode));
table_printf(table, 4, tabidx, ">-");
table_printf(table, 5, tabidx, ">-");
table_printf(table, 6, tabidx, ">-");
if (with_usage)
table_printf(table, 7, tabidx, ">-");
tabidx++;
}
table_printf(table, 0, tabidx, ">%llu", chidx++);
table_printf(table, 1, tabidx, ">%10s/%-6s",
col = 0;
table_printf(table, col++, tabidx, ">%llu", e.devid);
table_printf(table, col++, tabidx, ">%llu", e.pnumber + 1);
table_printf(table, col++, tabidx, ">%10s/%-6s",
btrfs_group_type_str(e.flags),
btrfs_group_profile_str(e.flags));
table_printf(table, 2, tabidx, ">%s", pretty_size_mode(e.start, unit_mode));
table_printf(table, 3, tabidx, ">%s", pretty_size_mode(e.length, unit_mode));
table_printf(table, 4, tabidx, ">%s", pretty_size_mode(e.start + e.length, unit_mode));
table_printf(table, 5, tabidx, ">%llu", e.lnumber + 1);
table_printf(table, 6, tabidx, ">%s", pretty_size_mode(e.lstart, unit_mode));
if (with_usage)
table_printf(table, 7, tabidx, ">%6.2f",
(float)e.used / e.length * 100);
lastend = e.start + e.length;
table_printf(table, col++, tabidx, ">%s", pretty_size_mode(e.start, unit_mode));
table_printf(table, col++, tabidx, ">%s", pretty_size_mode(e.length, unit_mode));
table_printf(table, col++, tabidx, ">%s", pretty_size_mode(e.start + e.length, unit_mode));
table_printf(table, col++, tabidx, ">%llu", e.lnumber + 1);
table_printf(table, col++, tabidx, ">%s", pretty_size_mode(e.lstart, unit_mode));
table_printf(table, col++, tabidx, ">%6.2f", (float)e.used / e.length * 100);
tabidx++;
}
table_dump(table);
@ -1019,8 +983,6 @@ static int cmd_inspect_list_chunks(const struct cmd_struct *cmd,
int i;
unsigned unit_mode;
char *sortmode = NULL;
bool with_usage = true;
bool with_empty = true;
const char *path;
struct list_chunks_ctx ctx = {
.length = 0,
@ -1033,15 +995,10 @@ static int cmd_inspect_list_chunks(const struct cmd_struct *cmd,
while (1) {
int c;
enum { GETOPT_VAL_SORT = GETOPT_VAL_FIRST,
GETOPT_VAL_USAGE, GETOPT_VAL_NO_USAGE,
GETOPT_VAL_EMPTY, GETOPT_VAL_NO_EMPTY
};
static const struct option long_options[] = {
{"sort", required_argument, NULL, GETOPT_VAL_SORT },
{"usage", no_argument, NULL, GETOPT_VAL_USAGE },
{"no-usage", no_argument, NULL, GETOPT_VAL_NO_USAGE },
{"empty", no_argument, NULL, GETOPT_VAL_EMPTY },
{"no-empty", no_argument, NULL, GETOPT_VAL_NO_EMPTY },
{NULL, 0, NULL, 0}
};
@ -1054,14 +1011,6 @@ static int cmd_inspect_list_chunks(const struct cmd_struct *cmd,
free(sortmode);
sortmode = strdup(optarg);
break;
case GETOPT_VAL_USAGE:
case GETOPT_VAL_NO_USAGE:
with_usage = (c == GETOPT_VAL_USAGE);
break;
case GETOPT_VAL_EMPTY:
case GETOPT_VAL_NO_EMPTY:
with_empty = (c == GETOPT_VAL_EMPTY);
break;
default:
usage_unknown_option(cmd, argv);
}
@ -1136,7 +1085,7 @@ static int cmd_inspect_list_chunks(const struct cmd_struct *cmd,
e->lstart = sh.offset;
e->length = item->length;
e->flags = item->type;
e->number = -1;
e->pnumber = -1;
while (devid > lnumber_size) {
u64 *tmp;
unsigned old_size = lnumber_size;
@ -1152,13 +1101,9 @@ static int cmd_inspect_list_chunks(const struct cmd_struct *cmd,
lnumber = tmp;
}
e->lnumber = lnumber[devid]++;
if (with_usage) {
if (used == (u64)-1)
used = fill_usage(fd, sh.offset);
e->used = used;
} else {
e->used = 0;
}
if (used == (u64)-1)
used = fill_usage(fd, sh.offset);
e->used = used;
ctx.length++;
@ -1184,7 +1129,7 @@ static int cmd_inspect_list_chunks(const struct cmd_struct *cmd,
break;
}
ret = print_list_chunks(&ctx, sortmode, unit_mode, with_usage, with_empty);
ret = print_list_chunks(&ctx, sortmode, unit_mode);
close(fd);
out:

View file

@ -45,10 +45,9 @@
* Returns the length of the escaped characters. Unprintable characters are
* escaped as octals.
*/
static int print_path_escaped(const char *path)
static int print_path_escaped_len(const char *path, size_t path_len)
{
size_t i;
size_t path_len = strlen(path);
int len = 0;
for (i = 0; i < path_len; i++) {
@ -81,6 +80,11 @@ static int print_path_escaped(const char *path)
return len;
}
static int print_path_escaped(const char *path)
{
return print_path_escaped_len(path, strlen(path));
}
enum print_mode {
PRINT_DUMP_NORMAL,
PRINT_DUMP_SUBVOLUME,
@ -251,24 +255,37 @@ static int print_clone(const char *path, u64 offset, u64 len,
char full_path[PATH_MAX];
int ret;
PATH_CAT_OR_RET("clone", full_path, r->full_subvol_path, clone_path,
ret);
return PRINT_DUMP(user, path, "clone",
"offset=%llu len=%llu from=%s clone_offset=%llu",
offset, len, full_path, clone_offset);
PATH_CAT_OR_RET("clone", full_path, r->full_subvol_path, clone_path, ret);
PRINT_DUMP_NO_NEWLINE(user, path, "clone", "offset=%llu len=%llu from=", offset, len);
print_path_escaped(full_path);
putchar(' ');
printf("clone_offset=%llu\n", clone_offset);
return 0;
}
/*
* Xattr names are like paths and can potentially contain special characters,
* xattr values can be arbitrary.
*/
static int print_set_xattr(const char *path, const char *name,
const void *data, int len, void *user)
{
return PRINT_DUMP(user, path, "set_xattr", "name=%s data=%.*s len=%d",
name, len, (char *)data, len);
PRINT_DUMP_NO_NEWLINE(user, path, "set_xattr", "name=");
print_path_escaped(name);
putchar(' ');
print_path_escaped_len((char *)data, len);
putchar(' ');
printf("len=%d\n", len);
return 0;
}
static int print_remove_xattr(const char *path, const char *name, void *user)
{
return PRINT_DUMP(user, path, "remove_xattr", "name=%s", name);
PRINT_DUMP_NO_NEWLINE(user, path, "remove_xattr", "name=");
print_path_escaped(name);
putchar('\n');
return 0;
}
static int print_truncate(const char *path, u64 size, void *user)
@ -336,9 +353,7 @@ static int print_encoded_write(const char *path, const void *data, u64 offset,
u32 compression, u32 encryption, void *user)
{
return PRINT_DUMP(user, path, "encoded_write",
"offset=%llu len=%llu, unencoded_file_len=%llu, "
"unencoded_len=%llu, unencoded_offset=%llu, "
"compression=%u, encryption=%u",
"offset=%llu len=%llu unencoded_file_len=%llu unencoded_len=%llu unencoded_offset=%llu compression=%u encryption=%u",
offset, len, unencoded_file_len, unencoded_len,
unencoded_offset, compression, encryption);
}

View file

@ -179,7 +179,7 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
}
if (*rctx->dest_dir_path == 0) {
strncpy_null(rctx->cur_subvol_path, path);
strncpy_null(rctx->cur_subvol_path, path, sizeof(rctx->cur_subvol_path));
} else {
ret = path_cat_out(rctx->cur_subvol_path, rctx->dest_dir_path,
path);
@ -209,7 +209,7 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
}
memset(&args_v1, 0, sizeof(args_v1));
strncpy_null(args_v1.name, path);
strncpy_null(args_v1.name, path, sizeof(args_v1.name));
ret = ioctl(rctx->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
if (ret < 0) {
ret = -errno;
@ -249,7 +249,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}
if (*rctx->dest_dir_path == 0) {
strncpy_null(rctx->cur_subvol_path, path);
strncpy_null(rctx->cur_subvol_path, path, sizeof(rctx->cur_subvol_path));
} else {
ret = path_cat_out(rctx->cur_subvol_path, rctx->dest_dir_path,
path);
@ -281,7 +281,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}
memset(&args_v2, 0, sizeof(args_v2));
strncpy_null(args_v2.name, path);
strncpy_null(args_v2.name, path, sizeof(args_v2.name));
parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
parent_ctransid, NULL,
@ -663,7 +663,7 @@ static int open_inode_for_write(struct btrfs_receive *rctx, const char *path)
error("cannot open %s: %m", path);
goto out;
}
strncpy_null(rctx->write_path, path);
strncpy_null(rctx->write_path, path, sizeof(rctx->write_path));
out:
return ret;

View file

@ -266,8 +266,8 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
goto leave_with_error;
}
} else if (path_is_block_device(srcdev) > 0) {
strncpy((char *)start_args.start.srcdev_name, srcdev,
BTRFS_DEVICE_PATH_NAME_MAX);
strncpy_null((char *)start_args.start.srcdev_name, srcdev,
BTRFS_DEVICE_PATH_NAME_MAX + 1);
start_args.start.srcdevid = 0;
srcdev_size = device_get_partition_size(srcdev);
} else {
@ -292,8 +292,8 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
goto leave_with_error;
}
strncpy((char *)start_args.start.tgtdev_name, dstdev,
BTRFS_DEVICE_PATH_NAME_MAX);
strncpy_null((char *)start_args.start.tgtdev_name, dstdev,
BTRFS_DEVICE_PATH_NAME_MAX + 1);
ret = btrfs_prepare_device(fddstdev, dstdev, &dstdev_block_count, 0,
PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE |
(discard ? PREP_DEVICE_DISCARD : 0) |

View file

@ -1535,8 +1535,7 @@ static int cmd_restore(const struct cmd_struct *cmd, int argc, char **argv)
ret = 1;
goto out;
}
strncpy(dir_name, argv[optind + 1], sizeof dir_name);
dir_name[sizeof dir_name - 1] = 0;
strncpy_null(dir_name, argv[optind + 1], sizeof(dir_name));
/* Strip the trailing / on the dir name */
len = strlen(dir_name);

View file

@ -1437,7 +1437,7 @@ static int scrub_start(const struct cmd_struct *cmd, int argc, char **argv,
sock_path, sizeof(sock_path));
/* ignore EOVERFLOW, try using a shorter path for the socket */
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
strncpy_null(addr.sun_path, sock_path, sizeof(addr.sun_path));
ret = bind(prg_fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret != -1 || errno != EADDRINUSE)
break;

View file

@ -547,8 +547,7 @@ static int update_root(struct rb_root *root_lookup,
error_msg(ERROR_MSG_MEMORY, NULL);
exit(1);
}
strncpy(ri->name, name, name_len);
ri->name[name_len] = 0;
strncpy_null(ri->name, name, name_len);
}
if (ref_tree)
ri->ref_tree = ref_tree;
@ -619,8 +618,7 @@ static int add_root(struct rb_root *root_lookup,
error_msg(ERROR_MSG_MEMORY, NULL);
exit(1);
}
strncpy(ri->name, name, name_len);
ri->name[name_len] = 0;
strncpy_null(ri->name, name, name_len);
}
if (ref_tree)
ri->ref_tree = ref_tree;

View file

@ -197,7 +197,7 @@ static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *in
char dstdir_dup[PATH_MAX];
char *token;
strncpy_null(dstdir_dup, dstdir);
strncpy_null(dstdir_dup, dstdir, sizeof(dstdir_dup));
if (dstdir_dup[0] == '/')
strcat(p, "/");
@ -233,7 +233,7 @@ static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *in
struct btrfs_ioctl_vol_args_v2 args;
memset(&args, 0, sizeof(args));
strncpy_null(args.name, newname);
strncpy_null(args.name, newname, sizeof(args.name));
args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
args.size = btrfs_qgroup_inherit_size(inherit);
args.qgroup_inherit = inherit;
@ -243,8 +243,7 @@ static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *in
struct btrfs_ioctl_vol_args args;
memset(&args, 0, sizeof(args));
strncpy_null(args.name, newname);
strncpy_null(args.name, newname, sizeof(args.name));
ret = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
}
@ -738,7 +737,7 @@ static int cmd_subvolume_snapshot(const struct cmd_struct *cmd, int argc, char *
args.size = btrfs_qgroup_inherit_size(inherit);
args.qgroup_inherit = inherit;
}
strncpy_null(args.name, newname);
strncpy_null(args.name, newname, sizeof(args.name));
res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
if (res < 0) {

View file

@ -51,6 +51,7 @@
#include "common/utils.h"
#include "common/defs.h"
#include "common/open-utils.h"
#include "common/string-utils.h"
#include "common/units.h"
static int btrfs_scan_done = 0;
@ -237,7 +238,7 @@ int btrfs_register_one_device(const char *fname)
return -errno;
}
memset(&args, 0, sizeof(args));
strncpy_null(args.name, fname);
strncpy_null(args.name, fname, sizeof(args.name));
ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
if (ret < 0) {
error("device scan failed on '%s': %m", fname);
@ -468,7 +469,7 @@ int btrfs_scan_devices(int verbose)
if (!dev)
continue;
/* if we are here its definitely a btrfs disk*/
strncpy_null(path, blkid_dev_devname(dev));
strncpy_null(path, blkid_dev_devname(dev), sizeof(path));
if (stat(path, &dev_stat) < 0)
continue;

View file

@ -28,6 +28,7 @@
#include "common/filesystem-utils.h"
#include "common/messages.h"
#include "common/open-utils.h"
#include "common/string-utils.h"
#include "common/path-utils.h"
/*
@ -102,7 +103,7 @@ static int set_label_unmounted(const char *dev, const char *label)
error_msg(ERROR_MSG_START_TRANS, "set label");
return PTR_ERR(trans);
}
__strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1);
strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE);
btrfs_commit_transaction(trans, root);
@ -123,7 +124,7 @@ static int set_label_mounted(const char *mount_path, const char *labelp)
}
memset(label, 0, sizeof(label));
__strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1);
strncpy_null(label, labelp, BTRFS_LABEL_SIZE);
if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
error("unable to set label of %s: %m", mount_path);
close(fd);
@ -152,8 +153,7 @@ int get_label_unmounted(const char *dev, char *label)
if(!root)
return -1;
__strncpy_null(label, root->fs_info->super_copy->label,
BTRFS_LABEL_SIZE - 1);
strncpy_null(label, root->fs_info->super_copy->label, BTRFS_LABEL_SIZE);
/* Now we close it since we are done. */
close_ctree(root);
@ -187,7 +187,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
return ret;
}
__strncpy_null(labelp, label, BTRFS_LABEL_SIZE - 1);
strncpy_null(labelp, label, BTRFS_LABEL_SIZE);
close(fd);
return 0;
}

View file

@ -222,6 +222,7 @@ static const struct btrfs_feature mkfs_features[] = {
VERSION_NULL(default),
.desc = "block group tree, more efficient block group tracking to reduce mount time"
},
#if EXPERIMENTAL
{
.name = "rst",
.incompat_flag = BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE,
@ -238,6 +239,7 @@ static const struct btrfs_feature mkfs_features[] = {
VERSION_NULL(default),
.desc = "raid stripe tree, enhanced file extent tracking"
},
#endif
{
.name = "squota",
.incompat_flag = BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA,

View file

@ -68,8 +68,7 @@ static const struct btrfs_mkfs_features btrfs_convert_allowed_features = {
BTRFS_FEATURE_INCOMPAT_BIG_METADATA |
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF |
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA |
BTRFS_FEATURE_INCOMPAT_NO_HOLES |
BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE,
BTRFS_FEATURE_INCOMPAT_NO_HOLES,
.runtime_flags = BTRFS_FEATURE_RUNTIME_QUOTA,
};

View file

@ -47,8 +47,7 @@ void fixup_argv0(char **argv, const char *token)
void set_argv0(char **argv)
{
strncpy(argv0_buf, argv[0], sizeof(argv0_buf));
argv0_buf[sizeof(argv0_buf) - 1] = 0;
strncpy_null(argv0_buf, argv[0], sizeof(argv0_buf));
}
int check_argc_exact(int nargs, int expected)

View file

@ -32,6 +32,7 @@
#include "common/messages.h"
#include "common/path-utils.h"
#include "common/device-scan.h"
#include "common/string-utils.h"
#include "common/open-utils.h"
/*
@ -103,10 +104,9 @@ int check_mounted_where(int fd, const char *file, char *where, int size,
}
/* Did we find an entry in mnt table? */
if (mnt && size && where) {
strncpy(where, mnt->mnt_dir, size);
where[size-1] = 0;
}
if (mnt && size && where)
strncpy_null(where, mnt->mnt_dir, size);
if (fs_dev_ret)
*fs_dev_ret = fs_devices_mnt;
else if (noscan)

View file

@ -35,6 +35,7 @@
*/
#include <libgen.h>
#include <limits.h>
#include "common/string-utils.h"
#include "common/path-utils.h"
/*
@ -193,10 +194,10 @@ static int is_same_blk_file(const char* a, const char* b)
char real_b[PATH_MAX];
if (!realpath(a, real_a))
strncpy_null(real_a, a);
strncpy_null(real_a, a, sizeof(real_a));
if (!realpath(b, real_b))
strncpy_null(real_b, b);
strncpy_null(real_b, b, sizeof(real_b));
/* Identical path? */
if (strcmp(real_a, real_b) == 0)
@ -345,26 +346,6 @@ char *path_canonicalize(const char *path)
return canonical;
}
/*
* __strncpy_null - strncpy with null termination
* @dest: the target array
* @src: the source string
* @n: maximum bytes to copy (size of *dest)
*
* Like strncpy, but ensures destination is null-terminated.
*
* Copies the string pointed to by src, including the terminating null
* byte ('\0'), to the buffer pointed to by dest, up to a maximum
* of n bytes. Then ensure that dest is null-terminated.
*/
char *__strncpy_null(char *dest, const char *src, size_t n)
{
strncpy(dest, src, n);
if (n > 0)
dest[n - 1] = '\0';
return dest;
}
/*
* Test if path is a directory
* Returns:
@ -403,7 +384,7 @@ int path_is_in_dir(const char *parent, const char *path)
char *curr_dir = tmp;
int ret;
strncpy_null(tmp, path);
strncpy_null(tmp, path, sizeof(tmp));
while (strcmp(parent, curr_dir) != 0) {
if (strcmp(curr_dir, "/") == 0) {
@ -432,7 +413,7 @@ int arg_copy_path(char *dest, const char *src, int destlen)
if (len >= PATH_MAX || len >= destlen)
return -ENAMETOOLONG;
__strncpy_null(dest, src, destlen);
strncpy_null(dest, src, destlen);
return 0;
}

View file

@ -26,11 +26,6 @@ char *path_canonicalize(const char *path);
int arg_copy_path(char *dest, const char *src, int destlen);
int path_cat_out(char *out, const char *p1, const char *p2);
int path_cat3_out(char *out, const char *p1, const char *p2, const char *p3);
char *__strncpy_null(char *dest, const char *src, size_t n);
/* Helper to always get proper size of the destination string */
#define strncpy_null(dest, src) __strncpy_null(dest, src, sizeof(dest))
int path_is_block_device(const char *file);
int path_is_a_mount_point(const char *file);
int path_exists(const char *file);

View file

@ -16,7 +16,9 @@
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include "common/sort-utils.h"
#include "common/messages.h"
int compare_init(struct compare *comp, const struct sortdef *sortdef)
{
@ -55,3 +57,157 @@ int compare_add_sort_key(struct compare *comp, const char *key)
}
return 0;
}
/*
* Append given sort by its @id from associated sortdef.
*
* Return: 0 if id is valid
* -1 if id not in sortdef
*/
int compare_add_sort_id(struct compare *comp, int id)
{
int i;
if (!comp->sortdef)
return -1;
if (id < 0)
return -1;
for (i = 0; i < SORT_MAX_KEYS; i++) {
if (comp->sortdef[i].name == NULL)
return -1;
if (comp->sortdef[i].id == id) {
comp->comp[comp->count] = comp->sortdef[i].comp;
comp->count++;
break;
}
}
return 0;
}
/*
* Consume word-like list of key names (coma separated) and return its id if
* found in sortdef. The @next pointer is advanced to the next expected key start.
* Empty and NULL @next is accepted.
*
* Key lookup is case insensitive.
*
* Retrun: id from sortdef if a matching
* -1 on error
* -2 end of buffer
*/
int compare_parse_key_to_id(const struct compare *comp, const char **next)
{
const char *tmp = *next, *start = *next;
if (!comp->sortdef)
return -1;
/* No sort string (use defaults), or last. */
if (!*next || !**next)
return -2;
do {
/* End of word. */
if (*tmp == ',' || *tmp == 0) {
/* Look up in sortdef. */
for (int i = 0; comp->sortdef[i].name; i++) {
int len = strlen(comp->sortdef[i].name);
if (strncasecmp(start, comp->sortdef[i].name, len) == 0) {
/* Point to last NUL. */
*next = tmp;
/* Or the next valid char. */
if (*tmp)
(*next)++;
return comp->sortdef[i].id;
}
}
/* Not found, report which one. */
*next = start;
return -1;
}
/* Invalid char found. */
if (!isalnum(*tmp)) {
*next = tmp;
return -1;
}
tmp++;
} while(1);
/* Not found. */
*next = start;
return -1;
}
/* Read id of its associated sort @key. Key lookup is case insensitive. */
int compare_key_id(const struct compare *comp, const char *key)
{
if (!comp->sortdef)
return -1;
for (int i = 0; comp->sortdef[i].name; i++)
if (strcasecmp(comp->sortdef[i].name, key) == 0)
return comp->sortdef[i].id;
return -1;
}
/* Read sort key name associated to @id. */
const char *compare_id_name(const struct compare *comp, int id)
{
if (!comp->sortdef)
return NULL;
for (int i = 0; comp->sortdef[i].name; i++)
if (comp->sortdef[i].id == id)
return comp->sortdef[i].name;
return NULL;
}
/*
* Check if the given @id (must exist in the associated sortdef) enabled in
* @comp.
*/
bool compare_has_id(const struct compare *comp, int id)
{
int idx;
if (!comp->sortdef)
return false;
idx = -1;
for (int i = 0; comp->sortdef[i].name; i++)
if (comp->sortdef[i].id == id)
idx = i;
if (idx < 0)
return false;
for (int i = 0; i < comp->count; i++)
if (comp->comp[i] == comp->sortdef[idx].comp)
return true;
return false;
}
/*
* Set up compare structure with associated sortdef from a user specified list
* of keys.
*/
int compare_setup_sort(struct compare *comp, const struct sortdef *sdef, const char *def)
{
const char *tmp;
int id;
tmp = def;
do {
id = compare_parse_key_to_id(comp, &tmp);
if (id == -1) {
error("unknown sort key: %s", tmp);
return -1;
}
compare_add_sort_id(comp, id);
} while (id >= 0);
return 0;
}

View file

@ -17,6 +17,8 @@
#ifndef __COMMON_SORT_UTILS_H__
#define __COMMON_SORT_UTILS_H__
#include <stdbool.h>
/*
* Example:
@ -48,7 +50,7 @@ void test() {
.desc = "sort by id" },
{ .name = "size", .comp = (sort_cmp_t)cmp_entry_size,
.desc = "sort by entry size" },
{ .name = NULL, .comp = NULL }
SORTDEF_END
};
// List of keys to use for sort (e.g. from command line options)
const char *sortby[] = { "size", "id" };
@ -66,17 +68,24 @@ void test() {
}
*/
#define SORTDEF_END { .name = NULL, .comp = NULL }
#define SORT_MAX_KEYS 32
typedef int (*sort_cmp_t)(const void *a, const void *b);
typedef int (*sort_r_cmp_t)(const void *a, const void *b, void *data);
#define SORTDEF_END { .name = NULL, .comp = NULL }
struct sortdef {
const char *name;
const char *desc;
sort_cmp_t comp;
/* User defined identifier of this sort key. */
int id;
};
struct compare {
sort_cmp_t comp[32];
sort_cmp_t comp[SORT_MAX_KEYS];
unsigned long invert_map;
int count;
const struct sortdef *sortdef;
@ -85,5 +94,11 @@ struct compare {
int compare_init(struct compare *comp, const struct sortdef *sortdef);
int compare_cmp_multi(const void *a, const void *b, const struct compare *comp);
int compare_add_sort_key(struct compare *comp, const char *key);
int compare_parse_key_to_id(const struct compare *comp, const char **next);
int compare_add_sort_id(struct compare *comp, int id);
int compare_key_id(const struct compare *comp, const char *key);
const char *compare_id_name(const struct compare *comp, int id);
bool compare_has_id(const struct compare *comp, int id);
int compare_setup_sort(struct compare *comp, const struct sortdef *sdef, const char *def);
#endif

View file

@ -44,6 +44,26 @@ int string_has_prefix(const char *str, const char *prefix)
return (unsigned char)*prefix - (unsigned char)*str;
}
/*
* strncpy_null - strncpy with null termination
* @dest: the target array
* @src: the source string
* @n: maximum bytes to copy (size of *dest)
*
* Like strncpy, but ensures destination is null-terminated.
*
* Copies the string pointed to by src, including the terminating null
* byte ('\0'), to the buffer pointed to by dest, up to a maximum
* of n bytes. Then ensure that dest is null-terminated.
*/
char *strncpy_null(char *dest, const char *src, size_t n)
{
strncpy(dest, src, n);
if (n > 0)
dest[n - 1] = '\0';
return dest;
}
/*
* This function should be only used when parsing command arg, it won't return
* error to its caller and rather exit directly just like usage().

View file

@ -22,6 +22,8 @@
int string_is_numerical(const char *str);
int string_has_prefix(const char *str, const char *prefix);
char *strncpy_null(char *dest, const char *src, size_t n);
/*
* Helpers prefixed by arg_* can exit if the argument is invalid and are supposed
* to be used when parsing command line options where the immediate exit is valid

View file

@ -30,6 +30,7 @@
#include "kernel-shared/uapi/btrfs_tree.h"
#include "common/path-utils.h"
#include "common/messages.h"
#include "common/string-utils.h"
#include "common/fsfeatures.h"
#include "mkfs/common.h"
#include "convert/common.h"
@ -148,7 +149,7 @@ static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg,
btrfs_set_super_cache_generation(&super, -1);
btrfs_set_super_incompat_flags(&super, cfg->features.incompat_flags);
if (cfg->label)
__strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE);
/* Sys chunk array will be re-initialized at chunk tree init time */
super.sys_chunk_array_size = 0;

View file

@ -1322,8 +1322,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
if (convert_flags & CONVERT_FLAG_COPY_LABEL) {
__strncpy_null(root->fs_info->super_copy->label,
cctx.label, BTRFS_LABEL_SIZE - 1);
strncpy_null(root->fs_info->super_copy->label, cctx.label, BTRFS_LABEL_SIZE);
printf("Copy label '%s'\n", root->fs_info->super_copy->label);
} else if (convert_flags & CONVERT_FLAG_SET_LABEL) {
strcpy(root->fs_info->super_copy->label, fslabel);
@ -1938,7 +1937,7 @@ int BOX_MAIN(convert)(int argc, char *argv[])
"label too long, trimmed to %d bytes",
BTRFS_LABEL_SIZE - 1);
}
__strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1);
strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE);
break;
case 'L':
copylabel = CONVERT_FLAG_COPY_LABEL;
@ -1997,7 +1996,7 @@ int BOX_MAIN(convert)(int argc, char *argv[])
error("invalid UUID: %s\n", optarg);
return 1;
}
strncpy(fsid, optarg, sizeof(fsid));
strncpy_null(fsid, optarg, sizeof(fsid));
}
break;
case GETOPT_VAL_HELP:

View file

@ -30,6 +30,7 @@
#include "kernel-shared/file-item.h"
#include "common/extent-cache.h"
#include "common/messages.h"
#include "common/string-utils.h"
#include "convert/common.h"
#include "convert/source-fs.h"
#include "convert/source-ext2.h"
@ -638,7 +639,7 @@ static int ext2_copy_single_xattr(struct btrfs_trans_handle *trans,
data = databuf;
datalen = bufsize;
}
strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
strncpy_null(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root->fs_info) -
sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {

View file

@ -36,6 +36,7 @@
#include "common/defs.h"
#include "common/internal.h"
#include "common/messages.h"
#include "common/string-utils.h"
#include "uapi/btrfs.h"
static void print_dir_item_type(struct extent_buffer *eb,
@ -186,7 +187,7 @@ static void bg_flags_to_str(u64 flags, char *ret)
ret[0] = '\0';
if (flags & BTRFS_BLOCK_GROUP_DATA) {
empty = 0;
strncpy(ret, "DATA", BG_FLAG_STRING_LEN);
strncpy_null(ret, "DATA", BG_FLAG_STRING_LEN);
}
if (flags & BTRFS_BLOCK_GROUP_METADATA) {
if (!empty)
@ -209,7 +210,7 @@ static void bg_flags_to_str(u64 flags, char *ret)
* Thus here we only fill @profile if it's not single.
*/
if (strncmp(name, "SINGLE", strlen("SINGLE")) != 0)
strncpy(profile, name, BG_FLAG_STRING_LEN);
strncpy_null(profile, name, BG_FLAG_STRING_LEN);
}
if (profile[0]) {
strncat(ret, "|", BG_FLAG_STRING_LEN);
@ -1398,7 +1399,7 @@ static void print_header_info(struct extent_buffer *eb, unsigned int mode)
#define DEV_REPLACE_STRING_LEN 64
#define CASE_DEV_REPLACE_MODE_ENTRY(dest, name) \
case BTRFS_DEV_REPLACE_ITEM_CONT_READING_FROM_SRCDEV_MODE_##name: \
strncpy((dest), #name, DEV_REPLACE_STRING_LEN); \
strncpy_null((dest), #name, DEV_REPLACE_STRING_LEN); \
break;
static void replace_mode_to_str(u64 flags, char *ret)
@ -1415,7 +1416,7 @@ static void replace_mode_to_str(u64 flags, char *ret)
#define CASE_DEV_REPLACE_STATE_ENTRY(dest, name) \
case BTRFS_IOCTL_DEV_REPLACE_STATE_##name: \
strncpy((dest), #name, DEV_REPLACE_STRING_LEN); \
strncpy_null((dest), #name, DEV_REPLACE_STRING_LEN); \
break;
static void replace_state_to_str(u64 flags, char *ret)

View file

@ -38,6 +38,7 @@
#include "common/path-utils.h"
#include "common/device-utils.h"
#include "common/open-utils.h"
#include "common/string-utils.h"
#include "mkfs/common.h"
static u64 reference_root_table[] = {
@ -476,7 +477,7 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
btrfs_set_super_nr_global_roots(&super, 1);
if (cfg->label)
__strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE);
/* create the tree of root objects */
memset(buf->data, 0, cfg->nodesize);

View file

@ -1361,8 +1361,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
source_dir = strdup(optarg);
break;
case 'U':
strncpy(fs_uuid, optarg,
BTRFS_UUID_UNPARSED_SIZE - 1);
strncpy_null(fs_uuid, optarg, BTRFS_UUID_UNPARSED_SIZE);
break;
case 'K':
opt_discard = false;
@ -1371,7 +1370,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
bconf_be_quiet();
break;
case GETOPT_VAL_DEVICE_UUID:
strncpy(dev_uuid, optarg, BTRFS_UUID_UNPARSED_SIZE - 1);
strncpy_null(dev_uuid, optarg, BTRFS_UUID_UNPARSED_SIZE);
break;
case GETOPT_VAL_SHRINK:
shrink_rootdir = true;
@ -1693,7 +1692,9 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
case BTRFS_BLOCK_GROUP_RAID1C4:
case BTRFS_BLOCK_GROUP_RAID0:
case BTRFS_BLOCK_GROUP_RAID10:
#if EXPERIMENTAL
features.incompat_flags |= BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE;
#endif
break;
default:
break;

View file

@ -6,7 +6,7 @@ source "$TEST_TOP/common" || exit
check_prereq btrfs
# returns 1
run_mayfail $TOP/btrfs || true
run_mayfail "$TOP/btrfs" || true
run_check "$TOP/btrfs" version
run_check "$TOP/btrfs" version --
run_check "$TOP/btrfs" help

View file

@ -13,7 +13,7 @@ prepare_test_dev
run_check_mkfs_test_dev
run_check_mount_test_dev
here=`pwd`
here=$(pwd)
cd "$TEST_MNT" || _fail "cannot chdir to TEST_MNT"
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create subv-parent

View file

@ -13,14 +13,14 @@ check_default_id()
{
id=$(run_check_stdout $SUDO_HELPER "$TOP/btrfs" subvolume get-default .) \
|| { echo "$id"; exit 1; }
if $(echo "$id" | grep -vq "ID $1"); then
if echo "$id" | grep -vq "ID $1"; then
_fail "subvolume get-default: default id is not $1, but $id"
fi
}
run_check_mkfs_test_dev
run_check_mount_test_dev
cd "$TEST_MNT"
cd "$TEST_MNT" || _fail "Cannot cd into TEST_MNT $TEST_MNT"
check_default_id 5

View file

@ -62,8 +62,8 @@ test_run_commands() {
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d single -m single "${loopdevs[@]}"
run_check_mount_test_dev
run_check "$TOP/btrfs" filesystem usage "$TEST_MNT"
for i in `seq 10`; do
run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT"/file$i bs=100M count=1 status=none
for i in $(seq 10); do
run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/file$i" bs=100M count=1 status=none
done
# Create filesystem with single and RAID1 profiles
run_check $SUDO_HELPER "$TOP/btrfs" balance start -dconvert=raid1,limit=1 "$TEST_MNT"
@ -76,8 +76,8 @@ run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f --mixed -d single -m single "${loopd
run_check_mount_test_dev
run_check "$TOP/btrfs" filesystem usage "$TEST_MNT"
# Create 1 and a half of 1G chunks
for i in `seq 14`; do
run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT"/file$i bs=100M count=1 status=none
for i in $(seq 14); do
run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/file$i" bs=100M count=1 status=none
done
# Create filesystem with single and RAID1 profiles, the limit=1 trick does not work
# so use the usage filter to convert about half of the filesystem

View file

@ -63,7 +63,7 @@ test_raid1()
set -- $i
IFS=$OLDIFS
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d"$1" ${loopdevs[@]}
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d"$1" "${loopdevs[@]}"
run_check_mount_test_dev
vars=($(report_numbers))
data_chunk_size=${vars[1]}
@ -87,7 +87,7 @@ test_raid0()
local used_on_dev
local data_ratio
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -draid0 ${loopdevs[@]}
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -draid0 "${loopdevs[@]}"
run_check_mount_test_dev
vars=($(report_numbers))
data_chunk_size=${vars[1]}
@ -118,7 +118,7 @@ test_raid56()
set -- $i
IFS=$OLDIFS
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d"$1" ${loopdevs[@]}
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -d"$1" "${loopdevs[@]}"
run_check_mount_test_dev
vars=($(report_numbers))
data_chunk_size=${vars[1]}

View file

@ -36,11 +36,11 @@ fi
# Set the limits by command
here=`pwd`
cd "$sysfs/devinfo"
cd "$sysfs/devinfo" || _fail "Cannot cd to $sysfs/devinfo"
for i in *; do
run_check $SUDO_HELPER "$TOP/btrfs" scrub limit -d "$i" -l 20m "$TEST_MNT"
done
cd "$here"
cd "$here" || _fail "Cannot cd to $here"
run_check "$TOP/btrfs" scrub limit "$TEST_MNT"
# Set limits for all devices

View file

@ -6,6 +6,22 @@
# Temporary array for building the final command, injecting arguments as needed
declare -a cmd_array
# Check if a given option in config.h is set to 1
# $1: config option name
_test_config()
{
local feature="$1"
if [ ! -f "$TOP/include/config.h" ]; then
echo "include/config.h not exists"
exit 1
fi
if grep -q "$feature.*1" "${TOP}/include/config.h"; then
return 0
fi
return 1
}
# assert that argument is not empty and is an existing path (file or directory)
_assert_path()
{
@ -220,7 +236,13 @@ run_check()
echo "====== RUN CHECK ${cmd_array[@]}" >> "$RESULTS" 2>&1
if [[ $TEST_LOG =~ tty ]]; then echo "CMD: ${cmd_array[@]}" > /dev/tty; fi
"${cmd_array[@]}" >> "$RESULTS" 2>&1 || _fail "failed: ${cmd_array[@]}"
"${cmd_array[@]}" >> "$RESULTS" 2>&1
if [ "$?" -ne 0 ]; then
if cat "${cmd_array[@]}" | grep -q mount; then
dmesg | tail -n 15 >> "$RESULTS"
fi
_fail "failed: ${cmd_array[@]}"
fi
}
# same as run_check but the stderr+stdout output is duplicated on stdout and
@ -370,6 +392,20 @@ run_mustfail_stdout()
fi
}
check_experimental_build()
{
if ! _test_config "EXPERIMENTAL"; then
_not_run "This test requires experimental build"
fi
}
check_regular_build()
{
if _test_config "EXPERIMENTAL"; then
_not_run "This test requires non-experimental build"
fi
}
check_prereq()
{
# Internal tools for testing, not shipped with the package

View file

@ -12,7 +12,7 @@ check_kernel_support_acl
# Iterate over defaults and options that are not tied to hardware capabilities
# or number of devices
for feature in '' 'block-group-tree' 'raid-stripe-tree'; do
for feature in '' 'block-group-tree'; do
convert_test ext2 "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096
convert_test ext2 "$feature" "ext2 16k nodesize" 16384 mke2fs -b 4096
convert_test ext2 "$feature" "ext2 64k nodesize" 65536 mke2fs -b 4096

View file

@ -12,7 +12,7 @@ check_kernel_support_acl
# Iterate over defaults and options that are not tied to hardware capabilities
# or number of devices
for feature in '' 'block-group-tree' 'raid-stripe-tree' ; do
for feature in '' 'block-group-tree'; do
convert_test ext4 "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
convert_test ext4 "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
convert_test ext4 "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096

View file

@ -68,7 +68,7 @@ do_test() {
# Iterate over defaults and options that are not tied to hardware capabilities
# or number of devices
for feature in '' 'block-group-tree' 'raid-stripe-tree' ; do
for feature in '' 'block-group-tree'; do
do_test "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096
do_test "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096
do_test "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096

View file

@ -15,7 +15,7 @@ prepare_test_dev
# Iterate over defaults and options that are not tied to hardware capabilities
# or number of devices
for feature in '' 'block-group-tree' 'raid-stripe-tree'; do
for feature in '' 'block-group-tree'; do
convert_test reiserfs "$feature" "reiserfs 4k nodesize" 4096 mkreiserfs -b 4096
convert_test reiserfs "$feature" "reiserfs 16k nodesize" 16384 mkreiserfs -b 4096
convert_test reiserfs "$feature" "reiserfs 64k nodesize" 65536 mkreiserfs -b 4096

View file

@ -70,7 +70,7 @@ do_test() {
# Iterate over defaults and options that are not tied to hardware capabilities
# or number of devices
for feature in '' 'block-group-tree' 'raid-stripe-tree'; do
for feature in '' 'block-group-tree'; do
do_test "$feature" "reiserfs 4k nodesize" 4096 mkreiserfs -b 4096
do_test "$feature" "reiserfs 16k nodesize" 16384 mkreiserfs -b 4096
do_test "$feature" "reiserfs 64k nodesize" 65536 mkreiserfs -b 4096

View file

@ -17,6 +17,6 @@ prepare_test_dev
# Iterate over defaults and options that are not tied to hardware capabilities
# or number of devices. Test only 4K block size as minimum.
for feature in '' 'block-group-tree' 'raid-stripe-tree'; do
for feature in '' 'block-group-tree'; do
convert_test ntfs "$feature" "ntfs 4k nodesize" 4096 mkfs.ntfs -s 4096
done

View file

@ -524,6 +524,7 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
int ret;
int fd;
int excl;
int error = 0;
sum_file_data_t sum_file_data = flags[FLAG_STRUCTURE] ?
sum_file_data_strict : sum_file_data_permissive;
struct stat dir_st;
@ -542,18 +543,22 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
if (entries == alloclen) {
void *tmp;
alloclen += CHUNKS;
namelist = realloc(namelist,
alloclen * sizeof(*namelist));
if (!namelist) {
fprintf(stderr, "malloc failed\n");
exit(-1);
tmp = realloc(namelist, alloclen * sizeof(*namelist));
if (!tmp) {
fprintf(stderr, "realloc failed\n");
error = 1;
goto free_namelist;
}
namelist = tmp;
}
namelist[entries] = strdup(de->d_name);
if (!namelist[entries]) {
fprintf(stderr, "malloc failed\n");
exit(-1);
fprintf(stderr, "stdup failed\n");
error = 1;
goto free_namelist;
}
++entries;
}
@ -577,13 +582,15 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
ret = fchdir(dirfd);
if (ret == -1) {
perror("fchdir");
exit(-1);
error = 1;
goto free_namelist;
}
ret = lstat(namelist[i], &st);
if (ret) {
fprintf(stderr, "stat failed for %s/%s: %m\n",
path_prefix, path);
exit(-1);
error = 1;
goto free_namelist;
}
/* We are crossing into a different subvol, skip this subtree. */
@ -704,6 +711,14 @@ sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
next:
free(path);
}
free_namelist:
closedir(d);
for (i = 0; i < entries; i++)
free(namelist[i]);
free(namelist);
if (error)
exit(-1);
}
int

View file

@ -1,6 +1,6 @@
#!/bin/bash
# Verify that receive --dump escapes paths for rename, symlink and hardlink
# when it's the "dest=" value
# when it's the "dest=" value, clone (source path) and xattr name and value
source "$TEST_TOP/common" || exit
@ -19,7 +19,7 @@ run_check $SUDO_HELPER ln -s "$TEST_MNT/subv1/file
failed symlink source" "$TEST_MNT/subv1/file
failed symlink target"
# Hardlinke
# Hardlink
run_check $SUDO_HELPER touch "$TEST_MNT/subv1/file
failed link source"
@ -27,6 +27,19 @@ run_check $SUDO_HELPER ln "$TEST_MNT/subv1/file
failed link source" "$TEST_MNT/subv1/file
failed link target"
# Xattr name and value, create
run_check $SUDO_HELPER touch "$TEST_MNT/subv1/xattr-file"
run_check $SUDO_HELPER setfattr -n user.'name
failed xattr name' -v '123
failed xattr value' "$TEST_MNT/subv1/xattr-file"
# Clone
run_check $SUDO_HELPER dd if=/dev/zero of="$TEST_MNT/subv1/clone-source
failed clone source write" bs=1M count=1
run_check $SUDO_HELPER cp --reflink=always "$TEST_MNT/subv1/clone-source
failed clone source write" "$TEST_MNT/subv1/clone-target
failed clone target"
run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "$TEST_MNT/subv1" "$TEST_MNT/snap1"
_mktemp_local send.stream
run_check $SUDO_HELPER "$TOP/btrfs" send -f send.stream "$TEST_MNT/snap1"

View file

@ -0,0 +1,30 @@
#!/bin/bash
# Verify the csum conversion works as expected.
source "$TEST_TOP/common" || exit
source "$TEST_TOP/common.convert" || exit
check_experimental_build
setup_root_helper
prepare_test_dev
convert_to_csum()
{
local new_csum="$1"
run_check "$TOP/btrfstune" --csum "$new_csum" "$TEST_DEV"
run_check "$TOP/btrfs" check --check-data-csum "$TEST_DEV"
}
run_check_mkfs_test_dev --csum crc32c
# We only mount the filesystem once to populate its contents, later one we
# would never mount the fs (to reduce the dependency on kernel features).
run_check_mount_test_dev
populate_fs
run_check_umount_test_dev
convert_to_csum xxhash
convert_to_csum blake2
convert_to_csum sha256
convert_to_csum crc32c

View file

@ -3,6 +3,7 @@
source "$TEST_TOP/common" || exit
check_experimental_build
check_prereq mkfs.btrfs
check_prereq btrfs

View file

@ -3,6 +3,7 @@
source "$TEST_TOP/common" || exit
check_experimental_build
setup_root_helper
setup_nullbdevs 4 128 4
prepare_nullbdevs

View file

@ -0,0 +1,34 @@
#!/bin/bash
# Verify mkfs for all currently supported profiles of zoned + raid-stripe-tree
source "$TEST_TOP/common" || exit
check_regular_build
setup_root_helper
setup_nullbdevs 4 128 4
prepare_nullbdevs
TEST_DEV=${nullb_devs[1]}
profiles="dup raid1 raid1c3 raid1c4 raid10"
# The existing supported profiles.
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -O zoned -d single -m single "${nullb_devs[@]}"
run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -O zoned -d single -m DUP "${nullb_devs[@]}"
# RST feature is rejected
run_mustfail "RST feature created" \
$SUDO_HELPER "$TOP/mkfs.btrfs" -f -O zoned,raid-stripe-tree -d single -m DUP "${nullb_devs[@]}"
for dprofile in $profiles; do
# Make sure additional data profiles won't enable RST for non-experimental build
run_mustfail "unsupported profile created" \
$SUDO_HELPER "$TOP/mkfs.btrfs" -f -O zoned -d "$dprofile" -m DUP "${nullb_devs[@]}"
done
# The old unsupported profiles should fail no matter if experimental build is enabled or not.
run_mustfail "unsupported profile raid56 created" \
$SUDO_HELPER "$TOP/mkfs.btrfs" -f -O zoned -d raid5 -m raid5 "${nullb_devs[@]}"
run_mustfail "unsupported profile raid56 created" \
$SUDO_HELPER "$TOP/mkfs.btrfs" -f -O zoned -d raid6 -m raid6 "${nullb_devs[@]}"
cleanup_nullbdevs

View file

@ -224,14 +224,25 @@ out:
* item.
*/
#define CSUM_CHANGE_BYTES_THRESHOLD (SZ_2M)
static unsigned int calc_csum_change_nr_items(struct btrfs_fs_info *fs_info,
u16 new_csum_type)
{
const u32 new_csum_size = btrfs_csum_type_size(new_csum_type);
const u32 csum_item_size = CSUM_CHANGE_BYTES_THRESHOLD /
fs_info->sectorsize * new_csum_size;
return round_up(csum_item_size, fs_info->nodesize) / fs_info->nodesize * 2;
}
static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 start,
u16 new_csum_type)
{
const unsigned int nr_items = calc_csum_change_nr_items(fs_info, new_csum_type);
struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
struct btrfs_trans_handle *trans;
struct btrfs_path path = { 0 };
struct btrfs_key key;
const u32 new_csum_size = btrfs_csum_type_size(new_csum_type);
void *csum_buffer;
u64 converted_bytes = 0;
u64 last_csum;
@ -248,9 +259,7 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
if (!csum_buffer)
return -ENOMEM;
trans = btrfs_start_transaction(csum_root,
CSUM_CHANGE_BYTES_THRESHOLD / fs_info->sectorsize *
new_csum_size);
trans = btrfs_start_transaction(csum_root, nr_items);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
@ -306,9 +315,7 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
return -EUCLEAN;
if (ret < 0)
goto out;
trans = btrfs_start_transaction(csum_root,
CSUM_CHANGE_BYTES_THRESHOLD /
fs_info->sectorsize * new_csum_size);
trans = btrfs_start_transaction(csum_root, nr_items);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;