diff mbox

IOCTL #21 part two: btrfs progs patch, including iso 8601 timeout support

Message ID AANLkTim0NC5QRs_z=tA8R5R4J07aQ7LS-Ls17t2Qvd7p@mail.gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

David Nicol Oct. 6, 2010, 9 p.m. UTC
None
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 525676e..7442e14 100644
--- a/Makefile
+++ b/Makefile
@@ -37,12 +37,13 @@  all: version $(progs) manpages
 version:
 	bash version.sh
 
-btrfs: $(objects) btrfs.o btrfs_cmds.o
-	gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \
+btrfs: $(objects) btrfs.o btrfs_cmds.o iso8601toms.o
+	gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o iso8601toms.o \
 		$(objects) $(LDFLAGS) $(LIBS)
 
-btrfsctl: $(objects) btrfsctl.o
-	gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS)
+btrfsctl: $(objects) btrfsctl.o iso8601toms.o
+	gcc $(CFLAGS) -o btrfsctl btrfsctl.o iso8601toms.o \
+		$(objects) $(LDFLAGS) $(LIBS)
 
 btrfs-vol: $(objects) btrfs-vol.o
 	gcc $(CFLAGS) -o btrfs-vol btrfs-vol.o $(objects) $(LDFLAGS) $(LIBS)
diff --git a/btrfs-list.c b/btrfs-list.c
index 7741705..7b92bc0 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -16,6 +16,7 @@ 
  * Boston, MA 021110-1307, USA.
  */
 
+#define _GNU_SOURCE
 #ifndef __CHECKER__
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -34,6 +35,7 @@ 
 #include "transaction.h"
 #include "utils.h"
 #include "version.h"
+#include <string.h>
 
 /* we store all the roots we find in an rbtree so that we can
  * search for them later.
diff --git a/btrfs.c b/btrfs.c
index ab5e57f..3928961 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -15,6 +15,7 @@ 
  */
 
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -29,6 +30,7 @@  struct Command {
 	CommandFunction	func;	/* function which implements the command */
 	int	nargs;		/* if == 999, any number of arguments
 				   if >= 0, number of arguments,
+				   if > 1000, 1000 more than the _maximum_ number of arguments,
 				   if < 0, _minimum_ number of arguments */
 	char	*verb;		/* verb */
 	char	*help;		/* help lines; form the 2nd onward they are
@@ -77,6 +79,11 @@  static struct Command commands[] = {
 	  "filesystem sync", "<path>\n"
 		"Force a sync on the filesystem <path>."
 	},
+	{ do_wait4clean, 1002, /* require at most two args */
+	  "filesystem reclaim", "<path> [timeout]\n"
+		"Wait for cleanup of deleted subvolumes in the filesystem <path>.\n"
+		"Optional timeout in whole or partial seconds, or ISO8601 string.\n"
+	},
 	{ do_resize, 2,
 	  "filesystem resize", "[+/-]<newsize>[gkm]|max <filesystem>\n"
 		"Resize the file system. If 'max' is passed, the filesystem\n"
@@ -349,17 +356,26 @@  static int parse_args(int argc, char **argv,
 		return -2;
 
 	/* check the number of argument */
-	if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){
+
+	if(matchcmd->nargs > 1000 ){
+          if ( matchcmd->nargs < (1000 + *nargs_)){
+		fprintf(stderr, "ERROR: '%s' requires only %d or fewer arg(s)\n",
+			matchcmd->verb, matchcmd->nargs - 1000 );
+			return -2;
+          }
+	} else {
+
+	  if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){
 		fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n",
 			matchcmd->verb, -matchcmd->nargs);
 			return -2;
-	}
-	if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){
+	  }
+	  if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){
 		fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n",
 			matchcmd->verb, matchcmd->nargs);
 			return -2;
-	}
-	
+	  }
+	} 
         if (prepare_args( nargs_, args_, prgname, matchcmd )){
                 fprintf(stderr, "ERROR: not enough memory\\n");
 		return -20;
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index 8031c58..855c950 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -526,6 +526,37 @@  int do_fssync(int argc, char **argv)
 	return 0;
 }
 
