Btrfs-progs: introduce btrfs property subgroup

"btrfs filesystem property" is a generic interface to set/get
properties on filesystem objects (inodes/subvolumes/filesystems
/devs).

This patch adds the generic framework for properties and also
implements two properties. The first is the read-only property
for subvolumes and the second is the label property for devices.

Signed-off-by: Alexander Block <ablock84@googlemail.com>
Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
Alexander Block 2013-11-12 13:41:43 +00:00 committed by Chris Mason
parent 83ccf08509
commit 85be2aaf91
6 changed files with 618 additions and 2 deletions

View File

@ -9,11 +9,12 @@ CFLAGS = -g -O1 -fno-strict-aliasing
objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
root-tree.o dir-item.o file-item.o inode-item.o inode-map.o \
extent-cache.o extent_io.o volumes.o utils.o repair.o \
qgroup.o raid6.o free-space-cache.o list_sort.o
qgroup.o raid6.o free-space-cache.o list_sort.o props.o
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
cmds-restore.o cmds-rescue.o chunk-recover.o super-recover.o
cmds-restore.o cmds-rescue.o chunk-recover.o super-recover.o \
cmds-property.o
libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o \
uuid-tree.o
libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \

View File

@ -250,6 +250,7 @@ static const struct cmd_group btrfs_cmd_group = {
{ "rescue", cmd_rescue, NULL, &rescue_cmd_group, 0 },
{ "restore", cmd_restore, cmd_restore_usage, NULL, 0 },
{ "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
{ "property", cmd_property, NULL, &property_cmd_group, 0 },
{ "send", cmd_send, cmd_send_usage, NULL, 0 },
{ "receive", cmd_receive, cmd_receive_usage, NULL, 0 },
{ "quota", cmd_quota, NULL, &quota_cmd_group, 0 },

459
cmds-property.c Normal file
View File

@ -0,0 +1,459 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include "commands.h"
#include "props.h"
#include "ctree.h"
static const char * const property_cmd_group_usage[] = {
"btrfs property get/set/list [-t <type>] <object> [<name>] [value]",
NULL
};
static const char * const cmd_get_usage[] = {
"btrfs property get [-t <type>] <object> [<name>]",
"Gets a property from a btrfs object.",
"If no name is specified, all properties for the given object are",
"printed.",
"A filesystem object can be a the filesystem itself, a subvolume,",
"an inode or a device. The '-t <type>' option can be used to explicitly",
"specify what type of object you meant. This is only needed when a",
"property could be set for more then one object type. Possible types",
"are s[ubvol], f[ilesystem], i[node] and d[evice].",
NULL
};
static const char * const cmd_set_usage[] = {
"btrfs property set [-t <type>] <object> <name> <value>",
"Sets a property on a btrfs object.",
"Please see the help of 'btrfs property get' for a description of",
"objects and object types.",
NULL
};
static const char * const cmd_list_usage[] = {
"btrfs property list [-t <type>] <object>",
"Lists available properties with their descriptions for the given object.",
"Please see the help of 'btrfs property get' for a description of",
"objects and object types.",
NULL
};
static int parse_prop(const char *arg, const struct prop_handler *props,
const struct prop_handler **prop_ret)
{
const struct prop_handler *prop = props;
for (; prop->name; prop++) {
if (!strcmp(prop->name, arg)) {
*prop_ret = prop;
return 0;
}
}
return -1;
}
static int get_fsid(const char *path, u8 *fsid)
{
int ret;
int fd;
struct btrfs_ioctl_fs_info_args args;
fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "ERROR: open %s failed. %s\n", path,
strerror(-ret));
goto out;
}
ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
if (ret < 0) {
ret = -errno;
goto out;
}
memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
ret = 0;
out:
if (fd != -1)
close(fd);
return ret;
}
static int check_btrfs_object(const char *object)
{
int ret;
u8 fsid[BTRFS_FSID_SIZE];
ret = get_fsid(object, fsid);
if (ret < 0)
ret = 0;
else
ret = 1;
return ret;
}
static int check_is_root(const char *object)
{
int ret;
u8 fsid[BTRFS_FSID_SIZE];
u8 fsid2[BTRFS_FSID_SIZE];
char *tmp;
tmp = malloc(strlen(object) + 5);
if (!tmp) {
ret = -ENOMEM;
goto out;
}
strcpy(tmp, object);
if (tmp[strlen(tmp) - 1] != '/')
strcat(tmp, "/");
strcat(tmp, "..");
ret = get_fsid(object, fsid);
if (ret < 0) {
fprintf(stderr, "ERROR: get_fsid for %s failed. %s\n", object,
strerror(-ret));
goto out;
}
ret = get_fsid(tmp, fsid2);
if (ret < 0) {
ret = 0;
goto out;
}
if (!memcmp(fsid, fsid2, BTRFS_FSID_SIZE)) {
ret = 0;
goto out;
}
ret = 1;
out:
free(tmp);
return ret;
}
static int count_bits(int v)
{
unsigned int tmp = (unsigned int)v;
int cnt = 0;
while (tmp) {
if (tmp & 1)
cnt++;
tmp >>= 1;
}
return cnt;
}
static int autodetect_object_types(const char *object, int *types_out)
{
int ret;
int is_btrfs_object;
int types = 0;
struct stat st;
is_btrfs_object = check_btrfs_object(object);
ret = lstat(object, &st);
if (ret < 0) {
ret = -errno;
goto out;
}
if (is_btrfs_object) {
types |= prop_object_inode;
if (st.st_ino == BTRFS_FIRST_FREE_OBJECTID)
types |= prop_object_subvol;
ret = check_is_root(object);
if (ret < 0)
goto out;
if (ret)
types |= prop_object_root;
}
if (S_ISBLK(st.st_mode))
types |= prop_object_dev;
ret = 0;
*types_out = types;
out:
return ret;
}
static int print_prop_help(const struct prop_handler *prop)
{
fprintf(stdout, "%-20s%s\n", prop->name, prop->desc);
return 0;
}
static int dump_prop(const struct prop_handler *prop,
const char *object,
int types,
int type,
int name_and_help)
{
int ret = 0;
if ((types & type) && (prop->types & type)) {
if (!name_and_help)
ret = prop->handler(type, object, prop->name, NULL);
else
ret = print_prop_help(prop);
}
return ret;
}
static int dump_props(int types, const char *object, int name_and_help)
{
int ret;
int i;
int j;
const struct prop_handler *prop;
for (i = 0; prop_handlers[i].name; i++) {
prop = &prop_handlers[i];
for (j = 1; j < __prop_object_max; j <<= 1) {
ret = dump_prop(prop, object, types, j, name_and_help);
if (ret < 0) {
ret = 50;
goto out;
}
}
}
ret = 0;
out:
return ret;
}
static int setget_prop(int types, const char *object,
const char *name, const char *value)
{
int ret;
const struct prop_handler *prop = NULL;
ret = parse_prop(name, prop_handlers, &prop);
if (ret == -1) {
fprintf(stderr, "ERROR: property is unknown\n");
ret = 40;
goto out;
} else if (ret) {
fprintf(stderr, "ERROR: parse_prop reported unknown error\n");
ret = 42;
goto out;
}
types &= prop->types;
if (!types) {
fprintf(stderr,
"ERROR: object is not compatible with property\n");
ret = 47;
goto out;
}
if (count_bits(types) > 1) {
fprintf(stderr,
"ERROR: type of object is ambiguous. Please specify a type by hand.\n");
ret = 48;
goto out;
}
if (value && prop->read_only) {
fprintf(stderr, "ERROR: %s is a read-only property.\n",
prop->name);
ret = 51;
goto out;
}
ret = prop->handler(types, object, name, value);
if (ret < 0)
ret = 50;
else
ret = 0;
out:
return ret;
}
static void parse_args(int argc, char **argv,
const char * const *usage_str,
int *types, char **object,
char **name, char **value)
{
int ret;
char *type_str = NULL;
optind = 1;
while (1) {
int c = getopt(argc, argv, "t:");
if (c < 0)
break;
switch (c) {
case 't':
type_str = optarg;
break;
default:
usage(usage_str);
break;
}
}
*types = 0;
if (type_str) {
if (!strcmp(type_str, "s") || !strcmp(type_str, "subvol")) {
*types = prop_object_subvol;
} else if (!strcmp(type_str, "f") ||
!strcmp(type_str, "filesystem")) {
*types = prop_object_root;
} else if (!strcmp(type_str, "i") ||
!strcmp(type_str, "inode")) {
*types = prop_object_inode;
} else if (!strcmp(type_str, "d") ||
!strcmp(type_str, "device")) {
*types = prop_object_dev;
} else {
fprintf(stderr, "ERROR: invalid object type.\n");
usage(usage_str);
}
}
if (object && optind < argc)
*object = argv[optind++];
if (name && optind < argc)
*name = argv[optind++];
if (value && optind < argc)
*value = argv[optind++];
if (optind != argc) {
fprintf(stderr, "ERROR: invalid arguments.\n");
usage(usage_str);
}
if (!*types && object && *object) {
ret = autodetect_object_types(*object, types);
if (ret < 0) {
fprintf(stderr,
"ERROR: failed to detect object type. %s\n",
strerror(-ret));
usage(usage_str);
}
if (!*types) {
fprintf(stderr,
"ERROR: object is not a btrfs object.\n");
usage(usage_str);
}
}
}
static int cmd_get(int argc, char **argv)
{
int ret;
char *object;
char *name = NULL;
int types = 0;
if (check_argc_min(argc, 2) || check_argc_max(argc, 4))
usage(cmd_get_usage);
parse_args(argc, argv, cmd_get_usage, &types, &object, &name, NULL);
if (!object) {
fprintf(stderr, "ERROR: invalid arguments.\n");
usage(cmd_set_usage);
}
if (name)
ret = setget_prop(types, object, name, NULL);
else
ret = dump_props(types, object, 0);
return ret;
}
static int cmd_set(int argc, char **argv)
{
int ret;
char *object;
char *name;
char *value;
int types = 0;
if (check_argc_min(argc, 4) || check_argc_max(argc, 5))
usage(cmd_set_usage);
parse_args(argc, argv, cmd_set_usage, &types, &object, &name, &value);
if (!object || !name || !value) {
fprintf(stderr, "ERROR: invalid arguments.\n");
usage(cmd_set_usage);
}
ret = setget_prop(types, object, name, value);
return ret;
}
static int cmd_list(int argc, char **argv)
{
int ret;
char *object = NULL;
int types = 0;
if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
usage(cmd_list_usage);
parse_args(argc, argv, cmd_list_usage, &types, &object, NULL, NULL);
if (!object) {
fprintf(stderr, "ERROR: invalid arguments.\n");
usage(cmd_set_usage);
}
ret = dump_props(types, object, 1);
return ret;
}
const struct cmd_group property_cmd_group = {
property_cmd_group_usage, NULL, {
{ "get", cmd_get, cmd_get_usage, NULL, 0 },
{ "set", cmd_set, cmd_set_usage, NULL, 0 },
{ "list", cmd_list, cmd_list_usage, NULL, 0 },
{ 0, 0, 0, 0, 0 },
}
};
int cmd_property(int argc, char **argv)
{
return handle_command_group(&property_cmd_group, argc, argv);
}

