diff --git a/Makefile b/Makefile index fe29a152..31d26907 100644 --- a/Makefile +++ b/Makefile @@ -174,6 +174,7 @@ objects = \ common/device-scan.o \ common/device-utils.o \ common/extent-cache.o \ + common/filesystem-utils.o \ common/format-output.o \ common/fsfeatures.o \ common/help.o \ diff --git a/cmds/filesystem.c b/cmds/filesystem.c index 65ae26ee..e813878b 100644 --- a/cmds/filesystem.c +++ b/cmds/filesystem.c @@ -43,6 +43,7 @@ #include "common/device-utils.h" #include "common/open-utils.h" #include "common/parse-utils.h" +#include "common/filesystem-utils.h" #include "cmds/commands.h" #include "cmds/filesystem-usage.h" diff --git a/cmds/property.c b/cmds/property.c index 8bd88cdf..cb83a781 100644 --- a/cmds/property.c +++ b/cmds/property.c @@ -28,6 +28,7 @@ #include "common/open-utils.h" #include "common/utils.h" #include "common/help.h" +#include "common/filesystem-utils.h" #include "cmds/commands.h" #include "cmds/props.h" diff --git a/common/filesystem-utils.c b/common/filesystem-utils.c new file mode 100644 index 00000000..f92d84af --- /dev/null +++ b/common/filesystem-utils.c @@ -0,0 +1,217 @@ +/* + * 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 "kerncompat.h" +#include +#include +#include +#include "kernel-shared/ctree.h" +#include "kernel-shared/disk-io.h" +#include "kernel-shared/transaction.h" +#include "common/filesystem-utils.h" +#include "common/messages.h" +#include "common/open-utils.h" +#include "common/path-utils.h" +#include "ioctl.h" + +/* + * For a given: + * - file or directory return the containing tree root id + * - subvolume return its own tree id + * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is + * undefined and function returns -1 + */ +int lookup_path_rootid(int fd, u64 *rootid) +{ + struct btrfs_ioctl_ino_lookup_args args; + int ret; + + memset(&args, 0, sizeof(args)); + args.treeid = 0; + args.objectid = BTRFS_FIRST_FREE_OBJECTID; + + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); + if (ret < 0) + return -errno; + + *rootid = args.treeid; + + return 0; +} + +/* + * Checks to make sure that the label matches our requirements. + * Returns: + 0 if everything is safe and usable + -1 if the label is too long + */ +static int check_label(const char *input) +{ + int len = strlen(input); + + if (len > BTRFS_LABEL_SIZE - 1) { + error("label %s is too long (max %d)", input, + BTRFS_LABEL_SIZE - 1); + return -1; + } + + return 0; +} + +static int set_label_unmounted(const char *dev, const char *label) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *root; + int ret; + + ret = check_mounted(dev); + if (ret < 0) { + error("checking mount status of %s failed: %d", dev, ret); + return -1; + } + if (ret > 0) { + error("device %s is mounted, use mount point", dev); + return -1; + } + + /* Open the super_block at the default location + * and as read-write. + */ + root = open_ctree(dev, 0, OPEN_CTREE_WRITES); + if (!root) /* errors are printed by open_ctree() */ + return -1; + + trans = btrfs_start_transaction(root, 1); + BUG_ON(IS_ERR(trans)); + __strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1); + + btrfs_commit_transaction(trans, root); + + /* Now we close it since we are done. */ + close_ctree(root); + return 0; +} + +static int set_label_mounted(const char *mount_path, const char *labelp) +{ + int fd; + char label[BTRFS_LABEL_SIZE]; + + fd = open(mount_path, O_RDONLY | O_NOATIME); + if (fd < 0) { + error("unable to access %s: %m", mount_path); + return -1; + } + + memset(label, 0, sizeof(label)); + __strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1); + if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) { + error("unable to set label of %s: %m", mount_path); + close(fd); + return -1; + } + + close(fd); + return 0; +} + +int get_label_unmounted(const char *dev, char *label) +{ + struct btrfs_root *root; + int ret; + + ret = check_mounted(dev); + if (ret < 0) { + error("checking mount status of %s failed: %d", dev, ret); + return -1; + } + + /* Open the super_block at the default location + * and as read-only. + */ + root = open_ctree(dev, 0, 0); + if(!root) + return -1; + + __strncpy_null(label, root->fs_info->super_copy->label, + BTRFS_LABEL_SIZE - 1); + + /* Now we close it since we are done. */ + close_ctree(root); + return 0; +} + +/* + * If a partition is mounted, try to get the filesystem label via its + * mounted path rather than device. Return the corresponding error + * the user specified the device path. + */ +int get_label_mounted(const char *mount_path, char *labelp) +{ + char label[BTRFS_LABEL_SIZE]; + int fd; + int ret; + + fd = open(mount_path, O_RDONLY | O_NOATIME); + if (fd < 0) { + error("unable to access %s: %m", mount_path); + return -1; + } + + memset(label, '\0', sizeof(label)); + ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label); + if (ret < 0) { + if (errno != ENOTTY) + error("unable to get label of %s: %m", mount_path); + ret = -errno; + close(fd); + return ret; + } + + __strncpy_null(labelp, label, BTRFS_LABEL_SIZE - 1); + close(fd); + return 0; +} + +int get_label(const char *btrfs_dev, char *label) +{ + int ret; + + ret = path_is_reg_or_block_device(btrfs_dev); + if (!ret) + ret = get_label_mounted(btrfs_dev, label); + else if (ret > 0) + ret = get_label_unmounted(btrfs_dev, label); + + return ret; +} + +int set_label(const char *btrfs_dev, const char *label) +{ + int ret; + + if (check_label(label)) + return -1; + + ret = path_is_reg_or_block_device(btrfs_dev); + if (!ret) + ret = set_label_mounted(btrfs_dev, label); + else if (ret > 0) + ret = set_label_unmounted(btrfs_dev, label); + + return ret; +} + diff --git a/common/filesystem-utils.h b/common/filesystem-utils.h new file mode 100644 index 00000000..a8d42593 --- /dev/null +++ b/common/filesystem-utils.h @@ -0,0 +1,26 @@ +/* + * 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 __BTRFS_FILESYSTEM_UTILS_H__ +#define __BTRFS_FILESYSTEM_UTILS_H__ + +int lookup_path_rootid(int fd, u64 *rootid); +int get_label(const char *btrfs_dev, char *label); +int set_label(const char *btrfs_dev, const char *label); +int get_label_mounted(const char *mount_path, char *labelp); +int get_label_unmounted(const char *dev, char *label); + +#endif diff --git a/common/utils.c b/common/utils.c index 61aae42a..ae56050e 100644 --- a/common/utils.c +++ b/common/utils.c @@ -63,169 +63,6 @@ struct pending_dir { char name[PATH_MAX]; }; -/* - * Checks to make sure that the label matches our requirements. - * Returns: - 0 if everything is safe and usable - -1 if the label is too long - */ -static int check_label(const char *input) -{ - int len = strlen(input); - - if (len > BTRFS_LABEL_SIZE - 1) { - error("label %s is too long (max %d)", input, - BTRFS_LABEL_SIZE - 1); - return -1; - } - - return 0; -} - -static int set_label_unmounted(const char *dev, const char *label) -{ - struct btrfs_trans_handle *trans; - struct btrfs_root *root; - int ret; - - ret = check_mounted(dev); - if (ret < 0) { - error("checking mount status of %s failed: %d", dev, ret); - return -1; - } - if (ret > 0) { - error("device %s is mounted, use mount point", dev); - return -1; - } - - /* Open the super_block at the default location - * and as read-write. - */ - root = open_ctree(dev, 0, OPEN_CTREE_WRITES); - if (!root) /* errors are printed by open_ctree() */ - return -1; - - trans = btrfs_start_transaction(root, 1); - BUG_ON(IS_ERR(trans)); - __strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1); - - btrfs_commit_transaction(trans, root); - - /* Now we close it since we are done. */ - close_ctree(root); - return 0; -} - -static int set_label_mounted(const char *mount_path, const char *labelp) -{ - int fd; - char label[BTRFS_LABEL_SIZE]; - - fd = open(mount_path, O_RDONLY | O_NOATIME); - if (fd < 0) { - error("unable to access %s: %m", mount_path); - return -1; - } - - memset(label, 0, sizeof(label)); - __strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1); - if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) { - error("unable to set label of %s: %m", mount_path); - close(fd); - return -1; - } - - close(fd); - return 0; -} - -int get_label_unmounted(const char *dev, char *label) -{ - struct btrfs_root *root; - int ret; - - ret = check_mounted(dev); - if (ret < 0) { - error("checking mount status of %s failed: %d", dev, ret); - return -1; - } - - /* Open the super_block at the default location - * and as read-only. - */ - root = open_ctree(dev, 0, 0); - if(!root) - return -1; - - __strncpy_null(label, root->fs_info->super_copy->label, - BTRFS_LABEL_SIZE - 1); - - /* Now we close it since we are done. */ - close_ctree(root); - return 0; -} - -/* - * If a partition is mounted, try to get the filesystem label via its - * mounted path rather than device. Return the corresponding error - * the user specified the device path. - */ -int get_label_mounted(const char *mount_path, char *labelp) -{ - char label[BTRFS_LABEL_SIZE]; - int fd; - int ret; - - fd = open(mount_path, O_RDONLY | O_NOATIME); - if (fd < 0) { - error("unable to access %s: %m", mount_path); - return -1; - } - - memset(label, '\0', sizeof(label)); - ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label); - if (ret < 0) { - if (errno != ENOTTY) - error("unable to get label of %s: %m", mount_path); - ret = -errno; - close(fd); - return ret; - } - - __strncpy_null(labelp, label, BTRFS_LABEL_SIZE - 1); - close(fd); - return 0; -} - -int get_label(const char *btrfs_dev, char *label) -{ - int ret; - - ret = path_is_reg_or_block_device(btrfs_dev); - if (!ret) - ret = get_label_mounted(btrfs_dev, label); - else if (ret > 0) - ret = get_label_unmounted(btrfs_dev, label); - - return ret; -} - -int set_label(const char *btrfs_dev, const char *label) -{ - int ret; - - if (check_label(label)) - return -1; - - ret = path_is_reg_or_block_device(btrfs_dev); - if (!ret) - ret = set_label_mounted(btrfs_dev, label); - else if (ret > 0) - ret = set_label_unmounted(btrfs_dev, label); - - return ret; -} - void btrfs_format_csum(u16 csum_type, const u8 *data, char *output) { int i; @@ -1540,28 +1377,3 @@ out: return ret; } - -/* - * For a given: - * - file or directory return the containing tree root id - * - subvolume return its own tree id - * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is - * undefined and function returns -1 - */ -int lookup_path_rootid(int fd, u64 *rootid) -{ - struct btrfs_ioctl_ino_lookup_args args; - int ret; - - memset(&args, 0, sizeof(args)); - args.treeid = 0; - args.objectid = BTRFS_FIRST_FREE_OBJECTID; - - ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); - if (ret < 0) - return -errno; - - *rootid = args.treeid; - - return 0; -} diff --git a/common/utils.h b/common/utils.h index b3aea756..a3118e63 100644 --- a/common/utils.h +++ b/common/utils.h @@ -53,12 +53,7 @@ int get_fs_exclop(int fd); int check_running_fs_exclop(int fd, enum exclusive_operation start, bool enqueue); const char *get_fs_exclop_name(int op); -int get_label(const char *btrfs_dev, char *label); -int set_label(const char *btrfs_dev, const char *label); - int check_arg_type(const char *input); -int get_label_mounted(const char *mount_path, char *labelp); -int get_label_unmounted(const char *dev, char *label); int ask_user(const char *question); int lookup_path_rootid(int fd, u64 *rootid); int find_mount_fsroot(const char *subvol, const char *subvolid, char **mount);