]> git.neil.brown.name Git - lafs-utils.git/blob - tools/lafs.c
0c7360cd574dbe775f8392cc922ad75af2844238
[lafs-utils.git] / tools / lafs.c
1 /*
2  * lafs - Examine and manipulate and LaFS image
3  *
4  *  This program is essentially a front-end to liblafs.  It allows
5  *  a LaFS filesystem to be examined and modified.  All interesting
6  *  manipulations are function calls in to liblafs.
7  *  The program simply parses textual commands converting them into
8  *  library calls, and provides a readline interface with autocompletion
9  *  and context-sensitive help.
10  *
11  * Copyright (C) 2011 NeilBrown <neil@brown.name>
12  *
13  *    This program is free software; you can redistribute it and/or modify
14  *    it under the terms of the GNU General Public License as published by
15  *    the Free Software Foundation; either version 2 of the License, or
16  *    (at your option) any later version.
17  *
18  *    This program is distributed in the hope that it will be useful,
19  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *    GNU General Public License for more details.
22  *
23  *    You should have received a copy of the GNU General Public License
24  *    along with this program; if not, write to the Free Software
25  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  *
27  *    Author: Neil Brown
28  *    Email: <neil@brown.name>
29  *
30  */
31
32 #define _GNU_SOURCE
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <getopt.h>
38 #include <stdio.h>
39 #include <readline/readline.h>
40 #include <readline/history.h>
41 #include <lafs/lafs.h>
42 #include <talloc.h>
43 #include <uuid/uuid.h>
44 #include "internal.h"
45
46 /* This is the global state which is passed around among
47  * the various commands.
48  */
49 struct state {
50         struct lafs *lafs;
51         int done;
52         int verbose;
53 };
54
55 /* Every command can have arguments, both positional and
56  * named.
57  * Named arguments are specified with a leading hyphen, though
58  * multiple hyphens may be given in input (completion chooses 2).
59  * Tags for positional arguments must not start with '-', and is used
60  * only for help messages.
61  * Named arguments are normally followed by an '=' and a value.  If an argument is
62  * defined as a 'flag' then no '=' is expected.
63  * A tag argument can provide a value to a positional argument, and the
64  * command can easily determine if the tag version was used.
65  * Each tag may only be given once.
66  * A positional argument can be a subcommand which links to a new set
67  * of argument descriptions.
68  * types are:
69  *   flag:  expect --tagname      This is either present or not.
70  *   opaque:                      An uninterpreted string, often a number.
71  *   choice:                      On of a defined list of strings.
72  *   external:                    An external filename - completion is possible.
73  *   internal:                    An internal filename - completion might be possible.
74  *   subcommand:                  one of a list of subcommands.
75  * Any unique prefix of a tag is allowed to match.
76  */
77 enum argtype { flag, opaque, choice, external, internal, subcommand};
78 static struct args {
79         char *tag;
80         enum argtype type;
81         int pos;
82         union { struct cmd *subcmd; char **options; };
83         char *desc;
84 } lafs_args[];
85 #define TERMINAL_ARG {NULL, opaque, 0, {NULL}, NULL}
86
87 /* When a positional parameter is identified as 'subcommand' it is associated
88  * with a list of 'struct cmd' identifying the possible subcommands.
89  * Any unique prefix of a command is allowed to match.
90  * The initial argument list description allows a single argument which is
91  * a 'subcommand' identifying all the commands that lafs understands.
92  */
93 static struct cmd {
94         char *name;
95         void (*cmd)(struct state *st, void **args);
96         struct args *args;
97         char *help;
98 } lafs_cmds[];
99
100 /* Find a command in a list.  The word must be an exact match, or
101  * a unique prefix.
102  */
103 static struct cmd *find_cmd(struct cmd *cmds, char *word)
104 {
105         int c;
106         int l = strlen(word);
107         int best = -1;
108
109         for (c = 0; cmds[c].name; c++) {
110                 if (strcmp(word, cmds[c].name) == 0)
111                         return cmds+c;
112                 if (strncmp(word, cmds[c].name, l) == 0) {
113                         if (best == -1)
114                                 best = c;
115                         else
116                                 best = -2;
117                 }
118         }
119         if (best < 0)
120                 return NULL;
121         return cmds + best;
122 }
123
124 /* Find a tag in an argument list.  The given tag (up to the given length)
125  * must be an exact match or a unique prefix.
126  */
127 static int find_tag(struct args *args, const char *tag, int len)
128 {
129         int i;
130         int best = -1;
131
132         for (i = 0; args[i].tag; i++)
133                 if (args[i].tag[0] == '-') {
134                         if (strncmp(tag, args[i].tag+1, len) != 0)
135                                 continue;
136                         if (strlen(args[i].tag+1) == len)
137                                 return i;
138                         if (best == -1)
139                                 best = i;
140                         else
141                                 best = -2;
142                 }
143         return best;
144 }
145
146 /* Find an option in the list, return position.
147  * No prefixing is allowed
148  */
149 static int find_option(char **options, char *w)
150 {
151         int i;
152         for (i = 0; options[i]; i++)
153                 if (strcmp(options[i], w) == 0)
154                         return i;
155         return -1;
156 }
157
158 /* Return the first word on the line, modifying the line in-place and
159  * updating *line to be ready to get the next word.
160  *
161  * Space/tab separates words.  ' or " quotes words.
162  * \ protects quotes and spaces.
163  */
164 static char *take_word(char **line)
165 {
166         char *rv = *line; /* the buffer we will return - if non-empty */
167         char *lp = rv;    /* the line we are examining */
168         char *wp = rv;    /* the end of the word we are creating. */
169         static char *delim = " \t\n";
170         char quote = '\0';
171
172         while (*lp && strchr(delim, *lp) != NULL)
173                 lp++;
174
175         while (*lp && (quote || strchr(delim, *lp) == NULL)) {
176                 if (quote && *lp == quote) {
177                         lp++;
178                         continue;
179                 }
180                 switch(*lp) {
181                 case '\'':
182                 case '"':
183                         if (quote == *lp) {
184                                 quote = '\0';
185                                 continue;
186                         }
187                         if (!quote) {
188                                 quote = *lp++;
189                                 continue;
190                         }
191                         break;
192                 case '\\':
193                         if (lp[1] == '\'' || lp[1] == '"' || lp[1] == ' ')
194                                 lp++;
195                         break;
196                 }
197                 *wp++ = *lp++;
198         }
199         if (*lp)
200                 lp++;
201         *line = lp;
202         *wp = '\0';
203         if (wp > rv)
204                 return rv;
205         return NULL;
206 }
207
208 static int get_int(char *str, int *result)
209 {
210         long int num;
211         char *ep;
212
213         num = strtol(str, &ep, 10);
214         if (*str && !*ep) {
215                 *result = num;
216                 return 0;
217         }
218         return -1;
219 }
220
221 /* Return an array of void* corresponding to the entries in
222  * 'args'.  If an arg is present, the array entry is not NULL.
223  * For flags, the array entry will be the full flag.
224  * for --tag=, the array entry will be from after the '='
225  * for positional, the array entry will be the whole arg.
226  * where a tag can fill a positional, the value is placed in both
227  * slots.
228  * If an arg is a subcommand, then the 'struct cmd' point is placed
229  * in the return array rather than a string.
230  * The 'args' pointer is updated to the most precise subcommand.
231  * '*offsetp' is set to the offset in the returned array of the 
232  * first argument in the new 'args' list.
233  * '*lastp' is set to the last tag in 'args' that was matched.
234  * '*error' is an error message if something when astray.
235  *
236  * Named arguments can appear before, after, or among positional
237  * arguments.
238  */
239 static void **parse_line(struct args **argsp, char *line, int *offsetp,
240                          int *lastp, char **error)
241 {
242         void **rv;
243         int i;
244         char *w;
245         int offset = 0;
246         int size;
247         struct args *args = *argsp;
248
249         if (lastp)
250                 *lastp = -1;
251
252         for (i = 0; args[i].tag; i++)
253                 ;
254         rv = calloc(i+offset, sizeof(char*));
255         size = i+offset;
256
257         while (!*error && (w = take_word(&line)) != NULL) {
258                 if (*w == '-') {
259                         /* Find the matching tag. */
260                         char *t = w, *e;
261                         int n, n2;
262                         while (*t == '-')
263                                 t++;
264                         e = t;
265                         while (*e && *e != '=')
266                                 e++;
267                         n = n2 = find_tag(args, t, e-t);
268                         if (n < 0) {
269                                 asprintf(error, "Unrecognised option: %s", w);
270                                 break;
271                         }
272                         if (lastp)
273                                 *lastp = n;
274                         if (args[n].pos >= 0)
275                                 n2 = args[n].pos;
276                         if (rv[n+offset] != NULL || rv[n2+offset] != NULL) {
277                                 asprintf(error, "Duplicate option: %s", w);
278                                 break;
279                         } else {
280                                 if (*e == '=')
281                                         w = e+1;
282                                 else if (args[n].type != flag) {
283                                         w = take_word(&line);
284                                         if (!w) {
285                                                 asprintf(error,
286                                                          "Missing value for --%s", t);
287                                                 break;
288                                         }
289                                 }
290                                 rv[n+offset] = w;
291                                 rv[n2+offset] = w;
292                         }
293                 } else {
294                         /* must be next positional */
295                         for (i=0;
296                              args[i].tag && args[i].tag[0] != '-';
297                              i++)
298                                 if (rv[i+offset] == NULL)
299                                         break;
300                         if (args[i].tag == NULL || args[i].tag[0] == '-') {
301                                 /* No positions left */
302                                 asprintf(error, "Extra positional parameter: %s", w);
303                                 break;
304                         }
305                         rv[i+offset] = w;
306                         /* If this is a subcommand arg then we need to
307                          * parse the remaining args in the context of the
308                          * given subcommand - if it exists.
309                          */
310                         switch(args[i].type) {
311                                 struct cmd *c;
312                                 int o;
313                         default: break;
314                         case choice:
315                                 o = find_option(args[i].options, w);
316                                 if (o < 0)
317                                         asprintf(error, "Value %s for %s is not acceptable", w, args[i].tag);
318                                 break;
319                         case subcommand:
320                                 c = find_cmd(args[i].subcmd, w);
321                                 rv[i+offset] = c;
322                                 if (c) {
323                                         args = c->args;
324                                         *argsp = args;
325                                         offset += i+1;
326                                         if (lastp)
327                                                 *lastp = -1;
328                                         for (i = 0; args[i].tag; i++)
329                                                 ;
330                                         rv = realloc(rv, (i+offset) * sizeof(void*));
331                                         while (size < i + offset) {
332                                                 rv[size] = NULL;
333                                                 size++;
334                                         }
335                                 } else
336                                         asprintf(error, "Unrecognised command: %s",w);
337                                 break;
338                         }
339                 }
340         }
341         if (offsetp)
342                 *offsetp = offset;
343         return rv;
344 }
345
346 /* parse and execute the given command line against the given state. */
347 static int execute_line(struct state *st, char *line)
348 {
349         struct cmd *c;
350         struct args *args = lafs_args;
351         void **arglist;
352         char *error = NULL;
353
354         arglist = parse_line(&args, line, NULL, NULL, &error);
355         
356         if (error) {
357                 fprintf(stderr, "lafs: %s\n", error);
358                 free(error);
359                 free(arglist);
360                 return -1;
361         }
362         c = (struct cmd*)arglist[0];
363         if (c)
364                 c->cmd(st, arglist);
365         free(arglist);
366         return 1;
367 }
368
369 static char **complete_in_context(const char *word, int start, int end);
370
371 /* 'interact' is the main interface when used interactively.
372  * It reads lines using 'readline' and executes them.
373  * 'readline' is configured to provide context sensitive completion
374  * and help.
375  */
376 static void interact(void)
377 {
378         struct state st = {0};
379         st.lafs = lafs_alloc();
380         st.verbose = 1;
381         rl_attempted_completion_function = complete_in_context;
382         rl_basic_word_break_characters = " \t\n=";
383         rl_completer_quote_characters = "\"'";
384         rl_initialize();
385         
386         while (!st.done) {
387                 char *line = readline("LaFS: ");
388
389                 if (!line) {
390                         printf("\n");
391                         break;
392                 }
393
394                 if (*line)
395                         add_history(line);
396                 execute_line(&st, line);
397
398                 free(line);
399         }
400 }
401
402 /* 'runfile' is the alternate interface when a regular file is
403  * given with commands.  It reads and executes commands until
404  * it finds an error.
405  */
406 static void runfile(FILE *f)
407 {
408         struct state st = {0};
409         st.lafs = lafs_alloc();
410         st.verbose = 0;
411
412         while (!st.done) {
413                 char *line = NULL;
414                 ssize_t len;
415                 size_t size;
416
417                 len = getline(&line, &size, f);
418
419                 if (len <= 0)
420                         st.done = 1;
421                 else if (execute_line(&st, line) < 0)
422                         st.done = 1;
423
424                 free(line);
425         }
426 }
427
428
429 int main(int argc, char *argv[])
430 {
431         if (argc > 1) {
432                 if (strcmp(argv[1], "-") == 0)
433                         runfile(stdin);
434                 else {
435                         FILE *f = fopen(argv[1], "r");
436                         if (f)
437                                 runfile(f);
438                         else
439                                 fprintf(stderr, "lafs: cannot open %s\n", argv[1]);
440                 }
441         } else
442                 interact();
443         exit(0);
444 }
445
446 /*
447  * Here be routines to provide context sensitive completion and
448  * help.
449  * Completion understands:
450  *  - lists of subcommands
451  *  - lists of tags for options
452  *  - options that expect filenames, whether internal or external.
453  * Before listing possible matches, the description of the expected
454  * option is given.
455  */
456
457 /* cmd_gen is used to generate a list of matching commands.
458  * 'gen_cmds' must be initialised to point to the list.
459  */
460 static struct cmd *gen_cmds;
461 static char *cmd_gen(const char *prefix, int state)
462 {
463         static int next;
464         int len = strlen(prefix);
465         if (state == 0)
466                 next = 0;
467         for ( ; gen_cmds[next].name ; next++)
468                 if (strncmp(prefix, gen_cmds[next].name, len) == 0) {
469                         next++;
470                         return strdup(gen_cmds[next-1].name);
471                 }
472         return NULL;
473 }
474
475 /* tag_gen is used to generate a list of expected tags.
476  * 'gen_args' is the relevant argument list.
477  * 'gen_found' is used to determine which tags have already been given,
478  *    so they are not offered again.
479  * generated tags always start "--" even if user types "-ta".
480  */
481 static struct args *gen_args;
482 static void **gen_found;
483 static char *tag_gen(const char *prefix, int state)
484 {
485         static int next;
486         int len;
487
488         if (state == 0)
489                 next = 0;
490
491         while (*prefix == '-')
492                 prefix++;
493         len = strlen(prefix);
494
495         for ( ; gen_args[next].tag; next++) {
496                 char *c;
497                 if (gen_args[next].tag[0] != '-')
498                         continue;
499                 if (gen_found[next])
500                         continue;
501                 if (gen_args[next].pos >= 0 &&
502                     gen_found[gen_args[next].pos])
503                         continue;
504                 if (strncmp(prefix, gen_args[next].tag+1, len) != 0)
505                         continue;
506
507                 c = malloc(2 + strlen(gen_args[next].tag+1) + 2);
508                 strcpy(c, "--");
509                 strcpy(c+2, gen_args[next].tag+1);
510                 if (gen_args[next].type != flag) {
511                         strcat(c, "=");
512                         rl_completion_suppress_append = 1;
513                 }
514                 next++;
515                 return c;
516         }
517         return NULL;
518 }
519
520 /* choice_gen is used to generate a list of possible values
521  * for a 'choice' field.
522  * 'gen_options' is the options that can go here.
523  */
524 static char **gen_options;
525 static char *choice_gen(const char *prefix, int state)
526 {
527         static int next;
528         int len;
529
530         if (state == 0)
531                 next = 0;
532
533         len = strlen(prefix);
534         for (; gen_options[next] ; next++) {
535                 if (strncmp(prefix, gen_options[next], len) != 0)
536                         continue;
537                 next++;
538                 return strdup(gen_options[next-1]);
539         }
540         return NULL;
541 }
542
543 /*
544  * This is the brains of the completion handler.
545  * We parse the line-so-far to determine way options have already
546  * been provided, and so what is left to provide.
547  * We then look at the given prefix to determine if a positional or
548  * tag argument is most likely, and provide relevant completions.
549  */
550 static char **complete_in_context(const char *prefix, int start, int end)
551 {
552         static char *buf = NULL;
553         static int bufsize = 0;
554
555         char *line;
556         void **arglist;
557         struct args *args;
558         int offset, last;
559         int p;
560         char *error = NULL;
561         char **matches = NULL;
562
563         while (bufsize < start+1) {
564                 bufsize += 80;
565                 buf = realloc(buf, bufsize);
566         }
567         memcpy(buf, rl_line_buffer, start);
568         buf[start] = 0;
569         line = buf;
570
571         args = lafs_args;
572         arglist = parse_line(&args, line, &offset, &last, &error);
573
574         if (last >= 0 &&
575             error && strncmp(error, "Missing value for", 17) == 0) {
576                 free(error);
577                 error = 0;
578         } else if (!(start && rl_line_buffer[start-1] == '='))
579                 last = -1;
580
581         if (error) {
582                 printf("\n *** %s ***\n", error);
583                 free(error);
584                 goto after_message;
585         }
586
587         if (last >= 0 && (arglist[last+offset] == NULL ||
588                           ((char*)arglist[last+offset])[0] == '\0'))
589                 p = last;
590         else {
591                 last = -1;
592                 for (p = 0; args[p].tag && args[p].tag[0] != '-' ; p++)
593                         if (arglist[p+offset] == NULL)
594                                 break;
595                 if (args[p].tag == NULL || args[p].tag[0] == '-')
596                         p = -1;
597         }
598         /* 'p' is the arg we expect here, either first positional arg that
599          *  we haven't seen, or tag that we have "--tag=" for. */
600
601         if (last >= 0 || (p >= 0 && (!*prefix || *prefix != '-'))) {
602                 switch(args[p].type) {
603                 case subcommand:
604                         gen_cmds = args[p].subcmd;
605                         matches = rl_completion_matches(prefix, cmd_gen);
606                         break;
607                 case external:
608                         matches = rl_completion_matches(
609                                 prefix, rl_filename_completion_function);
610                         break;
611                 case choice:
612                         gen_options = args[p].options;
613                         matches = rl_completion_matches(
614                                 prefix, choice_gen);
615                         break;
616                 default:
617                         break;
618                 }
619                 if (rl_completion_type == '?') {
620                         printf("\n *** Please give: %s ***", args[p].desc);
621                         if (!matches) {
622                                 printf("\n");
623                                 rl_on_new_line();
624                         }
625                 }
626                 rl_attempted_completion_over = 1;
627                 return matches;
628         }
629         if (!*prefix || *prefix == '-') {
630                 gen_args = args;
631                 gen_found = arglist + offset;
632                 rl_attempted_completion_over = 1;
633                 return rl_completion_matches(prefix, tag_gen);
634         }
635
636         printf("\n *** No further positional arguments expected:"
637                " try '-' instead ***\n");
638 after_message:
639         rl_on_new_line();
640         rl_attempted_completion_over = 1;
641         return NULL;
642 }
643
644 /***********************************************************************8
645  * Here are the commands.
646  * Each command X must define
647  *   static char help_X = "One line of description for the command";
648
649  *   static struct args args_X[] = { list of arg definitions; TERMINAL_ARG};
650  *   static void c_X(struct state *st, void *args) { implement command ;}
651  * and must be listed in lafs_cmds below.
652  */
653
654 /* common helper functions... */
655 static long long parse_size_print(char *arg, char **error, char *func, char *name)
656 {
657         long long rv = parse_size(arg, error);
658         if (*error)
659                 printf("%s: %s: %s for %s\n", func, *error, arg, name);
660         return rv;
661 }
662 static long parse_num_print(char *arg, char **error, char *func, char *name)
663 {
664         char *endp;
665         long rv = strtol(arg, &endp, 0);
666         if (!*arg || *endp) {
667                 *error = "Not a valid number";
668                 printf("%s: %s: %s for %s\n", func, *error, arg, name);
669         }
670         return rv;
671 }
672 /****** EXIT ******/
673 static char help_exit[] = "Exit lafs";
674 static struct args args_exit[] = { TERMINAL_ARG };
675 static void c_exit(struct state *st, void **args)
676 {
677         st->done = 1;
678 }
679 /****** QUIT ******/
680 #define help_quit help_exit
681 #define c_quit c_exit
682 #define args_quit args_exit
683
684 /****** HELP ******/
685 static char help_help[] = "Print help for a command or all commands";
686 static struct args args_help[] = {
687         { "COMMAND", subcommand, -1, {lafs_cmds}, "Command to display help for"},
688         { "-all", flag,      -1, {NULL}, "List brief help for all commands"},
689         TERMINAL_ARG
690 };
691
692 static void c_help(struct state *st, void **args)
693 {
694         int c;
695         struct cmd *cmd = args[1];
696
697         if (cmd == NULL && args[2] == NULL) {
698                 for (c = 0; lafs_cmds[c].name; c++) {
699                         printf("%-9s ", lafs_cmds[c].name);
700                         if ((c%8)==7)
701                                 printf("\n");
702                 }
703                 printf("\n");
704                 return;
705         }
706
707         if (cmd) {
708                 printf("%s: %s\n", cmd->name, cmd->help);
709                 if (cmd->args[0].tag) {
710                         int i;
711                         printf(" Usage: %s", cmd->name);
712                         for (i=0; cmd->args[i].tag; i++) {
713                                 struct args *a = cmd->args+i;
714                                 if (a->tag[0] == '-') {
715                                         printf(" [--options...]");
716                                         break;
717                                 }
718                                 printf(" %s", a->tag);
719                         }
720                         printf("\n");
721                         printf(" Arguments:\n");
722                         for (i=0; cmd->args[i].tag; i++) {
723                                 struct args *a = cmd->args+i;
724                                 if (a->tag[0] != '-') {
725                                         printf("  %-15s: %s\n", a->tag, a->desc);
726                                 } else
727                                         printf("  -%-14s: %s\n", a->tag, a->desc);
728                         }
729                 }
730         } else {
731                 printf("-------------------------------------------\n");
732                 for (c=0; lafs_cmds[c].name; c++)
733                         printf("%-9s: %s\n", lafs_cmds[c].name, lafs_cmds[c].help);
734         }
735 }
736
737 /****** RESET ******/
738 static char help_reset[] = "Forget all fs information, and prepare to start afresh";
739 static struct args args_reset[] = { 
740         { "-force", flag, -1, {NULL}, "Reset even if there are unflushed changes"},
741         TERMINAL_ARG
742 };
743 static void c_reset(struct state *st, void **args)
744 {
745
746         if (st->lafs->blocksize == 0) {
747                 printf("reset: Filesystem state is already clear\n");
748                 return;
749         }
750
751         if (args[1] == NULL &&
752             !list_empty(&st->lafs->wc[0].blocks)) {
753                 printf("reset: filesystem has unflushed changes.  Consider using\n"
754                        "       \"flush\" command of \"--force\" argument\n");
755                 return;
756         }
757         talloc_free(st->lafs);
758         st->lafs = lafs_alloc();
759         if (st->verbose)
760                 printf("Filesystem state has been reset\n");
761 }
762
763
764 /****** NEWFS ******/
765 static char help_newfs[] = "Create a new LaFS filesystem, which can then be stored on one or more devices.";
766 static char *block_sizes[] = { "512", "1024", "2048", "4096", NULL };
767 static char *state_sizes[] = { "512", "1024", "2048", "4096", "8192",
768                                "16384", "32768", NULL };
769 static struct args args_newfs[] = {
770         { "BLOCK-SIZE",  choice, -1, {.options=block_sizes},
771           "Block size, 512..4096, defaults to 1024"},
772         { "-block-size", choice,  0, {.options=block_sizes},
773           "Block size, 512..4096, defaults to 1024"},
774         { "-state-size", choice, -1, {.options=state_sizes},
775           "Size of state block, 512..32768, defaults to 1024"},
776         { "-uuid",       opaque, -1, {NULL}, "UUID - normally randomly generated"},
777         { "-black", opaque, -1, {NULL}, "nothing (just testing)"},
778         TERMINAL_ARG
779 };
780 static void c_newfs(struct state *st, void **args)
781 {
782         int blockbytes = 1024;
783         int state_size = 0;
784         char uuidstr[37];
785         uuid_t uu;
786         struct lafs_ino *ifile, *imfile, *rootdir;
787         int create_atime = 1;
788
789         if (st->lafs->blocksize) {
790                 printf("newfs: Filesytem already has state"
791                        " - consider using \"reset\"\n");
792                 return;
793         }
794
795         if (args[1])
796                 /* As this is a 'choice' it must be a valid number. */
797                 get_int(args[1], &blockbytes);
798
799         if (args[3]) {
800                 /* state-size was given */
801                 get_int(args[3], &state_size);
802         }
803         if (args[4]) {
804                 /* uuid was given */
805                 if (uuid_parse((char*)args[4], uu) < 0) {
806                         printf("newfs: UUID in wrong format: %s\n", (char*)args[4]);
807                         return;
808                 }
809         }
810         lafs_new(st->lafs, blockbytes);
811         if (state_size)
812                 st->lafs->statesize = state_size;
813         if (args[4])
814                 memcpy(st->lafs->uuid, uu, 16);
815
816         ifile = lafs_get_itable(st->lafs);
817         imfile = lafs_add_inode(ifile, 1, TypeInodeMap);
818         rootdir = lafs_add_inode(ifile, 2, TypeDir);
819         if (create_atime)
820                 lafs_add_inode(ifile, 3, TypeAccessTime);
821         rootdir->md.file.linkcount = 2;
822         rootdir->md.file.mode = 0755;
823         rootdir->md.file.parent = 2;
824         lafs_dirty_inode(rootdir);
825         lafs_add_inode(ifile, 8, TypeOrphanList);
826
827         lafs_imap_set(imfile, 1);
828         lafs_imap_set(imfile, 2);
829         lafs_imap_set(imfile, 8);
830
831         lafs_cluster_init(st->lafs, 0, 0, 0, 1);
832
833         if (st->verbose) {
834                 uuid_unparse(st->lafs->uuid, uuidstr);
835                 printf("Filesystem has been initilised: block size %d, "
836                        "state size %d\n    UUID=%s\n",
837                        st->lafs->blocksize, st->lafs->statesize,
838                        uuidstr);
839         }
840         return;
841 }
842
843 /****** ADD_DEVICE ******/
844 static char help_add_device[] = "Add a device to the current LaFS";
845 static struct args args_add_device[] = {
846 /*1*/   { "DEVNAME", external, -1, {NULL}, "Device to store filesystem on"},
847 /*2*/   { "-file",   external, 0, {NULL}, "Regular file to use like a device"},
848 /*3*/   { "-size",   opaque, -1, {NULL}, "Size of regular file (K,M,G prefix allowed)"},
849 /*4*/   { "-segsize",opaque, -1, {NULL}, "Segment size for this device"},
850 /*5*/   { "-stride", opaque, -1, {NULL}, "Stride (from one member device to next)"},
851 /*6*/   { "-width",  opaque, -1, {NULL}, "Width of array in data-devices"},
852 /*7*/   { "-usage_inum", opaque, -1, {NULL}, "Inode number for segment-usage file"},
853         TERMINAL_ARG
854 };
855 static void c_add_device(struct state *st, void **args)
856 {
857         long block_bytes, segment_bytes = 0, stride_bytes = 0;
858         int width = 0;
859         long long device_bytes = 0;
860         int usage_inum = 0;
861         char *err = NULL;
862         char *devname = args[1];
863         int fd;
864         struct lafs_device *dev;
865         struct lafs_ino *ifile, *imfile, *segmap;
866
867         if (!devname) {
868                 printf("add_device: No device or file name given to add\n");
869                 return;
870         }
871
872         block_bytes = st->lafs->blocksize;
873         if (block_bytes == 0) {
874                 printf("add_device: filesystem is not ready for devices"
875                        " - consider \"newfs\".\n");
876                 return;
877         }
878
879         ifile = st->lafs->ss.root;
880         if (!ifile) {
881                 printf("add_device: filesystem has no root inode - strange"
882                        " - consider \"newfs\" again.\n");
883                 return;
884         }
885
886         imfile = lafs_get_inode(ifile, 1);
887         if (!imfile) {
888                 printf("add_device: Cannot find inode-map\n");
889                 return;
890         }
891         
892
893         if (args[3]) {
894                 device_bytes = parse_size_print(args[3], &err, "add_device",
895                                            "file size");
896                 if (err)
897                         return;
898         }
899         if (args[4]) {
900                 segment_bytes = parse_size_print(args[4], &err, "add_device",
901                                            "segment size");
902                 if (err)
903                         return;
904         }
905         if (args[5]) {
906                 stride_bytes = parse_size_print(args[5], &err, "add_device",
907                                           "stride size");
908                 if (err)
909                         return;
910         }
911         if (args[6]) {
912                 width = parse_num_print(args[6], &err, "add_device", "width");
913                 if (err)
914                         return;
915         }
916         if (args[7]) {
917                 usage_inum = parse_num_print(args[7], &err,
918                                              "add_device", "inode number");
919                 if (err)
920                         return;
921         }
922
923         fd = open_device(devname, &device_bytes, args[2] != NULL, &err);
924
925         if (fd < 0) {
926                 printf("add_device: %s\n", err);
927                 free(err);
928                 return;
929         }
930
931         err = lafs_validate_geometry(&block_bytes, &segment_bytes,
932                                      &stride_bytes, &width, device_bytes);
933
934         if (err) {
935                 printf("add_device: %s\n", err);
936                 free(err);
937                 return;
938         }
939
940         if (!usage_inum) {
941                 printf("FIXME defaulting usage inum to 16 "
942                        "- should check if in-use\n");
943                 usage_inum = 16;
944         }
945         dev = lafs_add_device(st->lafs, devname, fd,
946                               segment_bytes / block_bytes,
947                               stride_bytes / block_bytes,
948                               width,
949                               usage_inum);
950
951         segmap = lafs_add_inode(ifile, usage_inum, TypeSegmentMap);
952         if (segmap == NULL) {
953                 printf("ERROR: could not allocate segusage file.\n");
954                 st->lafs->flags |= LAFS_NEED_CHECK;
955                 return;
956         }
957         dev->segsum = segmap;
958         segmap->md.segmentusage.table_size = dev->tablesize * usage_inum;
959         dev->tablesize = segmap->md.segmentusage.table_size;
960         lafs_dirty_inode(segmap);
961         lafs_imap_set(imfile, usage_inum);
962
963         printf("Added device %s at %llu with %llu segments of %llu %dk blocks\n",
964                devname, (unsigned long long)dev->start,
965                        (unsigned long long)dev->segment_count,
966                (unsigned long long)dev->segment_size,
967                st->lafs->blocksize/1024);
968
969 }
970
971 /****** WRITE_DEV ******/
972 static char help_write_dev[] = "Write devices blocks to one or all devices";
973 static struct args args_write_dev[] = {
974         { "DEVNAME", external, -1, {NULL}, "Device to write devblocks to"},
975         { "-all",    flag,      0, {NULL}, "Write to all devices"},
976         TERMINAL_ARG
977 };
978 static void c_write_dev(struct state *st, void **args)
979 {
980         struct lafs_device *dev;
981         int found = 0;
982         if (!args[1] && !args[2]) {
983                 printf("write dev: no device given for writing\n");
984                 return;
985         }
986
987         for (dev = st->lafs->devs; dev; dev = dev->next) {
988                 if (args[2] || strcmp(dev->name, (char*)args[1]) == 0) {
989                         int err = lafs_write_dev(dev);
990                         found = 1;
991                         if (err)
992                                 printf("write dev: error when writing to %s\n",
993                                        dev->name);
994                         else if (st->verbose)
995                                 printf("Device block written to %s\n", dev->name);
996                 }
997         }
998         if (found)
999                 return;
1000         if (args[2])
1001                 printf("write dev: no devices exist to write to.\n");
1002         else
1003                 printf("write dev: %s is not a registered device in this LaFS.\n",
1004                        (char*)args[1]);
1005 }
1006
1007 /****** WRITE_STATE ******/
1008 static char help_write_state[] = "Write state blocks to all devices";
1009 static struct args args_write_state[] = {
1010         TERMINAL_ARG
1011 };
1012 static void c_write_state(struct state *st, void **args)
1013 {
1014         if (st->lafs->blocksize == 0)
1015                 printf("write state: filesystem is not initialised\n");
1016         else if (st->lafs->devs == NULL)
1017                 printf("write state: No devices exist to write to\n");
1018         else if (lafs_write_state(st->lafs))
1019                 printf("write state: Error writing a state block\n");
1020         else if (st->verbose)
1021                 printf("%s state blocks written: seq now %llu\n",
1022                        (st->lafs->seq & 1) ? "Odd" : "Even",
1023                        (unsigned long long) st->lafs->seq);
1024 }
1025
1026 /****** WRITE_CHECKPOINT ******/
1027 static char help_write_checkpoint[] = "Write a checkpoint with all committed blocks";
1028 static struct args args_write_checkpoint[] = {
1029         TERMINAL_ARG
1030 };
1031 static void c_write_checkpoint(struct state *st, void **args)
1032 {
1033         if (st->lafs->blocksize == 0)
1034                 printf("write checkpoint: filesystem is not initialised\n");
1035         else if (st->lafs->devs == NULL)
1036                 printf("write checkpoint: No devices exist to write to\n");
1037         else if (st->lafs->free_head == st->lafs->free_tail)
1038                 printf("write checkpoint: No free segments - try find_free\n");
1039         else if (lafs_checkpoint(st->lafs))
1040                 printf("write state: Error writing checkpoint\n");
1041         else if (st->verbose)
1042                 printf("Checkpoint written: seq now %llu\n",
1043                        (unsigned long long) st->lafs->seq);
1044 }
1045
1046 /****** LOAD_DEV ******/
1047 static char help_load_dev[] = "Allow access to LaFS filesystem stored on given device";
1048 static struct args args_load_dev[] = {
1049         { "DEVNAME", external, -1, {NULL}, "Device to load filesystem info from"},
1050         { "-file",   external,  0, {NULL}, "Regular file to load filesystem info from"},
1051         TERMINAL_ARG
1052 };
1053 static void c_load_dev(struct state *st, void **args)
1054 {
1055         char *devname = args[1];
1056         int fd;
1057         long long device_bytes = 0;
1058         char *err;
1059         struct lafs_device *dev;
1060
1061         if (!devname) {
1062                 printf("load_dev: No device of file name given to load\n");
1063                 return;
1064         }
1065
1066         fd = open_device(devname, &device_bytes, args[2] != NULL, &err);
1067
1068         if (fd < 0) {
1069                 printf("load_dev: %s\n", err);
1070                 free(err);
1071                 return;
1072         }
1073
1074         dev = lafs_load(fd, device_bytes, &err);
1075
1076         if (err) {
1077                 printf("load_dev: Cannot load %s: %s\n", devname, err);
1078                 if (dev)
1079                         talloc_free(dev);
1080                 close(fd);
1081                 return;
1082         }
1083
1084         if (lafs_include_dev(st->lafs, dev, &err) != 0) {
1085                 printf("load_dev: Cannot include %s: %s\n", devname, err);
1086                 talloc_free(dev);
1087                 return;
1088         }
1089         if (st->verbose) {
1090                 printf("loaded device %s - have %d of %d\n", devname,
1091                        st->lafs->loaded_devs, st->lafs->devices);
1092         }
1093 }
1094
1095 /****** STORE ******/
1096 static char help_store[] = "Create a file in the LaFS from an external file";
1097 static struct args args_store[] = {
1098         { "FROM", external, -1, {NULL}, "File to copy into LaFS"},
1099         { "TO",   internal, -1, {NULL}, "Where to store file in LaFS"},
1100         { "-from", external, 0, {NULL}, "File to copy into LaFS"},
1101         TERMINAL_ARG
1102 };
1103 static void c_store(struct state *st, void **args)
1104 {
1105         char *from = args[1];
1106         char *to = args[2];
1107         if (!from)
1108                 printf("ERROR: Source file is missing\n");
1109         else if (!to)
1110                 printf("ERROR: destination file name is missing\n");
1111         else
1112                 printf("Oh how I wish I could copy %s to %s\n", from, to);
1113 }
1114
1115 /* list of all commands - preferably in alphabetical order */
1116 #define CMD(x) {#x, c_##x, args_##x, help_##x}
1117 static struct cmd lafs_cmds[] = {
1118         {"?", c_help, args_help, help_help},
1119         CMD(add_device),
1120         CMD(exit),
1121         CMD(help),
1122         CMD(load_dev),
1123         CMD(newfs),
1124         CMD(quit),
1125         CMD(reset),
1126         CMD(store),
1127         CMD(write_checkpoint),
1128         CMD(write_dev),
1129         CMD(write_state),
1130         { NULL, NULL, NULL, NULL}
1131 };
1132
1133 static struct args lafs_args[] = {
1134         { "COMMAND",    subcommand, -1, {lafs_cmds}, "Command for lafs to execute"},
1135         TERMINAL_ARG
1136 };