Imported Upstream version 3.3.3
[debian/amanda] / amar-src / amarchiver.c
1 /*
2  * Copyright (c) 2008-2012 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 "getopt.h"
24 #include "amar.h"
25
26 static struct option long_options[] = {
27     {"create"          , 0, NULL,  1},
28     {"extract"         , 0, NULL,  2},
29     {"list"            , 0, NULL,  3},
30     {"verbose"         , 0, NULL,  4},
31     {"file"            , 1, NULL,  5},
32     {"version"         , 0, NULL,  6},
33     {NULL, 0, NULL, 0}
34 };
35
36 static void
37 usage(void)
38 {
39     printf("Usage: amarchiver [--version|--create|--list|--extract] [--verbose]* [--file file]\n");
40     printf("            [filename]*\n");
41     exit(1);
42 }
43
44 static void
45 error_exit(const char *action, GError *error)
46 {
47     const char *msg = error->message? error->message : "(unknown)";
48     g_fprintf(stderr, "%s: %s\n", action, msg);
49     exit(1);
50 }
51
52 static void
53 do_create(char *opt_file, int opt_verbose, int argc, char **argv)
54 {
55     FILE *output = stdout;
56     amar_t *archive;
57     amar_file_t *file;
58     amar_attr_t *attribute;
59     GError *error = NULL;
60     int i, fd_out, fd_in;
61     off_t filesize = 0;
62
63     if (opt_file != NULL && strcmp(opt_file,"-") != 0) {
64         fd_out = open(opt_file, O_CREAT|O_WRONLY|O_TRUNC, 0660);
65         if (fd_out <= 0) {
66             error("open of '%s' failed: %s\n", opt_file, strerror(errno));
67         }
68     } else {
69         fd_out = fileno(stdout);
70         output = stderr;
71     }
72     archive = amar_new(fd_out, O_WRONLY, &error);
73     if (!archive)
74         error_exit("amar_new", error);
75
76     i = 0;
77     while (i<argc) {
78         fd_in = open(argv[i], O_RDONLY);
79         if (fd_in <= 0) {
80             g_fprintf(stderr, "open of '%s' failed: %s\n", argv[i], strerror(errno));
81             i++;
82             continue;
83         }
84         filesize = 0;
85         file = amar_new_file(archive, argv[i], strlen(argv[i]), NULL, &error);
86         if (error)
87             error_exit("amar_new_file", error);
88         attribute = amar_new_attr(file, AMAR_ATTR_GENERIC_DATA, &error);
89         if (error)
90             error_exit("amar_new_attr", error);
91
92         filesize += amar_attr_add_data_fd(attribute, fd_in, 1, &error);
93         if (error)
94             error_exit("amar_attr_add_data_fd", error);
95
96         if (!amar_attr_close(attribute, &error))
97             error_exit("amar_attr_close", error);
98         if (!amar_file_close(file, &error))
99             error_exit("amar_file_close", error);
100
101         if (opt_verbose == 1) {
102             g_fprintf(output,"%s\n", argv[i]);
103         } else if (opt_verbose > 1) {
104             g_fprintf(output,"%llu %s\n", (unsigned long long)filesize, argv[i]);
105         }
106         close(fd_in);
107         i++;
108     }
109
110     if (!amar_close(archive, &error))
111         error_exit("amar_close", error);
112     close(fd_out);
113 }
114
115 struct read_user_data {
116     gboolean verbose;
117     char **argv;
118     int argc;
119 };
120
121 static gboolean
122 extract_file_start_cb(
123         gpointer user_data,
124         uint16_t filenum G_GNUC_UNUSED,
125         gpointer filename_buf,
126         gsize filename_len,
127         gboolean *ignore G_GNUC_UNUSED,
128         gpointer *file_data)
129 {
130     struct read_user_data *ud = user_data;
131     int i;
132
133     /* keep the filename for later */
134     *file_data = g_strndup(filename_buf, filename_len);
135
136     if (ud->argc) {
137         *ignore = TRUE;
138         for (i = 0; i < ud->argc; i++) {
139             if (strlen(ud->argv[i]) == filename_len
140                 && 0 == strcmp(ud->argv[i], *file_data))
141                 *ignore = FALSE;
142         }
143     }
144
145     return TRUE;
146 }
147
148 static gboolean
149 extract_file_finish_cb(
150         gpointer user_data G_GNUC_UNUSED,
151         uint16_t filenum G_GNUC_UNUSED,
152         gpointer *file_data,
153         gboolean truncated)
154 {
155     if (truncated)
156         g_fprintf(stderr, _("Data for '%s' may have been truncated\n"),
157                 (char *)*file_data);
158
159     g_free(*file_data);
160
161     return TRUE;
162 }
163
164 static gboolean
165 extract_frag_cb(
166         gpointer user_data G_GNUC_UNUSED,
167         uint16_t filenum G_GNUC_UNUSED,
168         gpointer file_data,
169         uint16_t attrid,
170         gpointer attrid_data G_GNUC_UNUSED,
171         gpointer *attr_data,
172         gpointer data,
173         gsize datasize,
174         gboolean eoa,
175         gboolean truncated)
176 {
177     struct read_user_data *ud = user_data;
178     int fd = GPOINTER_TO_INT(*attr_data);
179
180     if (!fd) {
181         char *filename = g_strdup_printf("%s.%d", (char *)file_data, attrid);
182         fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0660);
183         if (fd < 0) {
184             g_fprintf(stderr, _("Could not open '%s' for writing: %s"),
185                     filename, strerror(errno));
186         }
187         if (ud->verbose)
188             g_fprintf(stderr, "%s\n", filename);
189         g_free(filename);
190         *attr_data = GINT_TO_POINTER(fd);
191     }
192
193     if (full_write(fd, data, datasize) != datasize) {
194         g_fprintf(stderr, _("while writing '%s.%d': %s"),
195                 (char *)file_data, attrid, strerror(errno));
196         return FALSE;
197     }
198
199     if (eoa) {
200         if (truncated) {
201             g_fprintf(stderr, _("'%s.%d' may be truncated\n"),
202                     (char *)file_data, attrid);
203         }
204         close(fd);
205     }
206
207     return TRUE;
208 }
209
210 static void
211 do_extract(
212         char *opt_file,
213         int opt_verbose,
214         int argc,
215         char **argv)
216 {
217     amar_t *archive;
218     GError *error = NULL;
219     int fd_in;
220     amar_attr_handling_t handling[] = {
221         { 0, 0, extract_frag_cb, NULL },
222     };
223     struct read_user_data ud;
224
225     ud.argv = argv;
226     ud.argc = argc;
227     ud.verbose = opt_verbose;
228
229     if (opt_file && strcmp(opt_file,"-") != 0) {
230         fd_in = open(opt_file, O_RDONLY);
231         if (fd_in <= 0) {
232             error("open of '%s' failed: %s\n", opt_file, strerror(errno));
233         }
234     } else {
235         fd_in = fileno(stdin);
236     }
237
238     archive = amar_new(fd_in, O_RDONLY, &error);
239     if (!archive)
240         error_exit("amar_new", error);
241
242     if (!amar_read(archive, &ud, handling, extract_file_start_cb,
243                    extract_file_finish_cb, &error)) {
244         if (error)
245             error_exit("amar_read", error);
246         else
247             /* one of the callbacks already printed an error message */
248             exit(1);
249     }
250 }
251
252 static gboolean
253 list_file_start_cb(
254         gpointer user_data G_GNUC_UNUSED,
255         uint16_t filenum G_GNUC_UNUSED,
256         gpointer filename_buf,
257         gsize filename_len,
258         gboolean *ignore,
259         gpointer *file_data G_GNUC_UNUSED)
260 {
261     g_printf("%.*s\n", (int)filename_len, (char *)filename_buf);
262     *ignore = TRUE;
263
264     return TRUE;
265 }
266 static void
267 do_list(
268         char *opt_file,
269         int opt_verbose G_GNUC_UNUSED)
270 {
271     amar_t *archive;
272     GError *error = NULL;
273     int fd_in;
274     amar_attr_handling_t handling[] = {
275         { 0, 0, NULL, NULL },
276     };
277
278     if (opt_file && strcmp(opt_file,"-") != 0) {
279         fd_in = open(opt_file, O_RDONLY);
280         if (fd_in <= 0) {
281             error("open of '%s' failed: %s\n", opt_file, strerror(errno));
282         }
283     } else {
284         fd_in = fileno(stdin);
285     }
286
287     archive = amar_new(fd_in, O_RDONLY, &error);
288     if (!archive)
289         error_exit("amar_new", error);
290
291     if (!amar_read(archive, NULL, handling, list_file_start_cb,
292                    NULL, &error)) {
293         if (error)
294             error_exit("amar_read", error);
295         else
296             /* one of the callbacks already printed an error message */
297             exit(1);
298     }
299 }
300
301 int main(
302     int    argc,
303     char **argv)
304 {
305     int   opt_create    = 0;
306     int   opt_extract   = 0;
307     int   opt_list      = 0;
308     int   opt_verbose   = 0;
309     char *opt_file      = NULL;
310
311     while(1) {
312         int option_index = 0;
313         int c = getopt_long (argc, argv, "", long_options, &option_index);
314         if (c == -1) {
315             break;
316         }
317         switch (c) {
318         case 1: opt_create = 1;
319                 break;
320         case 2: opt_extract = 1;
321                 break;
322         case 3: opt_list = 1;
323                 break;
324         case 4: opt_verbose += 1;
325                 break;
326         case 5: opt_file = stralloc(optarg);
327                 break;
328         case 6: printf("amarchiver %s\n", VERSION);
329                 exit(0);
330                 break;
331         }
332     }
333     argc -= optind;
334     argv += optind;
335
336     /* check those arguments */
337     if (opt_create + opt_extract + opt_list == 0) {
338         g_fprintf(stderr,"--create, --list or --extract must be provided\n");
339         usage();
340     }
341     if (opt_create + opt_extract + opt_list > 1) {
342         g_fprintf(stderr,"Only one of --create, --list or --extract must be provided\n");
343         usage();
344     }
345
346     if (opt_create > 0)
347         do_create(opt_file, opt_verbose, argc, argv);
348     else if (opt_extract > 0)
349         do_extract(opt_file, opt_verbose, argc, argv);
350     else if (opt_list > 0)
351         do_list(opt_file, opt_verbose);
352
353     return 0;
354 }