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