implement argument parsing, split client_proto.h
[samba.git] / source3 / client / clitar.c
1 /*
2    Unix SMB/CIFS implementation.
3    Tar backup command extension
4    Copyright (C) AurĂ©lien Aptel 2013
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "client/client_proto.h"
23 #include "client/clitar_proto.h"
24 #include "libsmb/libsmb.h"
25 #include <archive.h>
26
27 #define LEN(x) (sizeof(x)/sizeof((x)[0]))
28 #define TAR_MAX_BLOCK_SIZE 65535
29 /*
30  * XXX: used in client.c, we have to export it for now.
31  * corresponds to the transfer operation. Can be '\0', 'c' or 'x'
32  */
33 char tar_type = 0;
34
35 enum tar_operation {
36     TAR_NO_OPERATION,
37     TAR_CREATE,    /* c flag */
38     TAR_EXTRACT,   /* x flag */
39 };
40
41 enum tar_selection {
42     TAR_NO_SELECTION,
43     TAR_INCLUDE,       /* I flag, default */
44     TAR_INCLUDE_LIST,  /* F flag */
45     TAR_EXCLUDE,       /* X flag */
46 };
47
48 enum {
49     ATTR_UNSET,
50     ATTR_SET,
51 };
52
53 struct tar {
54     /* flags */
55     struct tar_mode {
56         enum tar_operation operation; /* create, extract */
57         enum tar_selection selection; /* inc, inc from file, exclude */
58         int blocksize;    /* size in bytes of a block in the tar file */
59         bool hidden;      /* backup hidden file? */
60         bool system;      /* backup system file? */
61         bool incremental; /* backup _only_ archived file? */
62         bool reset;       /* unset archive bit? */
63         bool dry;         /* don't write tar file? */
64         bool regex;       /* XXX: never actually using regex... */
65         bool verbose;
66     } mode;
67
68     /* path to tar archive name */
69     char *tar_path;
70
71     /* file descriptor of tar file */
72     int tar_fd;
73
74     /* list of path to include or exclude */
75     char **path_list;
76
77     /* archive handle */
78     struct archive *archive;
79 };
80
81 struct tar tar_ctx = {
82     .mode.selection   = TAR_INCLUDE,
83     .mode.blocksize   = 20,
84     .mode.hidden      = True,
85     .mode.system      = True,
86     .mode.incremental = False,
87     .mode.dry         = False,
88 };
89
90 static int tar_set_blocksize(struct tar *t, int size)
91 {
92     if (size <= 0 || size > TAR_MAX_BLOCK_SIZE) {
93         return 0;
94     }
95
96     t->mode.blocksize = size;
97
98     return 1;
99 }
100
101 static bool tar_set_newer_than(struct tar *t, char *filename)
102 {
103     extern time_t newer_than;
104     SMB_STRUCT_STAT stbuf;
105
106     if (sys_stat(filename, &stbuf, false) != 0) {
107         DEBUG(0, ("Error setting newer-than time\n"));
108         return 0;
109     }
110
111     newer_than = convert_timespec_to_time_t(stbuf.st_ex_mtime);
112     DEBUG(1, ("Getting files newer than %s\n", time_to_asc(newer_than)));
113     return 1;
114 }
115
116 static bool tar_read_inclusion_file (struct tar *t, const char* filename)
117 {
118     char *line;
119     const char **list;
120     TALLOC_CTX *ctx = talloc_tos();
121     int fd = open(filename, O_RDONLY);
122
123     if (fd < 0) {
124         DEBUG(0, ("Can't open inclusion file '%s': %s\n", filename, strerror(errno)));
125         return 0;
126     }
127
128     list = str_list_make_empty(ctx);
129
130     while ((line = afdgets(fd, ctx, 0))) {
131         list = str_list_add(list, line);
132     }
133
134     close(fd);
135     t->path_list = list;
136     return 1;
137 }
138
139 /**
140  * cmd_block - interactive command to change tar blocksize
141  *
142  * Read a size from the client command line and update the current
143  * blocksize.
144  */
145 int cmd_block(void)
146 {
147     /* XXX: from client.c */
148     const extern char *cmd_ptr;
149     char *buf;
150     TALLOC_CTX *ctx = talloc_tos();
151
152     if (!next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) {
153         DEBUG(0, ("blocksize <n>\n"));
154         return 1;
155     }
156
157     if(!tar_set_blocksize(&tar_ctx, atoi(buf))) {
158         DEBUG(0, ("invalid blocksize\n"));
159     }
160
161     DEBUG(2, ("blocksize is now %d\n", tar_ctx.mode.blocksize));
162
163     return 0;
164 }
165
166 /**
167  * cmd_tarmode - interactive command to change tar behaviour
168  *
169  * Read one or more modes from the client command line and update the
170  * current tar mode.
171  */
172 int cmd_tarmode(void)
173 {
174     const extern char *cmd_ptr;
175     char *buf;
176     int i;
177     TALLOC_CTX *ctx = talloc_tos();
178
179     struct {
180         const char *cmd;
181         bool *p;
182         bool value;
183     } table[] = {
184         {"full",      &tar_ctx.mode.incremental, False},
185         {"inc",       &tar_ctx.mode.incremental, True },
186         {"reset",     &tar_ctx.mode.reset,       True },
187         {"noreset",   &tar_ctx.mode.reset,       False},
188         {"system",    &tar_ctx.mode.system,      True },
189         {"nosystem",  &tar_ctx.mode.system,      False},
190         {"hidden",    &tar_ctx.mode.hidden,      True },
191         {"nohidden",  &tar_ctx.mode.hidden,      False},
192         {"verbose",   &tar_ctx.mode.verbose,     True },
193         {"noquiet",   &tar_ctx.mode.verbose,     True },
194         {"quiet",     &tar_ctx.mode.verbose,     False},
195         {"noverbose", &tar_ctx.mode.verbose,     False},
196     };
197
198     while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) {
199         for (i = 0; i < LEN(table); i++) {
200             if (strequal(table[i].cmd, buf)) {
201                 *table[i].p = table[i].value;
202                 break;
203             }
204         }
205
206         if (i == LEN(table))
207             DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
208
209         TALLOC_FREE(buf);
210     }
211
212     DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
213               tar_ctx.mode.incremental ? "incremental" : "full",
214               tar_ctx.mode.system      ? "system"      : "nosystem",
215               tar_ctx.mode.hidden      ? "hidden"      : "nohidden",
216               tar_ctx.mode.reset       ? "reset"       : "noreset",
217               tar_ctx.mode.verbose     ? "verbose"     : "quiet"));
218     return 0;
219 }
220
221 /**
222  * set_remote_attr - set DOS attributes of a remote file
223  * @filename: path to the file name
224  * @new_attr: attribute bit mask to use
225  * @mode: one of ATTR_SET or ATTR_UNSET
226  *
227  * Update the file attributes with the one provided.
228  */
229 static void set_remote_attr(char *filename, uint16 new_attr, int mode)
230 {
231     extern struct cli_state *cli;
232     uint16 old_attr;
233     NTSTATUS status;
234
235     if (!NT_STATUS_IS_OK(cli_getatr(cli, filename, &old_attr, NULL, NULL))) {
236         /* XXX: debug message */
237         return;
238     }
239
240     if (mode == ATTR_SET) {
241         new_attr |= old_attr;
242     } else {
243         new_attr = old_attr & ~new_attr;
244     }
245
246     status = cli_setatr(cli, filename, new_attr, 0);
247     if (!NT_STATUS_IS_OK(status)) {
248         DEBUG(1, ("setatr failed: %s\n", nt_errstr(status)));
249     }
250 }
251
252 /**
253  * cmd_setmode - interactive command to set DOS attributes
254  *
255  * Read a filename and mode from the client command line and update
256  * the file DOS attributes.
257  */
258 int cmd_setmode(void)
259 {
260     const extern char *cmd_ptr;
261     char *buf;
262     char *fname = NULL;
263     uint16 attr[2] = {0};
264     int mode = ATTR_SET;
265     TALLOC_CTX *ctx = talloc_tos();
266
267
268     if (!next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) {
269         DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
270         return 1;
271     }
272
273     fname = talloc_asprintf(ctx,
274                             "%s%s",
275                             client_get_cur_dir(),
276                             buf);
277     if (!fname) {
278         return 1;
279     }
280
281     while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) {
282         const char *s = buf;
283
284         while(*s) {
285             switch (*s++) {
286             case '+':
287                 mode = ATTR_SET;
288                 break;
289             case '-':
290                 mode = ATTR_UNSET;
291                 break;
292             case 'r':
293                 attr[mode] |= FILE_ATTRIBUTE_READONLY;
294                 break;
295             case 'h':
296                 attr[mode] |= FILE_ATTRIBUTE_HIDDEN;
297                 break;
298             case 's':
299                 attr[mode] |= FILE_ATTRIBUTE_SYSTEM;
300                 break;
301             case 'a':
302                 attr[mode] |= FILE_ATTRIBUTE_ARCHIVE;
303                 break;
304             default:
305                 DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
306                 return 1;
307             }
308         }
309     }
310
311     if (attr[ATTR_SET] == 0 && attr[ATTR_UNSET] == 0) {
312         DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
313         return 1;
314     }
315
316     DEBUG(2, ("\nperm set %d %d\n", attr[ATTR_SET], attr[ATTR_UNSET]));
317     set_remote_attr(fname, attr[ATTR_SET], ATTR_SET);
318     set_remote_attr(fname, attr[ATTR_UNSET], ATTR_UNSET);
319     return 0;
320 }
321
322
323 /**
324  * cmd_tar - interactive command to start a tar backup/restoration
325  *
326  * Check presence of argument, parse them and handle the request.
327  */
328 int cmd_tar(void)
329 {
330     return 0;
331 }
332
333 /****************************************************************************
334 Command line (option) version
335 ***************************************************************************/
336
337 int process_tar(void)
338 {
339     return 0;
340 }
341
342 /**
343  * tar_parse_args - parse and set tar command line arguments
344  * @flag: string pointing to tar options
345  * @val: number of tar arguments
346  * @valsize: table of arguments after the flags (number of element in val)
347  *
348  * tar arguments work in a weird way. For each flag f that takes a
349  * value v, the user is supposed to type:
350  *
351  * on the CLI:
352  *   -Tf1f2f3 v1 v2 v3 TARFILE PATHS...
353  *
354  * in the interactive session:
355  *   tar f1f2f3 v1 v2 v3 TARFILE PATHS...
356  *
357  * opt has only flags (eg. "f1f2f3") and val has the arguments
358  * (values) following them (eg. ["v1", "v2", "v3", "TARFILE", "PATH1",
359  * "PATH2"]).
360  *
361  * There are only 2 flags that take an arg: b and N. The other flags
362  * just change the semantic of PATH or TARFILE.
363  *
364  * PATH can be a list of included/excluded paths, the path to a file
365  * containing a list of included/excluded paths to use (F flag). If no
366  * PATH is provided, the whole share is used (/).
367  */
368 int tar_parse_args(struct tar* t, const char *flag, const char **val, int valsize)
369 {
370     TALLOC_CTX *ctx = talloc_tos();
371
372     /* index of next value to use */
373     int ival = 0;
374
375     /*
376      * Reset back some options - could be from interactive version
377          * all other modes are left as they are
378          */
379     t->mode.operation = TAR_NO_OPERATION;
380     t->mode.selection = TAR_NO_SELECTION;
381     t->mode.dry = False;
382
383     while (*flag) {
384         switch(*flag++) {
385         /* operation */
386         case 'c':
387             if (t->mode.operation != TAR_NO_OPERATION) {
388                 printf("Tar must be followed by only one of c or x.\n");
389                 return 0;
390             }
391             t->mode.operation = TAR_CREATE;
392             break;
393         case 'x':
394             if (t->mode.operation != TAR_NO_OPERATION) {
395                 printf("Tar must be followed by only one of c or x.\n");
396                 return 0;
397             }
398             t->mode.operation = TAR_CREATE;
399             break;
400
401         /* selection  */
402         case 'I':
403             if (t->mode.selection != TAR_NO_SELECTION) {
404                 DEBUG(0,("Only one of I,X,F must be specified\n"));
405                 return 0;
406             }
407             t->mode.selection = TAR_INCLUDE;
408             break;
409         case 'X':
410             if (t->mode.selection != TAR_NO_SELECTION) {
411                 DEBUG(0,("Only one of I,X,F must be specified\n"));
412                 return 0;
413             }
414             t->mode.selection = TAR_EXCLUDE;
415             break;
416         case 'F':
417             if (t->mode.selection != TAR_NO_SELECTION) {
418                 DEBUG(0,("Only one of I,X,F must be specified\n"));
419                 return 0;
420             }
421             t->mode.selection = TAR_INCLUDE_LIST;
422             break;
423
424         /* blocksize */
425         case 'b':
426             if (ival >= valsize) {
427                 DEBUG(0, ("Option b must be followed by a blocksize\n"));
428                 return 0;
429             }
430
431             if (!tar_set_blocksize(t, atoi(val[ival]))) {
432                 DEBUG(0, ("Option b must be followed by a valid blocksize\n"));
433                 return 0;
434             }
435
436             ival++;
437             break;
438
439          /* incremental mode */
440         case 'g':
441             t->mode.incremental = True;
442             break;
443
444         /* newer than */
445         case 'N':
446             if (ival >= valsize) {
447                 DEBUG(0, ("Option N must be followed by valid file name\n"));
448                 return 0;
449             }
450
451             if (!tar_set_newer_than(t, val[ival])) {
452                 DEBUG(0,("Error setting newer-than time\n"));
453                 return 0;
454             }
455
456             ival++;
457             break;
458
459         /* reset mode */
460         case 'a':
461             t->mode.reset = True;
462             break;
463
464         /* verbose */
465         case 'q':
466             t->mode.verbose = True;
467             break;
468
469         /* regex match  */
470         case 'r':
471             t->mode.regex = True;
472             break;
473
474         /* dry run mode */
475         case 'n':
476             if (t->mode.operation != TAR_CREATE) {
477                 DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
478                 return 0;
479             }
480
481             t->mode.dry = True;
482             DEBUG(0, ("dry_run set\n"));
483             break;
484
485         default:
486             DEBUG(0,("Unknown tar option\n"));
487             return 0;
488         }
489     }
490
491     /* no operation given? default operation is include */
492     if (t->mode.operation == TAR_NO_OPERATION) {
493         t->mode.operation = TAR_INCLUDE;
494     }
495
496     if (valsize - ival < 1) {
497         DEBUG(0, ("No tar file given.\n"));
498         return 0;
499     }
500
501     /* handle TARFILE */
502     t->tar_path = talloc_strdup(ctx, val[ival]);
503     ival++;
504
505     /* handle PATHs... */
506     tar_ctx.path_list = str_list_make_empty(ctx);
507
508     /* flag F -> read file list */
509     if (t->mode.selection == TAR_INCLUDE_LIST) {
510         if (valsize - ival != 1) {
511             DEBUG(0,("Option F must be followed by exactly one filename.\n"));
512             return 0;
513         }
514
515         if(!tar_read_inclusion_file(t, val[ival])) {
516             return 0;
517         }
518         ival++;
519     }
520
521     /* otherwise store all the PATHs on the command line */
522     else {
523         int i;
524         for (i = ival; i < valsize; i++) {
525             t->path_list = str_list_add(t->path_list, val[i]);
526         }
527     }
528
529     return 1;
530 }