@@ -17,7 +17,7 @@ bindir = $(prefix)/bin
LIBS=-luuid
progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
- btrfs-map-logical
+ btrfs-map-logical btrfslabel
# make C=1 to enable sparse
ifdef C
@@ -48,6 +48,9 @@ btrfs-show: $(objects) btrfs-show.o
btrfsck: $(objects) btrfsck.o
gcc $(CFLAGS) -o btrfsck btrfsck.o $(objects) $(LDFLAGS) $(LIBS)
+btrfslabel: $(objects) btrfslabel.o
+ gcc $(CFLAGS) -o btrfslabel btrfslabel.o $(objects) $(LDFLAGS) $(LIBS)
+
mkfs.btrfs: $(objects) mkfs.o
gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o $(LDFLAGS) $(LIBS)
new file mode 100644
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2008 Morey Roof. All rights reserved.
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#ifndef __CHECKER__
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include "ioctl.h"
+#endif /* __CHECKER__ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <ctype.h>
+#include "kerncompat.h"
+#include "ctree.h"
+#include "utils.h"
+#include "version.h"
+#include "disk-io.h"
+#include "transaction.h"
+
+#define MOUNTED 1
+#define UNMOUNTED 2
+#define GET_LABEL 3
+#define SET_LABEL 4
+
+#ifdef __CHECKER__
+#define BTRFS_IOC_SET_LABEL 0
+#define BTRFS_IOC_GET_LABEL 0
+struct btrfs_ioctl_label_args { char name[BTRFS_LABEL_SIZE]; };
+static inline int ioctl(int fd, int define, void *arg) { return 0; }
+#endif /*__CHECKER__*/
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: btrfslabel dev [newlabel]\n");
+ fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
+ exit(1);
+}
+
+static void change_label_unmounted(char *dev, char *nLabel)
+{
+ struct btrfs_root *root;
+ struct btrfs_trans_handle *trans;
+
+ /* Open the super_block at the default location
+ * and as read-write.
+ */
+ root = open_ctree(dev, 0, 1);
+
+ trans = btrfs_start_transaction(root, 1);
+ strncpy(root->fs_info->super_copy.label, nLabel, BTRFS_LABEL_SIZE);
+ btrfs_commit_transaction(trans, root);
+
+ /* Now we close it since we are done. */
+ close_ctree(root);
+}
+
+static void get_label_unmounted(char *dev)
+{
+ struct btrfs_root *root;
+
+ /* Open the super_block at the default location
+ * and as read-only.
+ */
+ root = open_ctree(dev, 0, 0);
+
+ fprintf(stdout, "%s\n", root->fs_info->super_copy.label);
+
+ /* Now we close it since we are done. */
+ close_ctree(root);
+}
+
+static void mount_label_op(char *dev, char *nLabel, int cmd)
+{
+ struct btrfs_ioctl_label_args label_args;
+ int ret = 0;
+ DIR *dirstream;
+ int fd;
+ char *mount_point;
+
+ mount_point = malloc(PATH_MAX);
+ if (mount_point == NULL)
+ {
+ fprintf(stderr, "FATAL: Unable to allocate memory\n");
+ exit(1);
+ }
+
+ if (get_mountpt(dev, mount_point, PATH_MAX) != 0)
+ {
+ fprintf(stderr, "FATAL: Unable to determine mount point\n");
+ exit(1);
+ }
+
+ dirstream = opendir(mount_point);
+ if (!dirstream)
+ {
+ fprintf(stderr, "FATAL: Unable to access mount point %s\n", mount_point);
+ exit(1);
+ }
+
+ fd = dirfd(dirstream);
+ if (!fd)
+ {
+ fprintf(stderr, "FATAL: Unable to access btrfs on %s\n", dev);
+ exit(1);
+ }
+
+ switch(cmd)
+ {
+ case GET_LABEL:
+ ret = ioctl(fd, BTRFS_IOC_GET_LABEL, &label_args);
+ if (ret == 0)
+ {
+ fprintf(stdout, "%s\n", label_args.name);
+ } else
+ {
+ fprintf(stderr, "FATAL: Unable to get label for %s\n", dev);
+ exit(1);
+ }
+ break;
+ case SET_LABEL:
+ strncpy(label_args.name, nLabel, BTRFS_LABEL_SIZE);
+
+ ret = ioctl(fd, BTRFS_IOC_SET_LABEL, &label_args);
+ if (ret != 0)
+ {
+ fprintf(stderr, "FATAL: Unable to set label for %s\n", dev);
+ exit(1);
+ }
+ break;
+ default:
+ fprintf(stderr, "FATAL: Unknown mounted label operation\n");
+ exit(1);
+ break;
+ }
+
+ /* Release the memory we used */
+ free(mount_point);
+}
+
+int main(int argc, char **argv)
+{
+ char *btrfs_dev;
+ char *nLabel;
+ int ret = 0;
+ int workas = 0;
+ int check_val = 0;
+
+ if (argc <= 1)
+ {
+ print_usage();
+ }
+
+ btrfs_dev = argv[1];
+ ret = check_mounted(btrfs_dev);
+ if (ret < 0)
+ {
+ fprintf(stderr, "FATAL: error checking %s mount status\n", btrfs_dev);
+ exit(1);
+ }
+
+ switch(ret)
+ {
+ case 0:
+ workas = UNMOUNTED;
+ break;
+ case 1:
+ workas = MOUNTED;
+ break;
+ default:
+ fprintf(stderr, "BUG: unknown return code from check_mounted()\n");
+ exit(1);
+ break;
+ }
+
+ if (argc == 2)
+ {
+ /* They just want to know what the current label is */
+ if (workas == MOUNTED)
+ {
+ mount_label_op(btrfs_dev, NULL, GET_LABEL);
+ } else
+ {
+ /* Get the label from an unmouted filesystem */
+ get_label_unmounted(btrfs_dev);
+ }
+ } else if (argc == 3)
+ {
+ /* They have requested we change the label */
+ nLabel = argv[2];
+ check_val = check_label(nLabel);
+
+ if (check_val == -1)
+ {
+ fprintf(stderr, "Label %s is too long (max %d)\n", nLabel,
+ BTRFS_LABEL_SIZE);
+ exit(1);
+ }
+
+ if (check_val == -2)
+ {
+ fprintf(stderr, "invalid label %s\n", nLabel);
+ exit(1);
+ }
+
+ if (workas == MOUNTED)
+ {
+ mount_label_op(btrfs_dev, nLabel, SET_LABEL);
+ } else
+ {
+ /* We are going to change the label on an unmounted filesystem */
+ change_label_unmounted(btrfs_dev, nLabel);
+ }
+ } else
+ {
+ fprintf(stderr, "FATAL: too many arguements\n\n");
+ print_usage();
+ }
+
+ return ret;
+}
@@ -20,6 +20,7 @@
#define __IOCTL_
#include <asm/types.h>
#include <linux/ioctl.h>
+#include "ctree.h"
#define BTRFS_IOCTL_MAGIC 0x94
#define BTRFS_VOL_NAME_MAX 255
@@ -30,6 +31,10 @@ struct btrfs_ioctl_vol_args {
char name[BTRFS_PATH_NAME_MAX + 1];
};
+struct btrfs_ioctl_label_args {
+ char name[BTRFS_LABEL_SIZE + 1];
+};
+
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -59,4 +64,11 @@ struct btrfs_ioctl_vol_args {
#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
struct btrfs_ioctl_vol_args)
+
+/* Used to set and get the current volume label */
+#define BTRFS_IOC_SET_LABEL _IOW(BTRFS_IOCTL_MAGIC, 16, \
+ struct btrfs_ioctl_label_args)
+#define BTRFS_IOC_GET_LABEL _IOW(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_label_args)
+
#endif
@@ -587,7 +587,7 @@ error:
}
/*
- * returns 1 if the device was mounted, < 0 on error or 0 if everything
+ * returns 1 if the device is mounted, < 0 on error or 0 if everything
* is safe to continue. TODO, this should also scan multi-device filesystems
*/
int check_mounted(char *file)
@@ -638,6 +638,39 @@ int check_mounted(char *file)
return ret;
}
+/* Gets the mount point of btrfs filesystem that is using the specified device.
+ * Returns 0 is everything is good, <0 if we have an error.
+ * TODO: Fix this fucntion and check_mounted to work with multiple drive BTRFS
+ * setups.
+ */
+int get_mountpt(char *dev, char *mntpt, size_t size)
+{
+ struct mntent *mnt;
+ FILE *f;
+ int ret = 0;
+
+ f = setmntent("/proc/mounts", "r");
+ if (f == NULL)
+ return -errno;
+
+ while ((mnt = getmntent(f)) != NULL )
+ {
+ if (strcmp(dev, mnt->mnt_fsname) == 0)
+ {
+ strncpy(mntpt, mnt->mnt_dir, size);
+ break;
+ }
+ }
+
+ if (mnt == NULL)
+ {
+ /* We didn't find an entry so lets report an error */
+ ret = -1;
+ }
+
+ return ret;
+}
+
struct pending_dir {
struct list_head list;
char name[256];
@@ -820,3 +853,27 @@ char *pretty_sizes(u64 size)
return pretty;
}
+/*
+ * 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
+ -2 if the label contains an invalid character
+ */
+int check_label(char *input)
+{
+ int i;
+ int len = strlen(input);
+
+ if (len > BTRFS_LABEL_SIZE) {
+ return -1;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (input[i] == '/' || input[i] == '\\') {
+ return -2;
+ }
+ }
+
+ return 0;
+}
@@ -40,4 +40,6 @@ int check_mounted(char *devicename);
int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
int super_offset);
char *pretty_sizes(u64 size);
+int check_label(char *input);
+int get_mountpt(char *dev, char *mntpt, size_t size);
#endif