View File

@ -87,6 +87,7 @@ extern const struct cmd_group balance_cmd_group;
extern const struct cmd_group device_cmd_group;
extern const struct cmd_group scrub_cmd_group;
extern const struct cmd_group inspect_cmd_group;
extern const struct cmd_group property_cmd_group;
extern const struct cmd_group quota_cmd_group;
extern const struct cmd_group qgroup_cmd_group;
extern const struct cmd_group replace_cmd_group;
@ -109,6 +110,7 @@ int cmd_check(int argc, char **argv);
int cmd_chunk_recover(int argc, char **argv);
int cmd_super_recover(int argc, char **argv);
int cmd_inspect(int argc, char **argv);
int cmd_property(int argc, char **argv);
int cmd_send(int argc, char **argv);
int cmd_receive(int argc, char **argv);
int cmd_quota(int argc, char **argv);

110
props.c Normal file
View File

@ -0,0 +1,110 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include "ctree.h"
#include "commands.h"
#include "utils.h"
#include "props.h"
static int prop_read_only(enum prop_object_type type,
const char *object,
const char *name,
const char *value)
{
int ret = 0;
int fd = -1;
u64 flags = 0;
fd = open(object, O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "ERROR: open %s failed. %s\n",
object, strerror(-ret));
goto out;
}
ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "ERROR: failed to get flags for %s. %s\n",
object, strerror(-ret));
goto out;
}
if (!value) {
if (flags & BTRFS_SUBVOL_RDONLY)
fprintf(stdout, "ro=true\n");
else
fprintf(stdout, "ro=false\n");
ret = 0;
goto out;
}
if (!strcmp(value, "true")) {
flags |= BTRFS_SUBVOL_RDONLY;
} else if (!strcmp(value, "false")) {
flags = flags & ~BTRFS_SUBVOL_RDONLY;
} else {
ret = -EINVAL;
fprintf(stderr, "ERROR: invalid value for property.\n");
goto out;
}
ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "ERROR: failed to set flags for %s. %s\n",
object, strerror(-ret));
goto out;
}
out:
if (fd != -1)
close(fd);
return ret;
}
static int prop_label(enum prop_object_type type,
const char *object,
const char *name,
const char *value)
{
int ret;
if (value) {
ret = set_label((char *) object, (char *) value);
} else {
char label[BTRFS_LABEL_SIZE];
ret = get_label((char *) object, label);
if (!ret)
fprintf(stdout, "label=%s\n", label);
}
return ret;
}
const struct prop_handler prop_handlers[] = {
{"ro", "Set/get read-only flag of subvolume.", 0, prop_object_subvol,
prop_read_only},
{"label", "Set/get label of device.", 0, prop_object_dev, prop_label},
{0, 0, 0, 0, 0}
};

43
props.h Normal file
View File

@ -0,0 +1,43 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#ifndef PROPS_H_
#define PROPS_H_
enum prop_object_type {
prop_object_dev = (1 << 0),
prop_object_root = (1 << 1),
prop_object_subvol = (1 << 2),
prop_object_inode = (1 << 3),
__prop_object_max,
};
typedef int (*prop_handler_t)(enum prop_object_type type,
const char *object,
const char *name,
const char *value);
struct prop_handler {
const char *name;
const char *desc;
int read_only;
int types;
prop_handler_t handler;
};
extern const struct prop_handler prop_handlers[];
#endif /* PROPS_H_ */