+#include "iso8601toms.h"
+
+int do_wait4clean(int argc, char **argv)
+{
+	int fd, res;
+        struct btrfs_ioctl_cleaner_wait_args w4c_arg;
+
+	char	*path = ".";
+        w4c_arg.ms = 0UL;
+
+        if (argc > 1) 
+	     path = argv[1];
+        if (argc > 2) 
+             w4c_arg.ms = iso8601toms(argv[2]);
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't open file or dir '%s'\n", path);
+		return 12;
+	}
+	res = ioctl(fd, BTRFS_IOC_CLEANER_WAIT, &w4c_arg);
+	close(fd);
+	if( res != 0 ){
+		fprintf(stderr, "%s:error #%i:%s\n",
+                        path, res,strerror(res));
+		return -res;
+	}
+
+	return 0;
+}
+
 int do_scan(int argc, char **argv)
 {
 	int	i, fd;
diff --git a/btrfs_cmds.h b/btrfs_cmds.h
index 7bde191..c958338 100644
--- a/btrfs_cmds.h
+++ b/btrfs_cmds.h
@@ -19,6 +19,7 @@  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_wait4clean(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);
diff --git a/btrfsctl.c b/btrfsctl.c
index be6bf25..307ec53 100644
--- a/btrfsctl.c
+++ b/btrfsctl.c
@@ -34,6 +34,7 @@ 
 #include "ctree.h"
 #include "transaction.h"
 #include "utils.h"
+#include "iso8601toms.h"
 #include "version.h"
 
 #ifdef __CHECKER__
@@ -57,6 +58,7 @@  static void print_usage(void)
 	printf("\t-a: scans all devices for Btrfs filesystems\n");
 	printf("\t-c: forces a single FS sync\n");
 	printf("\t-D: delete snapshot\n");
+	printf("\t-C [timeout] [directory]: wait for snapshot space recovery\n");
 	printf("\t-m [tree id] directory: set the default mounted subvolume"
 	       " to the [tree id] or the directory\n");
 	printf("%s\n", BTRFS_BUILD_VERSION);
@@ -96,7 +98,7 @@  int main(int ac, char **av)
 	char *fname = NULL;
 	char *snap_location = NULL;
 	int snap_fd = 0;
-	int fd;
+	int fd = -999; /* silence a warning */
 	int ret;
 	struct btrfs_ioctl_vol_args args;
 	char *name = NULL;
@@ -105,6 +107,7 @@  int main(int ac, char **av)
 	int len;
 	char *fullpath;
 	u64 objectid = 0;
+        struct btrfs_ioctl_cleaner_wait_args CWargs;
 
 	if (ac == 2 && strcmp(av[1], "-a") == 0) {
 		fprintf(stderr, "Scanning for Btrfs filesystems\n");
@@ -175,6 +178,10 @@  int main(int ac, char **av)
 				fprintf(stderr, "-D size too long\n");
 				exit(1);
 			}
+		} else if (strcmp(av[i], "-C") == 0) {
+			command = BTRFS_IOC_CLEANER_WAIT;
+                        CWargs.ms = ( ac > (i+1) ? iso8601toms(av[i+1]) : 0UL );
+			name = ( ac > ( i+2 ) ? av[i + 2] : "." );
 		} else if (strcmp(av[i], "-A") == 0) {
 			if (i >= ac - 1) {
 				fprintf(stderr, "-A requires an arg\n");
@@ -221,9 +228,9 @@  int main(int ac, char **av)
 			exit(1);
 		}
 		name = fname;
-	 } else {
+	} else if (command != BTRFS_IOC_CLEANER_WAIT) {
 		fd = open_file_or_dir(fname);
-	 }
+	}
 
 	if (name)
 		strcpy(args.name, name);
@@ -236,6 +243,9 @@  int main(int ac, char **av)
 	} else if (command == BTRFS_IOC_DEFAULT_SUBVOL) {
 		printf("objectid is %llu\n", objectid);
 		ret = ioctl(fd, command, &objectid);
+	} else if (command == BTRFS_IOC_CLEANER_WAIT) {
+		fd = open_file_or_dir(name);
+		ret = ioctl(fd, command, &CWargs);
 	} else
 		ret = ioctl(fd, command, &args);
 	if (ret < 0) {
diff --git a/ioctl.h b/ioctl.h
index 776d7a9..27df596 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -169,4 +169,11 @@  struct btrfs_ioctl_space_args {
 #define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, u64)
 #define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
 				    struct btrfs_ioctl_space_args)
+struct btrfs_ioctl_cleaner_wait_args{
+        unsigned long ms;
+        unsigned long flags; /* for future use */
+};
+#define BTRFS_IOC_CLEANER_WAIT _IOW(BTRFS_IOCTL_MAGIC, 21, \
+				    struct btrfs_ioctl_cleaner_wait_args)
 #endif
