From patchwork Sun Feb 21 16:40:24 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Goffredo Baroncelli X-Patchwork-Id: 81042 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1LGeUAc028566 for ; Sun, 21 Feb 2010 16:40:30 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753014Ab0BUQk3 (ORCPT ); Sun, 21 Feb 2010 11:40:29 -0500 Received: from mail-fx0-f213.google.com ([209.85.220.213]:63100 "EHLO mail-fx0-f213.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753009Ab0BUQk2 (ORCPT ); Sun, 21 Feb 2010 11:40:28 -0500 Received: by mail-fx0-f213.google.com with SMTP id 5so1778576fxm.29 for ; Sun, 21 Feb 2010 08:40:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:subject:date :user-agent:mime-version:content-type:content-transfer-encoding :message-id; bh=BjDu0qZzXpsR7PCTDwWDDkN7yEfCqdbfRPZDTd6nFz0=; b=lhPfm/JyK4HAOcrEAZeo4iwhWsvS6IjTwKWqiic+QloVJO6tyUtz7xyGBXLWw4VWis xTiApb7dbFd6HmnKwzmQSVU8uDSXSKdzWT/VDss9pmOnwEnAHnaT/wj4iMvym5R2aey1 jcyttdC85jA6swpeO1iuzqGHe9lamY9HS6aBE= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:subject:date:user-agent:mime-version:content-type :content-transfer-encoding:message-id; b=tBBfGlk1d8Ke4KajojtQEYwN9U3QY8GwU/3G9o1vsTrlr1ZG03Vp8NyZ78InRI/Fin UolMXqfZcQKIS2cwi5aazE8K3Ez4U0oetxDyQBHBzFr3jrwM38/APZ/ISKmgFjEIDIRa QKyUNHLE6SkPM1P8h5dUqUs1UqotcCoP1sc7Y= Received: by 10.223.92.142 with SMTP id r14mr9118957fam.93.1266770427910; Sun, 21 Feb 2010 08:40:27 -0800 (PST) Received: from venice.localnet (host94-173-dynamic.11-87-r.retail.telecomitalia.it [87.11.173.94]) by mx.google.com with ESMTPS id e17sm3298572fke.57.2010.02.21.08.40.26 (version=TLSv1/SSLv3 cipher=RC4-MD5); Sun, 21 Feb 2010 08:40:26 -0800 (PST) From: Goffredo Baroncelli To: linux-btrfs@vger.kernel.org Subject: [PATCH 2/3 V3] btrfs, a new tool to manage a btrfs filesystem - commands Date: Sun, 21 Feb 2010 17:40:24 +0100 User-Agent: KMail/1.13.0 (Linux/2.6.32-10-generic; KDE/4.4.0; x86_64; ; ) MIME-Version: 1.0 Message-Id: <201002211740.25182.kreijack@inwind.it> Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Sun, 21 Feb 2010 16:40:31 +0000 (UTC) diff --git a/btrfs_cmds.c b/btrfs_cmds.c new file mode 100644 index 0000000..e112902 --- /dev/null +++ b/btrfs_cmds.c @@ -0,0 +1,647 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef ULONG_MAX + +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" +#include "ioctl.h" +#include "volumes.h" + +#include "btrfs_cmds.h" + +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +#define BTRFS_IOC_SNAP_CREATE 0 +#define BTRFS_VOL_NAME_MAX 255 +struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; +static inline int ioctl(int fd, int define, void *arg) { return 0; } +#endif + +/* + * test if path is a subvolume: + * this function return + * 0-> path exists but it is not a subvolume + * 1-> path exists and it is a subvolume + * -1 -> path is unaccessible + */ +static int test_issubvolume(char *path) +{ + + struct stat st; + int res; + + res = stat(path, &st); + if(res < 0 ) + return -1; + + return (st.st_ino == 256) && S_ISDIR(st.st_mode); + +} + +/* + * test if path is a directory + * this function return + * 0-> path exists but it is not a directory + * 1-> path exists and it is a directory + * -1 -> path is unaccessible + */ +static int test_isdir(char *path) +{ + struct stat st; + int res; + + res = stat(path, &st); + if(res < 0 ) + return -1; + + return S_ISDIR(st.st_mode); + +} + +static int open_file_or_dir(const char *fname) +{ + int ret; + struct stat st; + DIR *dirstream; + int fd; + + ret = stat(fname, &st); + if (ret < 0) { + return -1; + } + if (S_ISDIR(st.st_mode)) { + dirstream = opendir(fname); + if (!dirstream) { + return -2; + } + fd = dirfd(dirstream); + } else { + fd = open(fname, O_RDWR); + } + if (fd < 0) { + return -3; + } + return fd; +} + +int do_clone(int argc, char **argv) +{ + char *subvol, *dst; + int res, fd, fddst, len; + char *newname; + char *dstdir; + + subvol = argv[0]; + dst = argv[1]; + struct btrfs_ioctl_vol_args args; + + res = test_issubvolume(subvol); + if(res<0){ + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if(!res){ + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + res = test_isdir(dst); + if(res == 0 ){ + fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst); + return 12; + } + + if(res>0){ + newname = strdup(subvol); + newname = basename(newname); + dstdir = dst; + }else{ + newname = strdup(dst); + newname = basename(newname); + dstdir = strdup(dst); + dstdir = dirname(dstdir); + } + + if( !strcmp(newname,".") || !strcmp(newname,"..") || + strchr(newname, '/') ){ + fprintf(stderr, "ERROR: uncorrect snapshot name ('%s')\n", + newname); + return 14; + } + + len = strlen(newname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: snapshot name too long ('%s)\n", + newname); + return 14; + } + + fddst = open_file_or_dir(dstdir); + if (fddst < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); + return 12; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + close(fddst); + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); + return 12; + } + + printf("Create a snapshot of '%s' in '%s/%s'\n", + subvol, dstdir, newname); + args.fd = fd; + strcpy(args.name, newname); + res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args); + + close(fd); + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot snapshot '%s'\n",subvol); + return 11; + } + + return 0; + +} + +int do_delete_subvolume(int argc, char **argv) +{ + int res, fd, len; + struct btrfs_ioctl_vol_args args; + char *dname, *vname, *cpath; + char *path = argv[0]; + + res = test_issubvolume(path); + if(res<0){ + fprintf(stderr, "ERROR: error accessing '%s'\n", path); + return 12; + } + if(!res){ + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path); + return 13; + } + + cpath = realpath(path, 0); + dname = strdup(cpath); + dname = dirname(dname); + vname = strdup(cpath); + vname = basename(vname); + free(cpath); + + if( !strcmp(vname,".") || !strcmp(vname,"..") || + strchr(vname, '/') ){ + fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n", + vname); + return 14; + } + + len = strlen(vname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: snapshot name too long ('%s)\n", + vname); + return 14; + } + + fd = open_file_or_dir(dname); + if (fd < 0) { + close(fd); + fprintf(stderr, "ERROR: can't access to '%s'\n", dname); + return 12; + } + + printf("Delete subvolume '%s/%s'\n", dname, vname); + strcpy(args.name, vname); + res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + + close(fd); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot delete '%s/%s'\n",dname, vname); + return 11; + } + + return 0; + +} + +int do_create_subvol(int argc, char **argv) +{ + int res, fddst, len; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args args; + char *dst = argv[0]; + + res = test_isdir(dst); + if(res >= 0 ){ + fprintf(stderr, "ERROR: '%s' exists\n", dst); + return 12; + } + + newname = strdup(dst); + newname = basename(newname); + dstdir = strdup(dst); + dstdir = dirname(dstdir); + + if( !strcmp(newname,".") || !strcmp(newname,"..") || + strchr(newname, '/') ){ + fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n", + newname); + return 14; + } + + len = strlen(newname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: subvolume name too long ('%s)\n", + newname); + return 14; + } + + fddst = open_file_or_dir(dstdir); + if (fddst < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir); + return 12; + } + + printf("Create subvolume '%s/%s'\n", dstdir, newname); + strcpy(args.name, newname); + res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); + + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot create subvolume\n"); + return 11; + } + + return 0; + +} + +int do_fssync(int argc, char **argv) +{ + int fd, res; + char *path = argv[0]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + printf("FSSync '%s'\n", path); + res = ioctl(fd, BTRFS_IOC_SYNC); + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to fs-syncing '%s'\n", path); + return 16; + } + + return 0; +} + +int do_scan(int nargs, char **argv) +{ + int i, fd; + if(!nargs){ + int ret; + + printf("Scanning for Btrfs filesystems\n"); + ret = btrfs_scan_one_dir("/dev", 1); + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + return 0; + } + + fd = open("/dev/btrfs-control", O_RDWR); + if (fd < 0) { + perror("failed to open /dev/btrfs-control"); + return 10; + } + + for( i = 0 ; i < nargs ; i++ ){ + struct btrfs_ioctl_vol_args args; + int ret; + + printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]); + + strcpy(args.name, argv[i]); + /* + * FIXME: which are the error code returned by this ioctl ? + * it seems that is impossible to undetand if there no is + * a btrfs filesystem from an I/O error !!! + */ + ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); + + if( ret < 0 ){ + close(fd); + fprintf(stderr, "ERROR: unable to scan the device '%s'\n", argv[i]); + return 11; + } + } + + close(fd); + return 0; + +} + +int do_defrag(int argc, char **argv) +{ + + int i, ret=0; + + for(i=0 ; i = BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: size value too long ('%s)\n", + amount); + return 14; + } + + printf("Resize '%s' of '%s'\n", path, amount); + strcpy(args.name, amount); + res = ioctl(fd, BTRFS_IOC_RESIZE, &args); + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to resize '%s'\n", path); + return 30; + } + return 0; +} + +static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) +{ + struct list_head *cur; + struct btrfs_device *device; + + list_for_each(cur, &fs_devices->devices) { + device = list_entry(cur, struct btrfs_device, dev_list); + if ((device->label && strcmp(device->label, search) == 0) || + strcmp(device->name, search) == 0) + return 1; + } + return 0; +} + +static void print_one_uuid(struct btrfs_fs_devices *fs_devices) +{ + char uuidbuf[37]; + struct list_head *cur; + struct btrfs_device *device; + char *super_bytes_used; + u64 devs_found = 0; + u64 total; + + uuid_unparse(fs_devices->fsid, uuidbuf); + device = list_entry(fs_devices->devices.next, struct btrfs_device, + dev_list); + if (device->label && device->label[0]) + printf("Label: '%s' ", device->label); + else + printf("Label: none "); + + super_bytes_used = pretty_sizes(device->super_bytes_used); + + total = device->total_devs; + printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, + (unsigned long long)total, super_bytes_used); + + free(super_bytes_used); + + list_for_each(cur, &fs_devices->devices) { + char *total_bytes; + char *bytes_used; + device = list_entry(cur, struct btrfs_device, dev_list); + total_bytes = pretty_sizes(device->total_bytes); + bytes_used = pretty_sizes(device->bytes_used); + printf("\tdevid %4llu size %s used %s path %s\n", + (unsigned long long)device->devid, + total_bytes, bytes_used, device->name); + free(total_bytes); + free(bytes_used); + devs_found++; + } + if (devs_found < total) { + printf("\t*** Some devices missing\n"); + } + printf("\n"); +} + +int do_show_filesystem(int argc, char **argv) +{ + struct list_head *all_uuids; + struct btrfs_fs_devices *fs_devices; + struct list_head *cur_uuid; + char *search = argv[0]; + int ret; + + ret = btrfs_scan_one_dir("/dev", 0); + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + + all_uuids = btrfs_scanned_uuids(); + list_for_each(cur_uuid, all_uuids) { + fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, + list); + if (search && uuid_search(fs_devices, search) == 0) + continue; + print_one_uuid(fs_devices); + } + printf("%s\n", BTRFS_BUILD_VERSION); + return 0; +} + +int do_add_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0; + + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + return 12; + } + + for(i=0 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args ioctl_args; + int devfd, res; + u64 dev_block_count = 0; + struct stat st; + + devfd = open(args[i], O_RDWR); + if (!devfd) { + fprintf(stderr, "ERROR: Unable to open device '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + ret = fstat(devfd, &st); + if (ret) { + fprintf(stderr, "ERROR: Unable to stat '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + if (!S_ISBLK(st.st_mode)) { + fprintf(stderr, "ERROR: '%s' is not a block device\n", args[i]); + close(devfd); + ret++; + continue; + } + + res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count); + if (ret) { + fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + close(devfd); + + strcpy(ioctl_args.name, args[i]); + res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); + if(res<0){ + fprintf(stderr, "ERROR: error adding the device '%s'\n", args[i]); + ret++; + } + + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; + +} + +int do_balance(int argc, char **argv) +{ + + int fdmnt, ret=0; + char *path = argv[0]; + + fdmnt = open_file_or_dir(path); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + ret = ioctl(fdmnt, BTRFS_IOC_BALANCE); + close(fdmnt); + if(ret<0){ + fprintf(stderr, "ERROR: balancing '%s'\n", path); + + return 19; + } + return 0; +} +int do_remove_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0; + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + return 12; + } + + for(i=0 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args arg; + int res; + + strcpy(arg.name, args[i]); + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); + if(res<0){ + fprintf(stderr, "ERROR: error removing the device '%s'\n", args[i]); + ret++; + } + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; +} \ No newline at end of file diff --git a/btrfs_cmds.h b/btrfs_cmds.h new file mode 100644 index 0000000..d22d753 --- /dev/null +++ b/btrfs_cmds.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/* btrfs_cmds.c*/ +int do_clone(int nargs, char **argv); +int do_delete_subvolume(int nargs, char **argv); +int do_create_subvol(int nargs, char **argv); +int do_fssync(int nargs, char **argv); +int do_defrag(int argc, char **argv); +int do_show_filesystem(int nargs, char **argv); +int do_add_volume(int nargs, char **args); +int do_balance(int nargs, char **argv); +int do_remove_volume(int nargs, char **args); +int do_scan(int nargs, char **argv); +int do_resize(int nargs, char **argv);