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