+
diff --git a/iso8601toms.c b/iso8601toms.c
new file mode 100644
index 0000000..a1ee9bd
--- /dev/null
+++ b/iso8601toms.c
@@ -0,0 +1,109 @@ 
+/*
+ * 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.
+ */
+
+/***********
+
+ the following will correctly parse valid duration strings,
+ also it will accept a lot of invalid ones. 
+
+ it does:
+
+    know all the ISO8601 letters
+
+    accept a non-integer as the last numeric component
+
+ it silently accepts:
+
+    out of order duration type letters
+
+    strings missing the leading P character
+
+    lowercase duration type letters
+
+    non-integers in any position
+
+ it warns on:
+
+    P or p appearing somewhere besides the beginning of the string
+
+    unrecognized characters
+
+
+***********/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+unsigned long iso8601toms(char *P){
+    unsigned long ms;
+    double component;
+    char *ptr;
+    char *endptr;
+    short M_min = 0;
+
+    ms = 0UL;
+    ptr = P;
+    for(;;){
+       component=strtod(ptr, &endptr);
+       switch (*endptr)
+       {
+          case 'P': /* anchor */ case 'p':
+             if (ptr > P)
+	         fprintf(stderr, "ignoring non-initial P "
+                         "in ISO8601 duration string %s\n", P);
+             break;
+          case 'Y': /* years */ case 'y':
+             component *= 12;
+          BIGM:
+             /* average days in a gregorian month */
+             component *= (365.2425 / 12.0);
+             /* ms in a day */
+             component *= ( 24 * 3600 * 1000 );
+             ms += component;
+             break;
+          case 'T': /* Time (not date) anchor */ case 't':
+             M_min = 1;
+             break;
+          case 'W': /* week */ case 'w':
+             component *= 7;
+          case 'D': /* day */ case 'd':
+             component *=  24 ;
+          case 'H': /* hour */ case 'h':
+             component *= 60;
+             M_min = 1;
+          case 'M': /* month, or minute */ case 'm':
+             if (!M_min++)
+                 goto BIGM;
+             component *= 60;
+          case 'S': /* second */ case 's':
+          case '\0': /* default to second */
+             component *= 1000;
+             ms += component;
+             break;
+          
+          default:
+	     fprintf(stderr,
+                "ignoring unexpected char [%c] "
+                "in iso8601 duration string %s\n",
+                *endptr, P
+             );
+       };
+       if (!*endptr)
+          return (ms); 
+       ptr = 1+endptr;
+    };
+}
+
diff --git a/iso8601toms.h b/iso8601toms.h
new file mode 100644
index 0000000..8f7ca83
--- /dev/null
+++ b/iso8601toms.h
@@ -0,0 +1,3 @@ 
+
+unsigned long iso8601toms(char *P);
+
diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index 26ef982..5f1155e 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -19,6 +19,8 @@  btrfs \- control a btrfs filesystem
 .PP
 \fBbtrfs\fP \fBfilesystem sync\fP\fI <path> \fP
 .PP
+\fBbtrfs\fP \fBfilesystem reclaim\fP\fI <path> [timeout] \fP
+.PP
 \fBbtrfs\fP \fBfilesystem resize\fP\fI [+/\-]<size>[gkm]|max <filesystem>\fP
 .PP
 \fBbtrfs\fP \fBdevice scan\fP\fI [<device> [<device>..]]\fP
@@ -115,6 +117,15 @@  all the block devices.
 Force a sync for the filesystem identified by \fI<path>\fR.
 .TP
 
+\fBfilesystem reclaim\fR\fI <path> [timeout] \fR
+Wait for recovery of space from deleted
+subvolumes on the filesystem containing \fI<path>\fR to complete.
+The optional timeout is in seconds, which can be fractional, or you may use
+an ISO8601 duration string, such as PT5M2.5S, which would mean five minutes,
+two and a half seconds. The P and T are optional, but without them "M" might
+get interpreted as months, which is probably not what you want.
+.TP
+
 .\"
 .\" Some wording are extracted by the resize2fs man page
 .\"
diff --git a/man/btrfsctl.8.in b/man/btrfsctl.8.in
index c2d4488..62a1db2 100644
--- a/man/btrfsctl.8.in
+++ b/man/btrfsctl.8.in
@@ -10,6 +10,7 @@  btrfsctl \- control a btrfs filesystem
 [ \fB \-A\fP\fI device\fP ]
 [ \fB \-a\fP ]
 [ \fB \-c\fP ]
+[ \fB \-C\fP\fI [timeout [directory]]\fP ]
 .SH DESCRIPTION
 .B btrfsctl
 is used to control the filesystem and the files and directories stored. It is the tool to create a new snapshot for the filesystem.
@@ -35,6 +36,13 @@  Scans all devices present in the system for btrfs filesystem.
 .TP
 \fB\-c\fR
 Forces a filesystem sync.
+.TP
+\fB\-C\fR \fI[timeout] [directory]\fR
+Wait until all the space from all deleted snapshots has been recovered.
+The optional timeout parameter is in seconds, or an ISO8601 string,
+defaulting to zero, which means do not time out. Fractional seconds
+resolve to milliseconds. The optional directory parameter defaults to
+the current directory.
 .SH AVAILABILITY
 .B btrfsctl
 is part of btrfs-progs. Btrfs is currently under heavy development,