struct btrfs_fs_info *fs_info = inode->root->fs_info;
int type;
+ size_t type_len;
+ int level = 0;
+
/* Reset to defaults */
if (len == 0) {
inode->flags &= ~BTRFS_INODE_COMPRESS;
inode->flags &= ~BTRFS_INODE_NOCOMPRESS;
inode->prop_compress = BTRFS_COMPRESS_NONE;
+ inode->prop_compress_level = 0;
return 0;
}
inode->flags |= BTRFS_INODE_NOCOMPRESS;
inode->flags &= ~BTRFS_INODE_COMPRESS;
inode->prop_compress = BTRFS_COMPRESS_NONE;
+ inode->prop_compress_level = 0;
return 0;
}
if (!strncmp("lzo", value, 3)) {
type = BTRFS_COMPRESS_LZO;
+ type_len = 3;
btrfs_set_fs_incompat(fs_info, COMPRESS_LZO);
} else if (!strncmp("zlib", value, 4)) {
type = BTRFS_COMPRESS_ZLIB;
+ type_len = 4;
} else if (!strncmp("zstd", value, 4)) {
type = BTRFS_COMPRESS_ZSTD;
+ type_len = 4;
btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD);
} else {
return -EINVAL;
}
+ /*
+ * Parse an optional ":level" suffix. Old kernels accepted and
+ * silently ignored arbitrary suffixes after the algorithm name, and
+ * such values may still exist on disk, so fall back to the default
+ * level instead of returning an error when the suffix cannot be
+ * parsed, otherwise the inode's properties could not be loaded.
+ */
+ if (len > type_len && value[type_len] == ':') {
+ char str[16];
+ size_t suffix_len = len - type_len;
+
+ if (suffix_len < sizeof(str)) {
+ memcpy(str, value + type_len, suffix_len);
+ str[suffix_len] = 0;
+ if (btrfs_compress_str2level(type, str, &level) < 0)
+ level = 0;
+ }
+ }
+
inode->flags &= ~BTRFS_INODE_NOCOMPRESS;
inode->flags |= BTRFS_INODE_COMPRESS;
inode->prop_compress = type;
+ inode->prop_compress_level = level;
return 0;
}
+/*
+ * Regenerate the canonical "type" or "type:level" value of the
+ * compression property from the inode's cached fields. Used where the
+ * property is rewritten rather than parsed: FS_IOC_SETFLAGS preserving
+ * an existing property, and directory inheritance.
+ */
+ssize_t btrfs_prop_compression_extract(const struct btrfs_inode *inode,
+ char *buf, size_t size)
+{
+ int len;
+
+ switch (inode->prop_compress) {
+ case BTRFS_COMPRESS_ZLIB:
+ case BTRFS_COMPRESS_LZO:
+ case BTRFS_COMPRESS_ZSTD:
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ if (inode->prop_compress_level)
+ len = snprintf(buf, size, "%s:%d",
+ btrfs_compress_type2str(inode->prop_compress),
+ inode->prop_compress_level);
+ else
+ len = snprintf(buf, size, "%s",
+ btrfs_compress_type2str(inode->prop_compress));
+ if (len >= size)
+ return -EOVERFLOW;
+
+ return len;
+}
+
static bool prop_compression_ignore(const struct btrfs_inode *inode)
{
/*
for (i = 0; i < ARRAY_SIZE(prop_handlers); i++) {
const struct prop_handler *h = &prop_handlers[i];
const char *value;
+ char buf[16];
u64 num_bytes = 0;
if (!h->inheritable)
if (!value)
continue;
+ /*
+ * The extract hooks return static strings, which cannot
+ * carry the compression property's optional ":level"
+ * suffix; regenerate the full value for inheritance.
+ */
+ if (h->extract == prop_compression_extract &&
+ btrfs_prop_compression_extract(parent, buf, sizeof(buf)) > 0)
+ value = buf;
+
/*
* This is not strictly necessary as the property should be
* valid, but in case it isn't, don't propagate it further.