1 /*
2  * Copyright (c) 2008-2013 Zmanda, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17  *
18  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20  */
21 
22 #include "amanda.h"
23 #include "event.h"
24 #include "getopt.h"
25 #include "amar.h"
26 
27 static struct option long_options[] = {
28     {"create"          , 0, NULL,  1},
29     {"extract"         , 0, NULL,  2},
30     {"list"            , 0, NULL,  3},
31     {"verbose"         , 0, NULL,  4},
32     {"file"            , 1, NULL,  5},
33     {"version"         , 0, NULL,  6},
34     {NULL, 0, NULL, 0}
35 };
36 
37 static void
usage(void)38 usage(void)
39 {
40     printf("Usage: amarchiver [--version|--create|--list|--extract] [--verbose]* [--file file]\n");
41     printf("            [filename]*\n");
42     exit(1);
43 }
44 
45 static void
error_exit(const char * action,GError * error)46 error_exit(const char *action, GError *error)
47 {
48     const char *msg = error->message? error->message : "(unknown)";
49     g_fprintf(stderr, "%s: %s\n", action, msg);
50     exit(1);
51 }
52 
53 static void
do_create(char * opt_file,int opt_verbose,int argc,char ** argv)54 do_create(char *opt_file, int opt_verbose, int argc, char **argv)
55 {
56     FILE *output = stdout;
57     amar_t *archive;
58     amar_file_t *file;
59     amar_attr_t *attribute;
60     GError *error = NULL;
61     int i, fd_out, fd_in;
62     off_t filesize = 0;
63 
64     if (opt_file != NULL && strcmp(opt_file,"-") != 0) {
65 	fd_out = open(opt_file, O_CREAT|O_WRONLY|O_TRUNC, 0660);
66 	if (fd_out <= 0) {
67 	    error("open of '%s' failed: %s\n", opt_file, strerror(errno));
68 	}
69     } else {
70 	fd_out = fileno(stdout);
71 	output = stderr;
72     }
73     archive = amar_new(fd_out, O_WRONLY, &error);
74     if (!archive)
75 	error_exit("amar_new", error);
76 
77     i = 0;
78     while (i<argc) {
79 	fd_in = open(argv[i], O_RDONLY);
80 	if (fd_in <= 0) {
81 	    g_fprintf(stderr, "open of '%s' failed: %s\n", argv[i], strerror(errno));
82 	    i++;
83 	    continue;
84 	}
85 	filesize = 0;
86 	file = amar_new_file(archive, argv[i], strlen(argv[i]), NULL, &error);
87 	if (error)
88 	    error_exit("amar_new_file", error);
89 	attribute = amar_new_attr(file, AMAR_ATTR_GENERIC_DATA, &error);
90 	if (error)
91 	    error_exit("amar_new_attr", error);
92 
93 	filesize += amar_attr_add_data_fd(attribute, fd_in, 1, &error);
94 	if (error)
95 	    error_exit("amar_attr_add_data_fd", error);
96 
97 	if (!amar_attr_close(attribute, &error))
98 	    error_exit("amar_attr_close", error);
99 	if (!amar_file_close(file, &error))
100 	    error_exit("amar_file_close", error);
101 
102 	if (opt_verbose == 1) {
103 	    g_fprintf(output,"%s\n", argv[i]);
104 	} else if (opt_verbose > 1) {
105 	    g_fprintf(output,"%llu %s\n", (unsigned long long)filesize, argv[i]);
106 	}
107 	close(fd_in);
108 	i++;
109     }
110 
111     if (!amar_close(archive, &error))
112 	error_exit("amar_close", error);
113     close(fd_out);
114 }
115 
116 struct read_user_data {
117     gboolean verbose;
118     char **argv;
119     int argc;
120 };
121 
122 static gboolean
extract_file_start_cb(gpointer user_data,uint16_t filenum G_GNUC_UNUSED,gpointer filename_buf,gsize filename_len,gboolean * ignore G_GNUC_UNUSED,gpointer * file_data)123 extract_file_start_cb(
124 	gpointer user_data,
125 	uint16_t filenum G_GNUC_UNUSED,
126 	gpointer filename_buf,
127 	gsize filename_len,
128 	gboolean *ignore G_GNUC_UNUSED,
129 	gpointer *file_data)
130 {
131     struct read_user_data *ud = user_data;
132     int i;
133 
134     /* keep the filename for later */
135     *file_data = g_strndup(filename_buf, filename_len);
136 
137     if (ud->argc) {
138 	*ignore = TRUE;
139 	for (i = 0; i < ud->argc; i++) {
140 	    if (strlen(ud->argv[i]) == filename_len
141 		&& 0 == strcmp(ud->argv[i], *file_data))
142 		*ignore = FALSE;
143 	}
144     }
145 
146     return TRUE;
147 }
148 
149 static gboolean
extract_file_finish_cb(gpointer user_data G_GNUC_UNUSED,uint16_t filenum G_GNUC_UNUSED,gpointer * file_data,gboolean truncated)150 extract_file_finish_cb(
151 	gpointer user_data G_GNUC_UNUSED,
152 	uint16_t filenum G_GNUC_UNUSED,
153 	gpointer *file_data,
154 	gboolean truncated)
155 {
156     if (truncated)
157 	g_fprintf(stderr, _("Data for '%s' may have been truncated\n"),
158 		(char *)*file_data);
159 
160     g_free(*file_data);
161 
162     return TRUE;
163 }
164 
165 static int
mkpath(const char * s,mode_t mode)166 mkpath(
167     const char *s,
168     mode_t mode)
169 {
170     char *path = NULL;
171     char *r = NULL;
172     int rv = -1;
173 
174     if (strcmp(s, ".") == 0 || strcmp(s, "/") == 0)
175 	return 0;
176 
177     path = g_strdup(s);
178     r = dirname(path);
179 
180     if ((mkpath(r, mode) == -1) && (errno != EEXIST))
181 	goto out;
182 
183     if ((mkdir(s, mode) == -1) && (errno != EEXIST))
184 	rv = -1;
185     else
186 	rv = 0;
187 
188 out:
189     g_free(path);
190 
191     return rv;
192 }
193 
194 static gboolean
extract_frag_cb(gpointer user_data G_GNUC_UNUSED,uint16_t filenum G_GNUC_UNUSED,gpointer file_data,uint16_t attrid,gpointer attrid_data G_GNUC_UNUSED,gpointer * attr_data,gpointer data,gsize datasize,gboolean eoa,gboolean truncated)195 extract_frag_cb(
196 	gpointer user_data G_GNUC_UNUSED,
197 	uint16_t filenum G_GNUC_UNUSED,
198 	gpointer file_data,
199 	uint16_t attrid,
200 	gpointer attrid_data G_GNUC_UNUSED,
201 	gpointer *attr_data,
202 	gpointer data,
203 	gsize datasize,
204 	gboolean eoa,
205 	gboolean truncated)
206 {
207     struct read_user_data *ud = user_data;
208     int fd = GPOINTER_TO_INT(*attr_data);
209 
210     if (!fd) {
211 	char *filename;
212 	char *dir;
213 	if (attrid == AMAR_ATTR_GENERIC_DATA) {
214 	    filename = g_strdup((char *)file_data);
215 	} else {
216 	    filename = g_strdup_printf("%s.%d", (char *)file_data, attrid);
217 	}
218 	dir = g_strdup(filename);
219 	mkpath(dirname(dir), 0770);
220 	g_free(dir);
221 	fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0660);
222 	if (fd < 0) {
223 	    g_fprintf(stderr, _("Could not open '%s' for writing: %s"),
224 		    filename, strerror(errno));
225 	}
226 	if (ud->verbose)
227 	    g_fprintf(stderr, "%s\n", filename);
228 	g_free(filename);
229 	*attr_data = GINT_TO_POINTER(fd);
230     }
231 
232     if (full_write(fd, data, datasize) != datasize) {
233 	g_fprintf(stderr, _("while writing '%s.%d': %s"),
234 		(char *)file_data, attrid, strerror(errno));
235 	return FALSE;
236     }
237 
238     if (eoa) {
239 	if (truncated) {
240 	    g_fprintf(stderr, _("'%s.%d' may be truncated\n"),
241 		    (char *)file_data, attrid);
242 	}
243 	close(fd);
244     }
245 
246     return TRUE;
247 }
248 
249 static void
do_extract(char * opt_file,int opt_verbose,int argc,char ** argv)250 do_extract(
251 	char *opt_file,
252 	int opt_verbose,
253 	int argc,
254 	char **argv)
255 {
256     amar_t *archive;
257     GError *error = NULL;
258     int fd_in;
259     amar_attr_handling_t handling[] = {
260 	{ 0, 0, extract_frag_cb, NULL },
261     };
262     struct read_user_data ud;
263 
264     ud.argv = argv;
265     ud.argc = argc;
266     ud.verbose = opt_verbose;
267 
268     if (opt_file && strcmp(opt_file,"-") != 0) {
269 	fd_in = open(opt_file, O_RDONLY);
270 	if (fd_in <= 0) {
271 	    error("open of '%s' failed: %s\n", opt_file, strerror(errno));
272 	}
273     } else {
274 	fd_in = fileno(stdin);
275     }
276 
277     archive = amar_new(fd_in, O_RDONLY, &error);
278     if (!archive)
279 	error_exit("amar_new", error);
280 
281 //    if (!amar_read(archive, &ud, handling, extract_file_start_cb,
282 //		   extract_file_finish_cb, NULL, &error)) {
283 //	if (error)
284 //	    error_exit("amar_read", error);
285 //	else
286 //	    /* one of the callbacks already printed an error message */
287 //	    exit(1);
288 //    }
289 
290     set_amar_read_cb(archive, &ud, handling, extract_file_start_cb,
291 		     extract_file_finish_cb, NULL, &error);
292     event_loop(0);
293     if (error) {
294 	error_exit("amar_read", error);
295     }
296     amar_close(archive, NULL);
297 }
298 
299 static gboolean
list_file_start_cb(gpointer user_data G_GNUC_UNUSED,uint16_t filenum G_GNUC_UNUSED,gpointer filename_buf,gsize filename_len,gboolean * ignore,gpointer * file_data G_GNUC_UNUSED)300 list_file_start_cb(
301 	gpointer user_data G_GNUC_UNUSED,
302 	uint16_t filenum G_GNUC_UNUSED,
303 	gpointer filename_buf,
304 	gsize filename_len,
305 	gboolean *ignore,
306 	gpointer *file_data G_GNUC_UNUSED)
307 {
308     g_printf("%.*s\n", (int)filename_len, (char *)filename_buf);
309     *ignore = TRUE;
310 
311     return TRUE;
312 }
313 
314 static void
do_list(char * opt_file,int opt_verbose G_GNUC_UNUSED)315 do_list(
316 	char *opt_file,
317 	int opt_verbose G_GNUC_UNUSED)
318 {
319     amar_t *archive;
320     GError *error = NULL;
321     int fd_in;
322     amar_attr_handling_t handling[] = {
323 	{ 0, 0, NULL, NULL },
324     };
325 
326     if (opt_file && strcmp(opt_file,"-") != 0) {
327 	fd_in = open(opt_file, O_RDONLY);
328 	if (fd_in <= 0) {
329 	    error("open of '%s' failed: %s\n", opt_file, strerror(errno));
330 	}
331     } else {
332 	fd_in = fileno(stdin);
333     }
334 
335     archive = amar_new(fd_in, O_RDONLY, &error);
336     if (!archive)
337 	error_exit("amar_new", error);
338 
339     if (!amar_read(archive, NULL, handling, list_file_start_cb,
340 		   NULL, NULL, &error)) {
341 	if (error)
342 	    error_exit("amar_read", error);
343 	else
344 	    /* one of the callbacks already printed an error message */
345 	    exit(1);
346     }
347 }
348 
main(int argc,char ** argv)349 int main(
350     int    argc,
351     char **argv)
352 {
353     int   opt_create    = 0;
354     int   opt_extract   = 0;
355     int   opt_list      = 0;
356     int   opt_verbose   = 0;
357     char *opt_file      = NULL;
358 
359     while(1) {
360 	int option_index = 0;
361 	int c = getopt_long (argc, argv, "", long_options, &option_index);
362 	if (c == -1) {
363 	    break;
364 	}
365 	switch (c) {
366 	case 1: opt_create = 1;
367 		break;
368 	case 2: opt_extract = 1;
369 		break;
370 	case 3: opt_list = 1;
371 		break;
372 	case 4: opt_verbose += 1;
373 		break;
374 	case 5: opt_file = stralloc(optarg);
375 		break;
376 	case 6: printf("amarchiver %s\n", VERSION);
377 		exit(0);
378 		break;
379 	}
380     }
381     argc -= optind;
382     argv += optind;
383 
384     /* check those arguments */
385     if (opt_create + opt_extract + opt_list == 0) {
386 	g_fprintf(stderr,"--create, --list or --extract must be provided\n");
387 	usage();
388     }
389     if (opt_create + opt_extract + opt_list > 1) {
390 	g_fprintf(stderr,"Only one of --create, --list or --extract must be provided\n");
391 	usage();
392     }
393 
394     if (opt_create > 0)
395 	do_create(opt_file, opt_verbose, argc, argv);
396     else if (opt_extract > 0)
397 	do_extract(opt_file, opt_verbose, argc, argv);
398     else if (opt_list > 0)
399 	do_list(opt_file, opt_verbose);
400 
401     return 0;
402 }
403