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