1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: amgtar.c 8888 2007-10-02 13:40:42Z martineau $
29  *
30  * send estimated backup sizes using dump
31  */
32 
33 /* PROPERTY:
34  *
35  * GNUTAR-PATH     (default GNUTAR)
36  * GNUTAR-LISTDIR  (default CNF_GNUTAR_LIST_DIR)
37  * DIRECTORY       (no default, if set, the backup will be from that directory
38  *		    instead of from the --device)
39  * ONE-FILE-SYSTEM (default YES)
40  * SPARSE          (default YES)
41  * ATIME-PRESERVE  (default YES)
42  * CHECK-DEVICE    (default YES)
43  * NO-UNQUOTE      (default NO)
44  * ACLS            (default NO)
45  * SELINUX         (default NO)
46  * XATTRS          (default NO)
47  * INCLUDE-FILE
48  * INCLUDE-LIST
49  * INCLUDE-LIST-GLOB
50  * INCLUDE-OPTIONAL
51  * EXCLUDE-FILE
52  * EXCLUDE-LIST
53  * EXCLUDE-LIST-GLOB
54  * EXCLUDE-OPTIONAL
55  * NORMAL
56  * IGNORE
57  * STRANGE
58  * EXIT-HANDLING   (1=GOOD 2=BAD)
59  * TAR-BLOCKSIZE   (default does not add --blocking-factor option,
60  *                  using tar's default)
61  * VERBOSE
62  */
63 
64 #include "amanda.h"
65 #include "match.h"
66 #include "pipespawn.h"
67 #include "amfeatures.h"
68 #include "clock.h"
69 #include "util.h"
70 #include "getfsent.h"
71 #include "client_util.h"
72 #include "conffile.h"
73 #include "getopt.h"
74 #include "security-file.h"
75 
76 int debug_application = 1;
77 #define application_debug(i, ...) do {	\
78 	if ((i) <= debug_application) {	\
79 	    dbprintf(__VA_ARGS__);	\
80 	}				\
81 } while (0)
82 
83 static amregex_t init_re_table[] = {
84   /* tar prints the size in bytes */
85   AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
86   AM_NORMAL_RE("^could not open conf file"),
87   AM_NORMAL_RE("^Elapsed time:"),
88   AM_NORMAL_RE("^Throughput"),
89   AM_IGNORE_RE(": Directory is new$"),
90   AM_IGNORE_RE(": Directory has been renamed"),
91 
92   /* GNU tar 1.27 */
93   AM_NORMAL_RE(": directory is on a different filesystem; not dumped"),
94 
95   /* GNU tar 1.13.17 will print this warning when (not) backing up a
96      Unix named socket.  */
97   AM_NORMAL_RE(": socket ignored$"),
98 
99   /* GNUTAR produces a few error messages when files are modified or
100      removed while it is running.  They may cause data to be lost, but
101      then they may not.  We shouldn't consider them NORMAL until
102      further investigation.  */
103   AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
104   AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
105   AM_NORMAL_RE(": Error exit delayed from previous errors"),
106 
107   /* catch-all: DMP_STRANGE is returned for all other lines */
108   AM_STRANGE_RE(NULL)
109 };
110 static amregex_t *re_table;
111 
112 /* local functions */
113 int main(int argc, char **argv);
114 
115 typedef struct application_argument_s {
116     char      *config;
117     char      *host;
118     int        message;
119     int        collection;
120     int        calcsize;
121     char      *tar_blocksize;
122     GSList    *level;
123     GSList    *command_options;
124     char      *include_list_glob;
125     char      *exclude_list_glob;
126     dle_t      dle;
127     int        argc;
128     char     **argv;
129     int        verbose;
130     int        ignore_zeros;
131 } application_argument_t;
132 
133 enum { CMD_ESTIMATE, CMD_BACKUP };
134 
135 static void amgtar_support(application_argument_t *argument);
136 static void amgtar_selfcheck(application_argument_t *argument);
137 static void amgtar_estimate(application_argument_t *argument);
138 static void amgtar_backup(application_argument_t *argument);
139 static void amgtar_restore(application_argument_t *argument);
140 static void amgtar_validate(application_argument_t *argument);
141 static void amgtar_build_exinclude(dle_t *dle, int verbose,
142 				   int *nb_exclude, char **file_exclude,
143 				   int *nb_include, char **file_include);
144 static char *amgtar_get_incrname(application_argument_t *argument, int level,
145 				 FILE *mesgstream, int command);
146 static void check_no_check_device(void);
147 static GPtrArray *amgtar_build_argv(char *gnutar_realpath,
148 				application_argument_t *argument,
149 				char *incrname, char **file_exclude,
150 				char **file_include, int command);
151 static char *gnutar_path;
152 static char *gnutar_listdir;
153 static char *gnutar_directory;
154 static int gnutar_onefilesystem;
155 static int gnutar_atimepreserve;
156 static int gnutar_acls;
157 static int gnutar_selinux;
158 static int gnutar_xattrs;
159 static int gnutar_checkdevice;
160 static int gnutar_no_unquote;
161 static int gnutar_sparse;
162 static int gnutar_sparse_set = 0;
163 static GSList *normal_message = NULL;
164 static GSList *ignore_message = NULL;
165 static GSList *strange_message = NULL;
166 static char   *exit_handling;
167 static int    exit_value[256];
168 
169 static struct option long_options[] = {
170     {"config"          , 1, NULL,  1},
171     {"host"            , 1, NULL,  2},
172     {"disk"            , 1, NULL,  3},
173     {"device"          , 1, NULL,  4},
174     {"level"           , 1, NULL,  5},
175     {"index"           , 1, NULL,  6},
176     {"message"         , 1, NULL,  7},
177     {"collection"      , 0, NULL,  8},
178     {"record"          , 0, NULL,  9},
179     {"gnutar-path"     , 1, NULL, 10},
180     {"gnutar-listdir"  , 1, NULL, 11},
181     {"one-file-system" , 1, NULL, 12},
182     {"sparse"          , 1, NULL, 13},
183     {"atime-preserve"  , 1, NULL, 14},
184     {"check-device"    , 1, NULL, 15},
185     {"include-file"    , 1, NULL, 16},
186     {"include-list"    , 1, NULL, 17},
187     {"include-optional", 1, NULL, 18},
188     {"exclude-file"    , 1, NULL, 19},
189     {"exclude-list"    , 1, NULL, 20},
190     {"exclude-optional", 1, NULL, 21},
191     {"directory"       , 1, NULL, 22},
192     {"normal"          , 1, NULL, 23},
193     {"ignore"          , 1, NULL, 24},
194     {"strange"         , 1, NULL, 25},
195     {"exit-handling"   , 1, NULL, 26},
196     {"calcsize"        , 0, NULL, 27},
197     {"tar-blocksize"   , 1, NULL, 28},
198     {"no-unquote"      , 1, NULL, 29},
199     {"acls"            , 1, NULL, 30},
200     {"selinux"         , 1, NULL, 31},
201     {"xattrs"          , 1, NULL, 32},
202     {"command-options" , 1, NULL, 33},
203     {"include-list-glob", 1, NULL, 34},
204     {"exclude-list-glob", 1, NULL, 35},
205     {"verbose"          , 1, NULL, 36},
206     {"ignore-zeros"     , 1, NULL, 37},
207     {NULL, 0, NULL, 0}
208 };
209 
210 static char *
escape_tar_glob(char * str,int * in_argv)211 escape_tar_glob(
212     char *str,
213     int  *in_argv)
214 {
215     char *result = malloc(4*strlen(str)+1);
216     char *r = result;
217     char *s;
218 
219     *in_argv = 0;
220     for (s = str; *s != '\0'; s++) {
221 	if (*s == '\\') {
222 	    char c = *(s+1);
223 	    if (c == '\\') {
224 		*r++ = '\\';
225 		*r++ = '\\';
226 		*r++ = '\\';
227 		s++;
228 	    } else if (c == '?') {
229 		*r++ = 127;
230 		s++;
231 		continue;
232 	    } else if (c == 'a') {
233 		*r++ = 7;
234 		s++;
235 		continue;
236 	    } else if (c == 'b') {
237 		*r++ = 8;
238 		s++;
239 		continue;
240 	    } else if (c == 'f') {
241 		*r++ = 12;
242 		s++;
243 		continue;
244 	    } else if (c == 'n') {
245 		*r++ = 10;
246 		s++;
247 		*in_argv = 1;
248 		continue;
249 	    } else if (c == 'r') {
250 		*r++ = 13;
251 		s++;
252 		*in_argv = 1;
253 		continue;
254 	    } else if (c == 't') {
255 		*r++ = 9;
256 		s++;
257 		continue;
258 	    } else if (c == 'v') {
259 		*r++ = 11;
260 		s++;
261 		continue;
262 	    } else if (c >= '0' && c <= '9') {
263 		char d = c-'0';
264 		s++;
265 		c = *(s+1);
266 		if (c >= '0' && c <= '9') {
267 		    d = (d*8)+(c-'0');
268 		    s++;
269 		    c = *(s+1);
270 		    if (c >= '0' && c <= '9') {
271 			d = (d*8)+(c-'0');
272 			s++;
273 		    }
274 		}
275 		*r++ = d;
276 		continue;
277 	    } else {
278 		*r++ = '\\';
279 	    }
280 	} else if (*s == '?') {
281 	    *r++ = '\\';
282 	    *r++ = '\\';
283 	} else if (*s == '*' || *s == '[') {
284 	    *r++ = '\\';
285 	}
286 	*r++ = *s;
287     }
288     *r = '\0';
289 
290     return result;
291 }
292 
293 
294 int
main(int argc,char ** argv)295 main(
296     int		argc,
297     char **	argv)
298 {
299     int c;
300     char *command;
301     application_argument_t argument;
302     int i;
303 
304 #ifdef GNUTAR
305     gnutar_path = GNUTAR;
306 #else
307     gnutar_path = NULL;
308 #endif
309     gnutar_listdir = NULL;
310     gnutar_directory = NULL;
311     gnutar_onefilesystem = 1;
312     gnutar_atimepreserve = 1;
313     gnutar_acls = 0;
314     gnutar_selinux = 0;
315     gnutar_xattrs = 0;
316     gnutar_checkdevice = 1;
317     gnutar_sparse = 1;
318     gnutar_no_unquote = 0;
319     exit_handling = NULL;
320 
321     /* initialize */
322 
323     /*
324      * Configure program for internationalization:
325      *   1) Only set the message locale for now.
326      *   2) Set textdomain for all amanda related programs to "amanda"
327      *      We don't want to be forced to support dozens of message catalogs.
328      */
329     setlocale(LC_MESSAGES, "C");
330     textdomain("amanda");
331 
332     if (argc < 2) {
333         printf("ERROR no command given to amgtar\n");
334         error(_("No command given to amgtar"));
335     }
336 
337     /* drop root privileges */
338     if (!set_root_privs(0)) {
339 	if (strcmp(argv[1], "selfcheck") == 0) {
340 	    printf("ERROR amgtar must be run setuid root\n");
341 	}
342 	error(_("amgtar must be run setuid root"));
343     }
344 
345     safe_fd(3, 2);
346 
347     set_pname("amgtar");
348 
349     /* Don't die when child closes pipe */
350     signal(SIGPIPE, SIG_IGN);
351 
352 #if defined(USE_DBMALLOC)
353     malloc_size_1 = malloc_inuse(&malloc_hist_1);
354 #endif
355 
356     add_amanda_log_handler(amanda_log_stderr);
357     add_amanda_log_handler(amanda_log_syslog);
358     dbopen(DBG_SUBDIR_CLIENT);
359     startclock();
360     dbprintf(_("version %s\n"), VERSION);
361 
362     config_init(CONFIG_INIT_CLIENT, NULL);
363 
364     //check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
365     //root for amrecover
366     //RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
367 
368     /* parse argument */
369     command = argv[1];
370 
371     argument.config     = NULL;
372     argument.host       = NULL;
373     argument.message    = 0;
374     argument.collection = 0;
375     argument.calcsize   = 0;
376     argument.tar_blocksize = NULL;
377     argument.level      = NULL;
378     argument.command_options = NULL;
379     argument.include_list_glob = NULL;
380     argument.exclude_list_glob = NULL;
381     argument.verbose = 0;
382     argument.ignore_zeros = 1;
383     init_dle(&argument.dle);
384     argument.dle.record = 0;
385 
386     while (1) {
387 	int option_index = 0;
388     	c = getopt_long (argc, argv, "", long_options, &option_index);
389 	if (c == -1) {
390 	    break;
391 	}
392 	switch (c) {
393 	case 1: argument.config = stralloc(optarg);
394 		break;
395 	case 2: argument.host = stralloc(optarg);
396 		break;
397 	case 3: argument.dle.disk = stralloc(optarg);
398 		break;
399 	case 4: argument.dle.device = stralloc(optarg);
400 		break;
401 	case 5: argument.level = g_slist_append(argument.level,
402 					        GINT_TO_POINTER(atoi(optarg)));
403 		break;
404 	case 6: argument.dle.create_index = 1;
405 		break;
406 	case 7: argument.message = 1;
407 		break;
408 	case 8: argument.collection = 1;
409 		break;
410 	case 9: argument.dle.record = 1;
411 		break;
412 	case 10: gnutar_path = stralloc(optarg);
413 		 break;
414 	case 11: gnutar_listdir = stralloc(optarg);
415 		 break;
416 	case 12: if (optarg && strcasecmp(optarg, "NO") == 0)
417 		     gnutar_onefilesystem = 0;
418 		 else if (optarg && strcasecmp(optarg, "YES") == 0)
419 		     gnutar_onefilesystem = 1;
420 		 else if (strcasecmp(command, "selfcheck") == 0)
421 		     printf(_("ERROR [%s: bad ONE-FILE-SYSTEM property value (%s)]\n"), get_pname(), optarg);
422 		 break;
423 	case 13: gnutar_sparse_set = 1;
424 		 if (optarg && strcasecmp(optarg, "NO") == 0)
425 		     gnutar_sparse = 0;
426 		 else if (optarg && strcasecmp(optarg, "YES") == 0)
427 		     gnutar_sparse = 1;
428 		 else if (strcasecmp(command, "selfcheck") == 0)
429 		     printf(_("ERROR [%s: bad SPARSE property value (%s)]\n"), get_pname(), optarg);
430 		 break;
431 	case 14: if (optarg && strcasecmp(optarg, "NO") == 0)
432 		     gnutar_atimepreserve = 0;
433 		 else if (optarg && strcasecmp(optarg, "YES") == 0)
434 		     gnutar_atimepreserve = 1;
435 		 else if (strcasecmp(command, "selfcheck") == 0)
436 		     printf(_("ERROR [%s: bad ATIME-PRESERVE property value (%s)]\n"), get_pname(), optarg);
437 		 break;
438 	case 15: if (optarg && strcasecmp(optarg, "NO") == 0)
439 		     gnutar_checkdevice = 0;
440 		 else if (optarg && strcasecmp(optarg, "YES") == 0)
441 		     gnutar_checkdevice = 1;
442 		 else if (strcasecmp(command, "selfcheck") == 0)
443 		     printf(_("ERROR [%s: bad CHECK-DEVICE property value (%s)]\n"), get_pname(), optarg);
444 		 break;
445 	case 16: if (optarg)
446 		     argument.dle.include_file =
447 			 append_sl(argument.dle.include_file, optarg);
448 		 break;
449 	case 17: if (optarg)
450 		     argument.dle.include_list =
451 			 append_sl(argument.dle.include_list, optarg);
452 		 break;
453 	case 18: argument.dle.include_optional = 1;
454 		 break;
455 	case 19: if (optarg)
456 		     argument.dle.exclude_file =
457 			 append_sl(argument.dle.exclude_file, optarg);
458 		 break;
459 	case 20: if (optarg)
460 		     argument.dle.exclude_list =
461 			 append_sl(argument.dle.exclude_list, optarg);
462 		 break;
463 	case 21: argument.dle.exclude_optional = 1;
464 		 break;
465 	case 22: gnutar_directory = stralloc(optarg);
466 		 break;
467 	case 23: if (optarg)
468 		     normal_message =
469 			 g_slist_append(normal_message, optarg);
470 		 break;
471 	case 24: if (optarg)
472 		     ignore_message =
473 			 g_slist_append(ignore_message, optarg);
474 		 break;
475 	case 25: if (optarg)
476 		     strange_message =
477 			 g_slist_append(strange_message, optarg);
478 		 break;
479 	case 26: if (optarg)
480 		     exit_handling = stralloc(optarg);
481 		 break;
482 	case 27: argument.calcsize = 1;
483 		 break;
484 	case 28: argument.tar_blocksize = stralloc(optarg);
485 		 break;
486 	case 29: if (optarg && strcasecmp(optarg, "NO") == 0)
487 		     gnutar_no_unquote = 0;
488 		 else if (optarg && strcasecmp(optarg, "YES") == 0)
489 		     gnutar_no_unquote = 1;
490 		 else if (strcasecmp(command, "selfcheck") == 0)
491 		     printf(_("ERROR [%s: bad No_UNQUOTE property value (%s)]\n"), get_pname(), optarg);
492 		 break;
493         case 30: if (optarg && strcasecmp(optarg, "YES") == 0)
494                    gnutar_acls = 1;
495                  break;
496         case 31: if (optarg && strcasecmp(optarg, "YES") == 0)
497                    gnutar_selinux = 1;
498                  break;
499         case 32: if (optarg && strcasecmp(optarg, "YES") == 0)
500                    gnutar_xattrs = 1;
501                  break;
502 	case 33: argument.command_options =
503 			g_slist_append(argument.command_options,
504 				       stralloc(optarg));
505 		 break;
506 	case 34: if (optarg)
507 		     argument.include_list_glob = stralloc(optarg);
508 		 break;
509 	case 35: if (optarg)
510 		     argument.exclude_list_glob = stralloc(optarg);
511 		 break;
512 	case 36: if (optarg  && strcasecmp(optarg, "YES") == 0)
513 		     argument.verbose = 1;
514 		 break;
515 	case 37: if (strcasecmp(optarg, "YES") != 0)
516 		     argument.ignore_zeros = 0;
517 		 break;
518 	case ':':
519 	case '?':
520 		break;
521 	}
522     }
523 
524     if (!argument.dle.disk && argument.dle.device)
525 	argument.dle.disk = stralloc(argument.dle.device);
526     if (!argument.dle.device && argument.dle.disk)
527 	argument.dle.device = stralloc(argument.dle.disk);
528 
529     argument.argc = argc - optind;
530     argument.argv = argv + optind;
531 
532     if (argument.config) {
533 	config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
534 		    argument.config);
535 	dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
536     }
537 
538     if (config_errors(NULL) >= CFGERR_ERRORS) {
539 	g_critical(_("errors processing config file"));
540     }
541 
542     if (!gnutar_listdir) {
543 	gnutar_listdir = g_strdup(getconf_str(CNF_GNUTAR_LIST_DIR));
544     }
545 
546     re_table = build_re_table(init_re_table, normal_message, ignore_message,
547 			      strange_message);
548 
549     for(i=0;i<256;i++)
550 	exit_value[i] = 1; /* BAD  */
551     exit_value[0] = 0;     /* GOOD */
552     exit_value[1] = 0;     /* GOOD */
553     if (exit_handling) {
554 	char *s = exit_handling;
555 	while (s) {
556 	    char *r = strchr(s, '=');
557 	    if (r) {
558 		int j = atoi(s);
559 		if (j >= 0 && j < 256) {
560 		    r++;
561 		    if (strncasecmp(r, "GOOD", 4) == 0) {
562 			exit_value[j] = 0;
563 		    }
564 		}
565 	    }
566 	    s = strchr(s+1, ' ');
567 	}
568     }
569 
570     if (strlen(gnutar_listdir) == 0)
571 	gnutar_listdir = NULL;
572 
573     if (gnutar_path) {
574 	dbprintf("GNUTAR-PATH %s\n", gnutar_path);
575     } else {
576 	dbprintf("GNUTAR-PATH is not set\n");
577     }
578     if (gnutar_listdir) {
579 	    dbprintf("GNUTAR-LISTDIR %s\n", gnutar_listdir);
580     } else {
581 	dbprintf("GNUTAR-LISTDIR is not set\n");
582     }
583     if (gnutar_directory) {
584 	dbprintf("DIRECTORY %s\n", gnutar_directory);
585     }
586     dbprintf("ONE-FILE-SYSTEM %s\n", gnutar_onefilesystem? "yes":"no");
587     dbprintf("SPARSE %s\n", gnutar_sparse? "yes":"no");
588     dbprintf("NO-UNQUOTE %s\n", gnutar_no_unquote? "yes":"no");
589     dbprintf("ATIME-PRESERVE %s\n", gnutar_atimepreserve? "yes":"no");
590     dbprintf("ACLS %s\n", gnutar_acls? "yes":"no");
591     dbprintf("SELINUX %s\n", gnutar_selinux? "yes":"no");
592     dbprintf("XATTRS %s\n", gnutar_xattrs? "yes":"no");
593     dbprintf("CHECK-DEVICE %s\n", gnutar_checkdevice? "yes":"no");
594     {
595 	amregex_t *rp;
596 	for (rp = re_table; rp->regex != NULL; rp++) {
597 	    switch (rp->typ) {
598 		case DMP_NORMAL : dbprintf("NORMAL %s\n", rp->regex); break;
599 		case DMP_IGNORE : dbprintf("IGNORE %s\n", rp->regex); break;
600 		case DMP_STRANGE: dbprintf("STRANGE %s\n", rp->regex); break;
601 		case DMP_SIZE   : dbprintf("SIZE %s\n", rp->regex); break;
602 		case DMP_ERROR  : dbprintf("ERROR %s\n", rp->regex); break;
603 	    }
604 	}
605     }
606 
607     if (strcmp(command, "support") == 0) {
608 	amgtar_support(&argument);
609     } else if (strcmp(command, "selfcheck") == 0) {
610 	amgtar_selfcheck(&argument);
611     } else if (strcmp(command, "estimate") == 0) {
612 	amgtar_estimate(&argument);
613     } else if (strcmp(command, "backup") == 0) {
614 	amgtar_backup(&argument);
615     } else if (strcmp(command, "restore") == 0) {
616 	amgtar_restore(&argument);
617     } else if (strcmp(command, "validate") == 0) {
618 	amgtar_validate(&argument);
619     } else {
620 	dbprintf("Unknown command `%s'.\n", command);
621 	fprintf(stderr, "Unknown command `%s'.\n", command);
622 	exit (1);
623     }
624 
625     dbclose();
626 
627     return 0;
628 }
629 
validate_command_options(application_argument_t * argument)630 static char *validate_command_options(
631     application_argument_t *argument)
632 {
633     GSList    *copt;
634 
635     for (copt = argument->command_options; copt != NULL; copt = copt->next) {
636 	char *opt = (char *)copt->data;
637 
638 	if (g_str_has_prefix(opt, "--rsh-command") ||
639 	    g_str_has_prefix(opt,"--to-command") ||
640 	    g_str_has_prefix(opt,"--info-script") ||
641 	    g_str_has_prefix(opt,"--new-volume-script") ||
642 	    g_str_has_prefix(opt,"--rmt-command") ||
643 	    g_str_has_prefix(opt,"--use-compress-program")) {
644 	    return opt;
645 	}
646     }
647 
648     return NULL;
649 }
650 
651 static void
amgtar_support(application_argument_t * argument)652 amgtar_support(
653     application_argument_t *argument)
654 {
655     (void)argument;
656     fprintf(stdout, "CONFIG YES\n");
657     fprintf(stdout, "HOST YES\n");
658     fprintf(stdout, "DISK YES\n");
659     fprintf(stdout, "MAX-LEVEL 399\n");
660     fprintf(stdout, "INDEX-LINE YES\n");
661     fprintf(stdout, "INDEX-XML NO\n");
662     fprintf(stdout, "MESSAGE-LINE YES\n");
663     fprintf(stdout, "MESSAGE-XML NO\n");
664     fprintf(stdout, "RECORD YES\n");
665     fprintf(stdout, "INCLUDE-FILE YES\n");
666     fprintf(stdout, "INCLUDE-LIST YES\n");
667     fprintf(stdout, "INCLUDE-LIST-GLOB YES\n");
668     fprintf(stdout, "INCLUDE-OPTIONAL YES\n");
669     fprintf(stdout, "EXCLUDE-FILE YES\n");
670     fprintf(stdout, "EXCLUDE-LIST YES\n");
671     fprintf(stdout, "EXCLUDE-LIST-GLOB YES\n");
672     fprintf(stdout, "EXCLUDE-OPTIONAL YES\n");
673     fprintf(stdout, "COLLECTION NO\n");
674     fprintf(stdout, "MULTI-ESTIMATE YES\n");
675     fprintf(stdout, "CALCSIZE YES\n");
676     fprintf(stdout, "CLIENT-ESTIMATE YES\n");
677 }
678 
679 static void
amgtar_selfcheck(application_argument_t * argument)680 amgtar_selfcheck(
681     application_argument_t *argument)
682 {
683     char *option;
684 
685     if (argument->dle.disk) {
686 	char *qdisk = quote_string(argument->dle.disk);
687 	fprintf(stdout, "OK disk %s\n", qdisk);
688 	amfree(qdisk);
689     }
690 
691     printf("OK amgtar version %s\n", VERSION);
692     amgtar_build_exinclude(&argument->dle, 1, NULL, NULL, NULL, NULL);
693 
694     printf("OK amgtar\n");
695 
696 
697     if ((option = validate_command_options(argument))) {
698 	fprintf(stdout, "ERROR [Invalid '%s' COMMAND-OPTIONS\n", option);
699     }
700 
701     if (gnutar_path) {
702 	char *gnutar_realpath = NULL;
703 	if (check_file(gnutar_path, X_OK) &&
704 	    check_exec_for_suid("GNUTAR_PATH", gnutar_path, stdout, &gnutar_realpath)) {
705 	    char *gtar_version;
706 	    GPtrArray *argv_ptr = g_ptr_array_new();
707 
708 	    g_ptr_array_add(argv_ptr, gnutar_realpath);
709 	    g_ptr_array_add(argv_ptr, "--version");
710 	    g_ptr_array_add(argv_ptr, NULL);
711 
712 	    gtar_version = get_first_line(argv_ptr);
713 	    if (gtar_version) {
714 		char *gv;
715 		for (gv = gtar_version; *gv && !g_ascii_isdigit(*gv); gv++);
716 		printf("OK amgtar gtar-version %s\n", gv);
717 	    } else {
718 		printf(_("ERROR [Can't get %s version]\n"), gnutar_realpath);
719 	    }
720 
721 	    g_ptr_array_free(argv_ptr, TRUE);
722 	    amfree(gtar_version);
723 	}
724 	amfree(gnutar_realpath);
725     } else {
726 	printf(_("ERROR [GNUTAR program not available]\n"));
727     }
728     if (gnutar_listdir && strlen(gnutar_listdir) == 0)
729 	gnutar_listdir = NULL;
730     if (gnutar_listdir) {
731 	check_dir(gnutar_listdir, R_OK|W_OK);
732     } else {
733 	printf(_("ERROR [No GNUTAR-LISTDIR]\n"));
734     }
735 
736     set_root_privs(1);
737     if (gnutar_directory) {
738 	check_dir(gnutar_directory, R_OK);
739     } else if (argument->dle.device) {
740 	check_dir(argument->dle.device, R_OK);
741     }
742     if (argument->calcsize) {
743 	char *calcsize = vstralloc(amlibexecdir, "/", "calcsize", NULL);
744 	check_file(calcsize, X_OK);
745 	check_suid(calcsize);
746 	amfree(calcsize);
747     }
748     set_root_privs(0);
749 }
750 
751 static void
amgtar_estimate(application_argument_t * argument)752 amgtar_estimate(
753     application_argument_t *argument)
754 {
755     char      *incrname = NULL;
756     GPtrArray *argv_ptr;
757     int        nullfd = -1;
758     int        pipefd = -1;
759     FILE      *dumpout = NULL;
760     off_t      size = -1;
761     char       line[32768];
762     char      *errmsg = NULL;
763     char      *qerrmsg = NULL;
764     char      *qdisk;
765     amwait_t   wait_status;
766     int        tarpid;
767     amregex_t *rp;
768     times_t    start_time;
769     int        level;
770     GSList    *levels;
771     char      *file_exclude;
772     char      *file_include;
773     char      *option;
774     char      *gnutar_realpath = NULL;
775 
776     if (!argument->level) {
777         fprintf(stderr, "ERROR No level argument\n");
778         error(_("No level argument"));
779     }
780     if (!argument->dle.disk) {
781         fprintf(stderr, "ERROR No disk argument\n");
782         error(_("No disk argument"));
783     }
784     if (!argument->dle.device) {
785         fprintf(stderr, "ERROR No device argument\n");
786         error(_("No device argument"));
787     }
788 
789     qdisk = quote_string(argument->dle.disk);
790 
791     if ((option = validate_command_options(argument))) {
792 	fprintf(stderr, "ERROR Invalid '%s' COMMAND-OPTIONS\n", option);
793 	error("Invalid '%s' COMMAND-OPTIONS", option);
794     }
795 
796     if (argument->calcsize) {
797 	char *dirname;
798 	int   nb_exclude;
799 	int   nb_include;
800 
801 	if (gnutar_directory) {
802 	    dirname = gnutar_directory;
803 	} else {
804 	    dirname = argument->dle.device;
805 	}
806 
807 	amgtar_build_exinclude(&argument->dle, 1,
808 			       &nb_exclude, &file_exclude,
809 			       &nb_include, &file_include);
810 
811 	run_calcsize(argument->config, "GNUTAR", argument->dle.disk, dirname,
812 		     argument->level, file_exclude, file_include);
813 
814 	if (argument->verbose == 0) {
815 	    if (file_exclude)
816 		unlink(file_exclude);
817 	    if (file_include)
818 		unlink(file_include);
819         }
820 	return;
821     }
822 
823     if (!gnutar_path) {
824 	errmsg = vstrallocf(_("GNUTAR-PATH not defined"));
825 	goto common_error;
826     }
827 
828     if (!check_exec_for_suid("GNUTAR_PATH", gnutar_path, stderr, &gnutar_realpath)) {
829 	errmsg = g_strdup_printf("'%s' binary is not secure", gnutar_path);
830 	goto common_error;
831     }
832 
833     if (!gnutar_listdir) {
834 	errmsg = vstrallocf(_("GNUTAR-LISTDIR not defined"));
835 	goto common_error;
836     }
837 
838     for (levels = argument->level; levels != NULL; levels = levels->next) {
839 	level = GPOINTER_TO_INT(levels->data);
840 	incrname = amgtar_get_incrname(argument, level, stdout, CMD_ESTIMATE);
841 	argv_ptr = amgtar_build_argv(gnutar_realpath,
842 				     argument, incrname, &file_exclude,
843 				     &file_include, CMD_ESTIMATE);
844 
845 	start_time = curclock();
846 
847 	if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
848 	    errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
849 				strerror(errno));
850 	    goto common_exit;
851 	}
852 
853 	tarpid = pipespawnv(gnutar_realpath, STDERR_PIPE, 1,
854 			    &nullfd, &nullfd, &pipefd,
855 			    (char **)argv_ptr->pdata);
856 
857 	dumpout = fdopen(pipefd,"r");
858 	if (!dumpout) {
859 	    error(_("Can't fdopen: %s"), strerror(errno));
860 	    /*NOTREACHED*/
861 	}
862 
863 	size = (off_t)-1;
864 	while (size < 0 && (fgets(line, sizeof(line), dumpout) != NULL)) {
865 	    if (line[strlen(line)-1] == '\n') /* remove trailling \n */
866 		line[strlen(line)-1] = '\0';
867 	    if (line[0] == '\0')
868 		continue;
869 	    dbprintf("%s\n", line);
870 	    /* check for size match */
871 	    /*@ignore@*/
872 	    for(rp = re_table; rp->regex != NULL; rp++) {
873 		if(match(rp->regex, line)) {
874 		    if (rp->typ == DMP_SIZE) {
875 			size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
876 			if(size < 0.0)
877 			    size = 1.0;             /* found on NeXT -- sigh */
878 		    }
879 		    break;
880 		}
881 	    }
882 	    /*@end@*/
883 	}
884 
885 	while (fgets(line, sizeof(line), dumpout) != NULL) {
886 	    dbprintf("%s", line);
887 	}
888 
889 	dbprintf(".....\n");
890 	dbprintf(_("estimate time for %s level %d: %s\n"),
891 		 qdisk,
892 		 level,
893 		 walltime_str(timessub(curclock(), start_time)));
894 	if(size == (off_t)-1) {
895 	    errmsg = vstrallocf(_("no size line match in %s output"), gnutar_realpath);
896 	    dbprintf(_("%s for %s\n"), errmsg, qdisk);
897 	    dbprintf(".....\n");
898 	} else if(size == (off_t)0 && argument->level == 0) {
899 	    dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
900 		     gnutar_realpath, argument->dle.disk);
901 	    dbprintf(".....\n");
902 	}
903 	dbprintf(_("estimate size for %s level %d: %lld KB\n"),
904 		 qdisk,
905 		 level,
906 		 (long long)size);
907 
908 	kill(-tarpid, SIGTERM);
909 
910 	dbprintf(_("waiting for %s \"%s\" child\n"), gnutar_realpath, qdisk);
911 	waitpid(tarpid, &wait_status, 0);
912 	if (WIFSIGNALED(wait_status)) {
913 	    errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
914 				gnutar_realpath, WTERMSIG(wait_status), dbfn());
915 	} else if (WIFEXITED(wait_status)) {
916 	    if (exit_value[WEXITSTATUS(wait_status)] == 1) {
917 		errmsg = vstrallocf(_("%s exited with status %d: see %s"),
918 				    gnutar_realpath, WEXITSTATUS(wait_status), dbfn());
919 	    } else {
920 		/* Normal exit */
921 	    }
922 	} else {
923 	    errmsg = vstrallocf(_("%s got bad exit: see %s"),
924 				gnutar_realpath, dbfn());
925 	}
926 	dbprintf(_("after %s %s wait\n"), gnutar_realpath, qdisk);
927 
928 common_exit:
929 	if (errmsg) {
930 	    dbprintf("%s", errmsg);
931 	    fprintf(stdout, "ERROR %s\n", errmsg);
932 	}
933 
934 	if (incrname) {
935 	    unlink(incrname);
936 	}
937 
938 	if (argument->verbose == 0) {
939 	    if (file_exclude)
940 		unlink(file_exclude);
941 	    if (file_include)
942 		unlink(file_include);
943         }
944 
945 	g_ptr_array_free_full(argv_ptr);
946 
947 	aclose(nullfd);
948 	afclose(dumpout);
949 
950 	fprintf(stdout, "%d %lld 1\n", level, (long long)size);
951     }
952     amfree(qdisk);
953     amfree(gnutar_realpath);
954     return;
955 
956 common_error:
957     qerrmsg = quote_string(errmsg);
958     amfree(qdisk);
959     dbprintf("%s", errmsg);
960     fprintf(stdout, "ERROR %s\n", qerrmsg);
961     amfree(errmsg);
962     amfree(qerrmsg);
963     amfree(gnutar_realpath);
964     return;
965 }
966 
967 static void
amgtar_backup(application_argument_t * argument)968 amgtar_backup(
969     application_argument_t *argument)
970 {
971     int         dumpin;
972     char      *qdisk;
973     char      *incrname;
974     char       line[32768];
975     amregex_t *rp;
976     off_t      dump_size = -1;
977     char      *type;
978     char       startchr;
979     int        dataf = 1;
980     int        mesgf = 3;
981     int        indexf = 4;
982     int        outf;
983     FILE      *mesgstream;
984     FILE      *indexstream = NULL;
985     FILE      *outstream;
986     char      *errmsg = NULL;
987     amwait_t   wait_status;
988     GPtrArray *argv_ptr;
989     int        tarpid;
990     char      *file_exclude;
991     char      *file_include;
992     char      *option;
993     char      *gnutar_realpath = NULL;
994 
995     mesgstream = fdopen(mesgf, "w");
996     if (!mesgstream) {
997 	error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
998     }
999 
1000     if (!gnutar_path) {
1001         fprintf(mesgstream, "? GNUTAR-PATH not defined\n");
1002 	error(_("GNUTAR-PATH not defined"));
1003     }
1004     if (!gnutar_listdir) {
1005         fprintf(mesgstream, "? GNUTAR-LISTDIR not defined\n");
1006 	error(_("GNUTAR-LISTDIR not defined"));
1007     }
1008 
1009     if (!argument->level) {
1010         fprintf(mesgstream, "? No level argument\n");
1011         error(_("No level argument"));
1012     }
1013     if (!argument->dle.disk) {
1014         fprintf(mesgstream, "? No disk argument\n");
1015         error(_("No disk argument"));
1016     }
1017     if (!argument->dle.device) {
1018         fprintf(mesgstream, "? No device argument\n");
1019         error(_("No device argument"));
1020     }
1021 
1022     if (!check_exec_for_suid("GNUTAR_PATH", gnutar_path, NULL, &gnutar_realpath)) {
1023 	fprintf(mesgstream, "? '%s' binary is not secure\n", gnutar_path);
1024 	error("'%s' binary is not secure", gnutar_path);
1025     }
1026 
1027     if ((option = validate_command_options(argument))) {
1028 	fprintf(mesgstream, "? Invalid '%s' COMMAND-OPTIONS\n", option);
1029 	error("Invalid '%s' COMMAND-OPTIONS", option);
1030     }
1031 
1032     qdisk = quote_string(argument->dle.disk);
1033 
1034     incrname = amgtar_get_incrname(argument,
1035 				   GPOINTER_TO_INT(argument->level->data),
1036 				   mesgstream, CMD_BACKUP);
1037     argv_ptr = amgtar_build_argv(gnutar_realpath,
1038 				 argument, incrname, &file_exclude,
1039 				 &file_include, CMD_BACKUP);
1040 
1041     tarpid = pipespawnv(gnutar_realpath, STDIN_PIPE|STDERR_PIPE, 1,
1042 			&dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
1043     /* close the write ends of the pipes */
1044 
1045     aclose(dumpin);
1046     aclose(dataf);
1047     if (argument->dle.create_index) {
1048 	indexstream = fdopen(indexf, "w");
1049 	if (!indexstream) {
1050 	    error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
1051 	}
1052     }
1053     outstream = fdopen(outf, "r");
1054     if (!outstream) {
1055 	error(_("error outstream(%d): %s\n"), outf, strerror(errno));
1056     }
1057 
1058     while (fgets(line, sizeof(line), outstream) != NULL) {
1059 	if (line[strlen(line)-1] == '\n') /* remove trailling \n */
1060 	    line[strlen(line)-1] = '\0';
1061 	if (*line == '.' && *(line+1) == '/') { /* filename */
1062 	    if (argument->dle.create_index) {
1063 		fprintf(indexstream, "%s\n", &line[1]); /* remove . */
1064 	    }
1065 	} else { /* message */
1066 	    for(rp = re_table; rp->regex != NULL; rp++) {
1067 		if(match(rp->regex, line)) {
1068 		    break;
1069 		}
1070 	    }
1071 	    if(rp->typ == DMP_SIZE) {
1072 		dump_size = (off_t)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
1073 	    }
1074 	    switch(rp->typ) {
1075 	    case DMP_NORMAL:
1076 		type = "normal";
1077 		startchr = '|';
1078 		break;
1079 	    case DMP_IGNORE:
1080 		continue;
1081 	    case DMP_STRANGE:
1082 		type = "strange";
1083 		startchr = '?';
1084 		break;
1085 	    case DMP_SIZE:
1086 		type = "size";
1087 		startchr = '|';
1088 		break;
1089 	    case DMP_ERROR:
1090 		type = "error";
1091 		startchr = '?';
1092 		break;
1093 	    default:
1094 		type = "unknown";
1095 		startchr = '!';
1096 		break;
1097 	    }
1098 	    dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
1099 	    fprintf(mesgstream,"%c %s\n", startchr, line);
1100         }
1101     }
1102 
1103     waitpid(tarpid, &wait_status, 0);
1104     if (WIFSIGNALED(wait_status)) {
1105 	errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1106 			    gnutar_realpath, WTERMSIG(wait_status), dbfn());
1107     } else if (WIFEXITED(wait_status)) {
1108 	if (exit_value[WEXITSTATUS(wait_status)] == 1) {
1109 	    errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1110 				gnutar_realpath, WEXITSTATUS(wait_status), dbfn());
1111 	} else {
1112 	    /* Normal exit */
1113 	}
1114     } else {
1115 	errmsg = vstrallocf(_("%s got bad exit: see %s"),
1116 			    gnutar_realpath, dbfn());
1117     }
1118     dbprintf(_("after %s %s wait\n"), gnutar_realpath, qdisk);
1119     dbprintf(_("amgtar: %s: pid %ld\n"), gnutar_realpath, (long)tarpid);
1120     if (errmsg) {
1121 	dbprintf("%s", errmsg);
1122 	g_fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
1123     }
1124 
1125     if (!errmsg && incrname && strlen(incrname) > 4) {
1126 	if (argument->dle.record) {
1127 	    char *nodotnew;
1128 	    nodotnew = stralloc(incrname);
1129 	    nodotnew[strlen(nodotnew)-4] = '\0';
1130 	    if (rename(incrname, nodotnew)) {
1131 		dbprintf(_("%s: warning [renaming %s to %s: %s]\n"),
1132 			 get_pname(), incrname, nodotnew, strerror(errno));
1133 		g_fprintf(mesgstream, _("? warning [renaming %s to %s: %s]\n"),
1134 			  incrname, nodotnew, strerror(errno));
1135 	    }
1136 	    amfree(nodotnew);
1137 	} else {
1138 	    if (unlink(incrname) == -1) {
1139 		dbprintf(_("%s: warning [unlink %s: %s]\n"),
1140 			 get_pname(), incrname, strerror(errno));
1141 		g_fprintf(mesgstream, _("? warning [unlink %s: %s]\n"),
1142 			  incrname, strerror(errno));
1143 	    }
1144 	}
1145     }
1146 
1147     dbprintf("sendbackup: size %lld\n", (long long)dump_size);
1148     fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
1149     dbprintf("sendbackup: end\n");
1150     fprintf(mesgstream, "sendbackup: end\n");
1151 
1152     if (argument->dle.create_index)
1153 	fclose(indexstream);
1154 
1155     fclose(mesgstream);
1156 
1157     if (argument->verbose == 0) {
1158 	if (file_exclude)
1159 	    unlink(file_exclude);
1160 	if (file_include)
1161 	    unlink(file_include);
1162     }
1163 
1164     amfree(incrname);
1165     amfree(qdisk);
1166     amfree(gnutar_realpath);
1167     g_ptr_array_free_full(argv_ptr);
1168 }
1169 
1170 static void
amgtar_restore(application_argument_t * argument)1171 amgtar_restore(
1172     application_argument_t *argument)
1173 {
1174     GPtrArray  *argv_ptr = g_ptr_array_new();
1175     char      **env;
1176     int         j;
1177     char       *e;
1178     char       *include_filename = NULL;
1179     char       *exclude_filename = NULL;
1180     char       *gnutar_realpath = NULL;
1181 
1182     if (!gnutar_path) {
1183 	error(_("GNUTAR-PATH not defined"));
1184     }
1185 
1186     if (!check_exec_for_suid("GNUTAR_PATH", gnutar_path, NULL, &gnutar_realpath)) {
1187         error("'%s' binary is not secure", gnutar_path);
1188     }
1189 
1190     if (!security_allow_to_restore(NULL)) {
1191 	error("The user is not allowed to restore files");
1192     }
1193 
1194     g_ptr_array_add(argv_ptr, stralloc(gnutar_realpath));
1195     g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1196     if (gnutar_no_unquote)
1197 	g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1198     if (gnutar_acls)
1199 	g_ptr_array_add(argv_ptr, stralloc("--acls"));
1200     if (gnutar_selinux)
1201 	g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1202     if (gnutar_xattrs)
1203 	g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1204     /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1205     if (argument->ignore_zeros) {
1206 	g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1207     }
1208     if (argument->tar_blocksize) {
1209 	g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1210 	g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1211     }
1212     g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1213     g_ptr_array_add(argv_ptr, stralloc("-"));
1214     if (gnutar_directory) {
1215 	struct stat stat_buf;
1216 	if(stat(gnutar_directory, &stat_buf) != 0) {
1217 	    fprintf(stderr,"can not stat directory %s: %s\n", gnutar_directory, strerror(errno));
1218 	    exit(1);
1219 	}
1220 	if (!S_ISDIR(stat_buf.st_mode)) {
1221 	    fprintf(stderr,"%s is not a directory\n", gnutar_directory);
1222 	    exit(1);
1223 	}
1224 	if (access(gnutar_directory, W_OK) != 0) {
1225 	    fprintf(stderr, "Can't write to %s: %s\n", gnutar_directory, strerror(errno));
1226 	    exit(1);
1227 	}
1228 	g_ptr_array_add(argv_ptr, stralloc("--directory"));
1229 	g_ptr_array_add(argv_ptr, stralloc(gnutar_directory));
1230     }
1231 
1232     g_ptr_array_add(argv_ptr, stralloc("--wildcards"));
1233     if (argument->dle.exclude_list &&
1234 	argument->dle.exclude_list->nb_element == 1) {
1235 	FILE      *exclude;
1236 	char      *sdisk;
1237 	int        in_argv;
1238 	int        entry_in_exclude = 0;
1239 	char       line[2*PATH_MAX];
1240 	FILE      *exclude_list;
1241 
1242 	if (argument->dle.disk) {
1243 	    sdisk = sanitise_filename(argument->dle.disk);
1244 	} else {
1245 	    sdisk = g_strdup_printf("no_dle-%d", (int)getpid());
1246 	}
1247 	exclude_filename= vstralloc(AMANDA_TMPDIR, "/", "exclude-", sdisk,  NULL);
1248 	exclude_list = fopen(argument->dle.exclude_list->first->name, "r");
1249 
1250 	exclude = fopen(exclude_filename, "w");
1251 	while (fgets(line, 2*PATH_MAX, exclude_list)) {
1252 	    char *escaped;
1253 	    line[strlen(line)-1] = '\0'; /* remove '\n' */
1254 	    escaped = escape_tar_glob(line, &in_argv);
1255 	    if (in_argv) {
1256 		g_ptr_array_add(argv_ptr, "--exclude");
1257 		g_ptr_array_add(argv_ptr, escaped);
1258 	    } else {
1259 		fprintf(exclude,"%s\n", escaped);
1260 		entry_in_exclude++;
1261 		amfree(escaped);
1262 	    }
1263 	}
1264 	fclose(exclude);
1265 	g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1266 	g_ptr_array_add(argv_ptr, exclude_filename);
1267     }
1268 
1269     if (argument->exclude_list_glob) {
1270 	g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1271 	g_ptr_array_add(argv_ptr, stralloc(argument->exclude_list_glob));
1272     }
1273 
1274     {
1275 	GPtrArray *argv_include = g_ptr_array_new();
1276 	FILE      *include;
1277 	char      *sdisk;
1278 	int        in_argv;
1279 	guint      i;
1280 	int        entry_in_include = 0;
1281 
1282 	if (argument->dle.disk) {
1283 	    sdisk = sanitise_filename(argument->dle.disk);
1284 	} else {
1285 	    sdisk = g_strdup_printf("no_dle-%d", (int)getpid());
1286 	}
1287 	include_filename = vstralloc(AMANDA_TMPDIR, "/", "include-", sdisk,  NULL);
1288 	include = fopen(include_filename, "w");
1289 	if (argument->dle.include_list &&
1290 	    argument->dle.include_list->nb_element == 1) {
1291 	    char line[2*PATH_MAX];
1292 	    FILE *include_list = fopen(argument->dle.include_list->first->name, "r");
1293 	    while (fgets(line, 2*PATH_MAX, include_list)) {
1294 		char *escaped;
1295 		line[strlen(line)-1] = '\0'; /* remove '\n' */
1296 		escaped = escape_tar_glob(line, &in_argv);
1297 		if (in_argv) {
1298 		    g_ptr_array_add(argv_include, escaped);
1299 		} else {
1300 		    fprintf(include,"%s\n", escaped);
1301 		    entry_in_include++;
1302 		    amfree(escaped);
1303 		}
1304 	    }
1305 	}
1306 
1307 	for (j=1; j< argument->argc; j++) {
1308 	    char *escaped = escape_tar_glob(argument->argv[j], &in_argv);
1309 	    if (in_argv) {
1310 		g_ptr_array_add(argv_include, escaped);
1311 	    } else {
1312 		fprintf(include,"%s\n", escaped);
1313 		entry_in_include++;
1314 		amfree(escaped);
1315 	    }
1316 	}
1317 	fclose(include);
1318 
1319 	if (entry_in_include) {
1320 	    g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1321 	    g_ptr_array_add(argv_ptr, include_filename);
1322 	}
1323 
1324 	if (argument->include_list_glob) {
1325 	    g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1326 	    g_ptr_array_add(argv_ptr, stralloc(argument->include_list_glob));
1327 	}
1328 
1329 	for (i = 0; i < argv_include->len; i++) {
1330 	    g_ptr_array_add(argv_ptr, (char *)g_ptr_array_index(argv_include,i));
1331 	}
1332     }
1333     g_ptr_array_add(argv_ptr, NULL);
1334 
1335     debug_executing(argv_ptr);
1336 
1337     env = safe_env();
1338     become_root();
1339     execve(gnutar_realpath, (char **)argv_ptr->pdata, env);
1340     e = strerror(errno);
1341     error(_("error [exec %s: %s]"), gnutar_realpath, e);
1342 }
1343 
1344 static void
amgtar_validate(application_argument_t * argument G_GNUC_UNUSED)1345 amgtar_validate(
1346     application_argument_t *argument G_GNUC_UNUSED)
1347 {
1348     char       *cmd;
1349     GPtrArray  *argv_ptr = g_ptr_array_new();
1350     char      **env;
1351     char       *e;
1352     char        buf[32768];
1353 
1354     if (!gnutar_path) {
1355 	dbprintf("GNUTAR-PATH not set; Piping to /dev/null\n");
1356 	fprintf(stderr,"GNUTAR-PATH not set; Piping to /dev/null\n");
1357 	goto pipe_to_null;
1358     }
1359 
1360     cmd = stralloc(gnutar_path);
1361     g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1362     /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1363     g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1364     g_ptr_array_add(argv_ptr, stralloc("-tf"));
1365     g_ptr_array_add(argv_ptr, stralloc("-"));
1366     g_ptr_array_add(argv_ptr, NULL);
1367 
1368     debug_executing(argv_ptr);
1369     env = safe_env();
1370     execve(cmd, (char **)argv_ptr->pdata, env);
1371     e = strerror(errno);
1372     dbprintf("failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1373     fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1374 pipe_to_null:
1375     while (read(0, buf, 32768) > 0) {
1376     }
1377 }
1378 
1379 static void
amgtar_build_exinclude(dle_t * dle,int verbose,int * nb_exclude,char ** file_exclude,int * nb_include,char ** file_include)1380 amgtar_build_exinclude(
1381     dle_t  *dle,
1382     int     verbose,
1383     int    *nb_exclude,
1384     char  **file_exclude,
1385     int    *nb_include,
1386     char  **file_include)
1387 {
1388     int n_exclude = 0;
1389     int n_include = 0;
1390     char *exclude = NULL;
1391     char *include = NULL;
1392 
1393     if (dle->exclude_file) n_exclude += dle->exclude_file->nb_element;
1394     if (dle->exclude_list) n_exclude += dle->exclude_list->nb_element;
1395     if (dle->include_file) n_include += dle->include_file->nb_element;
1396     if (dle->include_list) n_include += dle->include_list->nb_element;
1397 
1398     if (n_exclude > 0) exclude = build_exclude(dle, verbose);
1399     if (n_include > 0) include = build_include(dle, verbose);
1400 
1401     if (nb_exclude)
1402 	*nb_exclude = n_exclude;
1403     if (file_exclude)
1404 	*file_exclude = exclude;
1405     else
1406 	amfree(exclude);
1407 
1408     if (nb_include)
1409 	*nb_include = n_include;
1410     if (file_include)
1411 	*file_include = include;
1412     else
1413 	amfree(include);
1414 }
1415 
1416 static char *
amgtar_get_incrname(application_argument_t * argument,int level,FILE * mesgstream,int command)1417 amgtar_get_incrname(
1418     application_argument_t *argument,
1419     int                     level,
1420     FILE                   *mesgstream,
1421     int                     command)
1422 {
1423     char *basename = NULL;
1424     char *incrname = NULL;
1425     int   infd, outfd;
1426     ssize_t   nb;
1427     char *inputname = NULL;
1428     char *errmsg = NULL;
1429     char *buf;
1430 
1431     if (gnutar_listdir) {
1432 	char number[NUM_STR_SIZE];
1433 	int baselevel;
1434 	char *sdisk = sanitise_filename(argument->dle.disk);
1435 
1436 	basename = vstralloc(gnutar_listdir,
1437 			     "/",
1438 			     argument->host,
1439 			     sdisk,
1440 			     NULL);
1441 	amfree(sdisk);
1442 
1443 	snprintf(number, SIZEOF(number), "%d", level);
1444 	incrname = vstralloc(basename, "_", number, ".new", NULL);
1445 	unlink(incrname);
1446 
1447 	/*
1448 	 * Open the listed incremental file from the previous level.  Search
1449 	 * backward until one is found.  If none are found (which will also
1450 	 * be true for a level 0), arrange to read from /dev/null.
1451 	 */
1452 	baselevel = level;
1453 	infd = -1;
1454 	while (infd == -1) {
1455 	    if (--baselevel >= 0) {
1456 		snprintf(number, SIZEOF(number), "%d", baselevel);
1457 		inputname = newvstralloc(inputname,
1458 					 basename, "_", number, NULL);
1459 	    } else {
1460 		inputname = newstralloc(inputname, "/dev/null");
1461 	    }
1462 	    if ((infd = open(inputname, O_RDONLY)) == -1) {
1463 
1464 		errmsg = vstrallocf(_("amgtar: error opening %s: %s"),
1465 				     inputname, strerror(errno));
1466 		dbprintf("%s\n", errmsg);
1467 		if (baselevel < 0) {
1468 		    if (command == CMD_ESTIMATE) {
1469 			fprintf(mesgstream, "ERROR %s\n", errmsg);
1470 		    } else {
1471 			fprintf(mesgstream, "? %s\n", errmsg);
1472 		    }
1473 		    exit(1);
1474 		}
1475 		amfree(errmsg);
1476 	    }
1477 	}
1478 
1479 	/*
1480 	 * Copy the previous listed incremental file to the new one.
1481 	 */
1482 	if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1483 	    errmsg = vstrallocf(_("error opening %s: %s"),
1484 			         incrname, strerror(errno));
1485 	    dbprintf("%s\n", errmsg);
1486 	    if (command == CMD_ESTIMATE) {
1487 		fprintf(mesgstream, "ERROR %s\n", errmsg);
1488 	    } else {
1489 		fprintf(mesgstream, "? %s\n", errmsg);
1490 	    }
1491 	    exit(1);
1492 	}
1493 
1494 	while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1495 	    if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1496 		errmsg = vstrallocf(_("writing to %s: %s"),
1497 				     incrname, strerror(errno));
1498 		dbprintf("%s\n", errmsg);
1499 		if (command == CMD_ESTIMATE) {
1500 		    fprintf(mesgstream, "ERROR %s\n", errmsg);
1501 		} else {
1502 		    fprintf(mesgstream, "? %s\n", errmsg);
1503 		}
1504 		exit(1);
1505 	    }
1506 	}
1507 
1508 	if (nb < 0) {
1509 	    errmsg = vstrallocf(_("reading from %s: %s"),
1510 			         inputname, strerror(errno));
1511 	    dbprintf("%s\n", errmsg);
1512 	    if (command == CMD_ESTIMATE) {
1513 		fprintf(mesgstream, "ERROR %s\n", errmsg);
1514 	    } else {
1515 		fprintf(mesgstream, "? %s\n", errmsg);
1516 	    }
1517 	    exit(1);
1518 	}
1519 
1520 	if (close(infd) != 0) {
1521 	    errmsg = vstrallocf(_("closing %s: %s"),
1522 			         inputname, strerror(errno));
1523 	    dbprintf("%s\n", errmsg);
1524 	    if (command == CMD_ESTIMATE) {
1525 		fprintf(mesgstream, "ERROR %s\n", errmsg);
1526 	    } else {
1527 		fprintf(mesgstream, "? %s\n", errmsg);
1528 	    }
1529 	    exit(1);
1530 	}
1531 	if (close(outfd) != 0) {
1532 	    errmsg = vstrallocf(_("closing %s: %s"),
1533 			         incrname, strerror(errno));
1534 	    dbprintf("%s\n", errmsg);
1535 	    dbprintf("%s\n", errmsg);
1536 	    if (command == CMD_ESTIMATE) {
1537 		fprintf(mesgstream, "ERROR %s\n", errmsg);
1538 	    } else {
1539 		fprintf(mesgstream, "? %s\n", errmsg);
1540 	    }
1541 	    exit(1);
1542 	}
1543 
1544 	amfree(inputname);
1545 	amfree(basename);
1546     }
1547     return incrname;
1548 }
1549 
1550 static void
check_no_check_device(void)1551 check_no_check_device(void)
1552 {
1553     if (gnutar_checkdevice == 0) {
1554 	GPtrArray *argv_ptr = g_ptr_array_new();
1555 	int dumpin;
1556 	int dataf;
1557 	int outf;
1558 	int size;
1559 	char buf[32768];
1560 
1561 	g_ptr_array_add(argv_ptr, gnutar_path);
1562 	g_ptr_array_add(argv_ptr, "-x");
1563 	g_ptr_array_add(argv_ptr, "--no-check-device");
1564 	g_ptr_array_add(argv_ptr, "-f");
1565 	g_ptr_array_add(argv_ptr, "-");
1566 	g_ptr_array_add(argv_ptr, NULL);
1567 
1568 	pipespawnv(gnutar_path, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
1569 			     &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
1570 	aclose(dumpin);
1571 	aclose(dataf);
1572 	size = read(outf, buf, 32767);
1573 	if (size > 0) {
1574 	    buf[size] = '\0';
1575 	    if (strstr(buf, "--no-check-device")) {
1576 		g_debug("disabling --no-check-device since '%s' doesn't support it", gnutar_path);
1577 		gnutar_checkdevice = 1;
1578 	    }
1579 	}
1580 	aclose(outf);
1581 	g_ptr_array_free(argv_ptr, TRUE);
1582     }
1583 }
1584 
amgtar_build_argv(char * gnutar_realpath,application_argument_t * argument,char * incrname,char ** file_exclude,char ** file_include,int command)1585 GPtrArray *amgtar_build_argv(
1586     char *gnutar_realpath,
1587     application_argument_t *argument,
1588     char  *incrname,
1589     char **file_exclude,
1590     char **file_include,
1591     int    command)
1592 {
1593     int    nb_exclude;
1594     int    nb_include;
1595     char  *dirname;
1596     char   tmppath[PATH_MAX];
1597     GPtrArray *argv_ptr = g_ptr_array_new();
1598     GSList    *copt;
1599 
1600     check_no_check_device();
1601     amgtar_build_exinclude(&argument->dle, 1,
1602 			   &nb_exclude, file_exclude,
1603 			   &nb_include, file_include);
1604 
1605     if (gnutar_directory) {
1606 	dirname = gnutar_directory;
1607     } else {
1608 	dirname = argument->dle.device;
1609     }
1610 
1611     g_ptr_array_add(argv_ptr, stralloc(gnutar_realpath));
1612 
1613     g_ptr_array_add(argv_ptr, stralloc("--create"));
1614     if (command == CMD_BACKUP && argument->dle.create_index)
1615         g_ptr_array_add(argv_ptr, stralloc("--verbose"));
1616     g_ptr_array_add(argv_ptr, stralloc("--file"));
1617     if (command == CMD_ESTIMATE) {
1618         g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
1619     } else {
1620         g_ptr_array_add(argv_ptr, stralloc("-"));
1621     }
1622     if (gnutar_no_unquote)
1623 	g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1624     g_ptr_array_add(argv_ptr, stralloc("--directory"));
1625     canonicalize_pathname(dirname, tmppath);
1626     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1627     if (gnutar_onefilesystem)
1628 	g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
1629     if (gnutar_atimepreserve)
1630 	g_ptr_array_add(argv_ptr, stralloc("--atime-preserve=system"));
1631     if (!gnutar_checkdevice)
1632 	g_ptr_array_add(argv_ptr, stralloc("--no-check-device"));
1633     if (gnutar_acls)
1634 	g_ptr_array_add(argv_ptr, stralloc("--acls"));
1635     if (gnutar_selinux)
1636 	g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1637     if (gnutar_xattrs)
1638 	g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1639     g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
1640     g_ptr_array_add(argv_ptr, stralloc(incrname));
1641     if (gnutar_sparse) {
1642 	if (!gnutar_sparse_set) {
1643 	    char  *gtar_version;
1644 	    char  *minor_version;
1645 	    char  *sminor_version;
1646 	    char  *gv;
1647 	    int    major;
1648 	    int    minor;
1649 	    GPtrArray *version_ptr = g_ptr_array_new();
1650 
1651 	    g_ptr_array_add(version_ptr, gnutar_path);
1652 	    g_ptr_array_add(version_ptr, "--version");
1653 	    g_ptr_array_add(version_ptr, NULL);
1654 	    gtar_version = get_first_line(version_ptr);
1655 	    if (gtar_version) {
1656 		for (gv = gtar_version; *gv && !g_ascii_isdigit(*gv); gv++);
1657 		minor_version = index(gtar_version, '.');
1658 		if (minor_version) {
1659 		    *minor_version++ = '\0';
1660 		    sminor_version = index(minor_version, '.');
1661 		    if (sminor_version) {
1662 			*sminor_version = '\0';
1663 		    }
1664 		    major = atoi(gv);
1665 		    minor = atoi(minor_version);
1666 		    if (major < 1 ||
1667 			(major == 1 && minor < 28)) {
1668 			gnutar_sparse = 0;
1669 		    }
1670 		}
1671 	    }
1672 	    g_ptr_array_free(version_ptr, TRUE);
1673 	    amfree(gtar_version);
1674 	}
1675 	if (gnutar_sparse) {
1676 	    g_ptr_array_add(argv_ptr, g_strdup("--sparse"));
1677 	}
1678     }
1679     if (argument->tar_blocksize) {
1680 	g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1681 	g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1682     }
1683     g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
1684     g_ptr_array_add(argv_ptr, stralloc("--totals"));
1685 
1686     for (copt = argument->command_options; copt != NULL; copt = copt->next) {
1687 	g_ptr_array_add(argv_ptr, stralloc((char *)copt->data));
1688     }
1689 
1690     if (*file_exclude) {
1691 	g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1692 	g_ptr_array_add(argv_ptr, stralloc(*file_exclude));
1693     }
1694 
1695     if (*file_include) {
1696 	g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1697 	g_ptr_array_add(argv_ptr, stralloc(*file_include));
1698     }
1699     else {
1700 	g_ptr_array_add(argv_ptr, stralloc("."));
1701     }
1702     g_ptr_array_add(argv_ptr, NULL);
1703 
1704     return(argv_ptr);
1705 }
1706 
1707