/[gentoo-projects]/portage-utils/qxpak.c
Gentoo

Contents of /portage-utils/qxpak.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.25 - (show annotations) (download) (as text)
Mon Aug 13 22:23:35 2012 UTC (2 years, 3 months ago) by robbat2
Branch: MAIN
Changes since 1.24: +4 -4 lines
File MIME type: text/x-csrc
Fix compiler warnings for signedness.

1 /*
2 * Copyright 2005-2010 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/portage-utils/qxpak.c,v 1.24 2011/12/19 04:28:35 vapier Exp $
5 *
6 * Copyright 2005-2010 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2005-2010 Mike Frysinger - <vapier@gentoo.org>
8 */
9
10 #ifdef APPLET_qxpak
11
12 /*
13 # The format for a tbz2/xpak:
14 #
15 # tbz2: tar.bz2 + xpak + (xpak_offset) + "STOP"
16 # xpak: "XPAKPACK" + (index_len) + (data_len) + index + data + "XPAKSTOP"
17 # index: (pathname_len) + pathname + (data_offset) + (data_len)
18 # index entries are concatenated end-to-end.
19 # data: concatenated data chunks, end-to-end.
20 #
21 # [tarball]XPAKPACKIIIIDDDD[index][data]XPAKSTOPOOOOSTOP
22 #
23 # (integer) == encodeint(integer) ===> 4 characters (big-endian copy)
24 # '+' means concatenate the fields ===> All chunks are strings
25 */
26 #define XPAK_START_MSG "XPAKPACK"
27 #define XPAK_START_MSG_LEN 8
28 #define XPAK_START_LEN (8 + 4 + 4)
29 #define XPAK_END_MSG "XPAKSTOP"
30 #define XPAK_END_MSG_LEN 8
31
32 #define QXPAK_FLAGS "lxcd:O" COMMON_FLAGS
33 static struct option const qxpak_long_opts[] = {
34 {"list", no_argument, NULL, 'l'},
35 {"extract", no_argument, NULL, 'x'},
36 {"create", no_argument, NULL, 'c'},
37 {"dir", a_argument, NULL, 'd'},
38 {"stdout", no_argument, NULL, 'O'},
39 COMMON_LONG_OPTS
40 };
41 static const char * const qxpak_opts_help[] = {
42 "List the contents of an archive",
43 "Extract the contents of an archive",
44 "Create an archive of a directory/files",
45 "Change to specified directory",
46 "Write files to stdout",
47 COMMON_OPTS_HELP
48 };
49 static const char qxpak_rcsid[] = "$Id: qxpak.c,v 1.24 2011/12/19 04:28:35 vapier Exp $";
50 #define qxpak_usage(ret) usage(ret, QXPAK_FLAGS, qxpak_long_opts, qxpak_opts_help, lookup_applet_idx("qxpak"))
51
52 typedef struct {
53 int dir_fd;
54 FILE *fp;
55 int index_len;
56 int data_len;
57 char *index, *data;
58 } _xpak_archive;
59
60 static char xpak_stdout;
61
62 typedef void (*xpak_callback_t)(int,char*,int,int,int,char*);
63
64 static void _xpak_walk_index(_xpak_archive *x, int argc, char **argv, xpak_callback_t func)
65 {
66 int i, pathname_len, data_offset, data_len;
67 char *p, pathname[100];
68
69 p = x->index;
70 while ((p - x->index) < x->index_len) {
71 pathname_len = tbz2_decode_int((unsigned char*)p);
72 assert((size_t)pathname_len < sizeof(pathname));
73 p += 4;
74 memcpy(pathname, p, pathname_len);
75 pathname[pathname_len] = '\0';
76 if (strchr(pathname, '/') != NULL || strchr(pathname, '\\') != NULL)
77 err("Index contains a file with a path: '%s'", pathname);
78 p += pathname_len;
79 data_offset = tbz2_decode_int((unsigned char*)p);
80 p += 4;
81 data_len = tbz2_decode_int((unsigned char*)p);
82 p += 4;
83 if (argc) {
84 for (i = 0; i < argc; ++i)
85 if (argv[i] && !strcmp(pathname, argv[i])) {
86 argv[i] = NULL;
87 break;
88 }
89 if (i == argc)
90 continue;
91 }
92 (*func)(x->dir_fd, pathname, pathname_len, data_offset, data_len, x->data);
93 }
94
95 if (argc)
96 for (i = 0; i < argc; ++i)
97 if (argv[i])
98 warn("Could not locate '%s' in archive", argv[i]);
99 }
100
101 static _xpak_archive *_xpak_open(const char *file)
102 {
103 static _xpak_archive ret;
104 char buf[XPAK_START_LEN];
105
106 /* init the file */
107 memset(&ret, 0x00, sizeof(ret));
108 if (file[0] == '-' && file[1] == '\0')
109 ret.fp = stdin;
110 else if ((ret.fp = fopen(file, "r")) == NULL)
111 return NULL;
112
113 /* verify this xpak doesnt suck */
114 if (fread(buf, 1, XPAK_START_LEN, ret.fp) != XPAK_START_LEN)
115 goto close_and_ret;
116 if (memcmp(buf, XPAK_START_MSG, XPAK_START_MSG_LEN)) {
117 warn("%s: Invalid xpak", file);
118 goto close_and_ret;
119 }
120
121 /* calc index and data sizes */
122 ret.index_len = tbz2_decode_int((unsigned char*)buf+XPAK_START_MSG_LEN);
123 ret.data_len = tbz2_decode_int((unsigned char*)buf+XPAK_START_MSG_LEN+4);
124 if (!ret.index_len || !ret.data_len) {
125 warn("Skipping empty archive '%s'", file);
126 goto close_and_ret;
127 }
128
129 return &ret;
130
131 close_and_ret:
132 if (ret.fp != stdin)
133 fclose(ret.fp);
134 return NULL;
135 }
136
137 static void _xpak_close(_xpak_archive *x)
138 {
139 fclose(x->fp);
140 }
141
142 static void
143 _xpak_list_callback(_q_unused_ int dir_fd, char *pathname, _q_unused_ int pathname_len,
144 int data_offset, int data_len, _q_unused_ char *data)
145 {
146 if (!verbose)
147 puts(pathname);
148 else if (verbose == 1)
149 printf("%s: %i byte%c\n", pathname, data_len, (data_len>1?'s':' '));
150 else
151 printf("%s: %i byte%c @ offset byte %i\n",
152 pathname, data_len, (data_len>1?'s':' '), data_offset);
153 }
154 static int
155 xpak_list(int dir_fd, const char *file, int argc, char **argv)
156 {
157 _xpak_archive *x;
158 char buf[BUFSIZE];
159
160 x = _xpak_open(file);
161 if (!x)
162 return 1;
163
164 x->dir_fd = dir_fd;
165 x->index = buf;
166 assert((size_t)x->index_len < sizeof(buf));
167 assert(fread(x->index, 1, x->index_len, x->fp) == (size_t)x->index_len);
168 _xpak_walk_index(x, argc, argv, &_xpak_list_callback);
169
170 _xpak_close(x);
171
172 return 0;
173 }
174
175 static void
176 _xpak_extract_callback(int dir_fd, char *pathname, _q_unused_ int pathname_len,
177 int data_offset, int data_len, char *data)
178 {
179 FILE *out;
180
181 if (verbose == 1)
182 puts(pathname);
183 else if (verbose > 1)
184 printf("%s: %i byte%c\n", pathname, data_len, (data_len>1?'s':' '));
185
186 if (!xpak_stdout) {
187 int fd = openat(dir_fd, pathname, O_WRONLY|O_CLOEXEC|O_CREAT|O_TRUNC, 0644);
188 if (fd < 0)
189 return;
190 out = fdopen(fd, "w");
191 if (!out)
192 return;
193 } else
194 out = stdout;
195
196 fwrite(data + data_offset, 1, data_len, out);
197
198 if (!xpak_stdout)
199 fclose(out);
200 }
201 static int
202 xpak_extract(int dir_fd, const char *file, int argc, char **argv)
203 {
204 _xpak_archive *x;
205 char buf[BUFSIZE], ext[BUFSIZE*32];
206 size_t in;
207
208 x = _xpak_open(file);
209 if (!x)
210 return 1;
211
212 x->dir_fd = dir_fd;
213 x->index = buf;
214
215 assert((size_t)x->index_len < sizeof(buf));
216 in = fread(x->index, 1, x->index_len, x->fp);
217 if ((int)in != x->index_len)
218 err("index chunk: read %i bytes, wanted %i bytes", (int)in, x->index_len);
219
220 /* the xpak may be large (like when it has CONTENTS) #300744 */
221 x->data = (size_t)x->data_len < sizeof(ext) ? ext : xmalloc(x->data_len);
222 in = fread(x->data, 1, x->data_len, x->fp);
223 if ((int)in != x->data_len)
224 err("data chunk: read %i bytes, wanted %i bytes", (int)in, x->data_len);
225
226 _xpak_walk_index(x, argc, argv, &_xpak_extract_callback);
227
228 _xpak_close(x);
229
230 if (x->data != ext)
231 free(x->data);
232
233 return 0;
234 }
235
236 static void
237 _xpak_add_file(int dir_fd, const char *filename, struct stat *st, FILE *findex,
238 int *index_len, FILE *fdata, int *data_len)
239 {
240 FILE *fin;
241 unsigned char *p;
242 const char *basefile;
243 int fd, in_len;
244
245 if ((basefile = strrchr(filename, '/')) == NULL) {
246 basefile = filename;
247 } else {
248 ++basefile;
249 assert(*basefile);
250 }
251
252 if (verbose == 1)
253 printf("%s\n", basefile);
254 else if (verbose)
255 printf("%s @ offset byte %i\n", basefile, *data_len);
256
257 /* write out the (pathname_len) */
258 in_len = strlen(basefile);
259 p = tbz2_encode_int(in_len);
260 fwrite(p, 1, 4, findex);
261 /* write out the pathname */
262 fwrite(basefile, 1, in_len, findex);
263 /* write out the (data_offset) */
264 p = tbz2_encode_int(*data_len);
265 fwrite(p, 1, 4, findex);
266
267 *index_len += 4 + in_len + 4 + 4;
268
269 /* now open the file, get (data_len), and append the file to the data file */
270 fd = openat(dir_fd, filename, O_RDONLY|O_CLOEXEC);
271 if (fd < 0) {
272 open_fail:
273 warnp("could not open for reading: %s", filename);
274 fake_data_len:
275 p = tbz2_encode_int(0);
276 fwrite(p, 1, 4, findex);
277 return;
278 }
279 fin = fdopen(fd, "r");
280 if (!fin) {
281 close(fd);
282 goto open_fail;
283 }
284 in_len = st->st_size;
285 /* the xpak format can only store files whose size is a 32bit int
286 * so we have to make sure we don't store a big file */
287 if (in_len != st->st_size) {
288 warnf("File is too big: %lu", st->st_size);
289 fclose(fin);
290 goto fake_data_len;
291 }
292 p = tbz2_encode_int(in_len);
293 fwrite(p, 1, 4, findex);
294 _tbz2_copy_file(fin, fdata);
295 fclose(fin);
296
297 *data_len += in_len;
298 }
299 static int
300 xpak_create(int dir_fd, const char *file, int argc, char **argv)
301 {
302 FILE *findex, *fdata, *fout;
303 struct dirent **dir;
304 int i, fidx, numfiles;
305 struct stat st;
306 char path[_Q_PATH_MAX];
307 unsigned char *p;
308 int index_len, data_len;
309
310 if (argc == 0)
311 err("Create usage: <xpak output> <files/dirs to pack>");
312
313 if (strlen(file) >= sizeof(path)-6)
314 err("Pathname is too long: %s", file);
315
316 if ((fout = fopen(file, "w")) == NULL) {
317 warnp("could not open output: %s", file);
318 return 1;
319 }
320 strcpy(path, file); strcat(path, ".index");
321 if ((findex = fopen(path, "w+")) == NULL) {
322 warnp("could not open output: %s", path);
323 fclose(fout);
324 return 1;
325 }
326 strcpy(path, file); strcat(path, ".dat");
327 if ((fdata = fopen(path, "w+")) == NULL) {
328 warnp("could not open output: %s", path);
329 fclose(fout);
330 fclose(findex);
331 return 1;
332 }
333
334 index_len = data_len = 0;
335 for (i = 0; i < argc; ++i) {
336 if (fstatat(dir_fd, argv[i], &st, 0)) {
337 warnp("fstatat(%s) failed", argv[i]);
338 continue;
339 }
340 if (S_ISDIR(st.st_mode)) {
341 if ((numfiles = scandir(argv[i], &dir, filter_hidden, alphasort)) < 0)
342 warn("Directory '%s' is empty; skipping", argv[i]);
343 for (fidx = 0; fidx < numfiles; ++fidx) {
344 snprintf(path, sizeof(path), "%s/%s", argv[i], dir[fidx]->d_name);
345 stat(path, &st);
346 _xpak_add_file(dir_fd, path, &st, findex, &index_len, fdata, &data_len);
347 }
348 scandir_free(dir, numfiles);
349 } else if (S_ISREG(st.st_mode)) {
350 _xpak_add_file(dir_fd, argv[i], &st, findex, &index_len, fdata, &data_len);
351 } else
352 warn("Skipping non file/directory '%s'", argv[i]);
353 }
354
355 rewind(findex);
356 rewind(fdata);
357
358 /* "XPAKPACK" + (index_len) + (data_len) + index + data + "XPAKSTOP" */
359 fwrite(XPAK_START_MSG, 1, XPAK_START_MSG_LEN, fout); /* "XPAKPACK" */
360 p = tbz2_encode_int(index_len);
361 fwrite(p, 1, 4, fout); /* (index_len) */
362 p = tbz2_encode_int(data_len);
363 fwrite(p, 1, 4, fout); /* (data_len) */
364 _tbz2_copy_file(findex, fout); /* index */
365 _tbz2_copy_file(fdata, fout); /* data */
366 fwrite(XPAK_END_MSG, 1, XPAK_END_MSG_LEN, fout); /* "XPAKSTOP" */
367
368 strcpy(path, file); strcat(path, ".index"); unlink(path);
369 strcpy(path, file); strcat(path, ".dat"); unlink(path);
370 fclose(findex);
371 fclose(fdata);
372 fclose(fout);
373
374 return 0;
375 }
376
377 int qxpak_main(int argc, char **argv)
378 {
379 enum { XPAK_ACT_NONE, XPAK_ACT_LIST, XPAK_ACT_EXTRACT, XPAK_ACT_CREATE };
380 int i, ret, dir_fd;
381 char *xpak;
382 char action = XPAK_ACT_NONE;
383
384 DBG("argc=%d argv[0]=%s argv[1]=%s",
385 argc, argv[0], argc > 1 ? argv[1] : "NULL?");
386
387 dir_fd = AT_FDCWD;
388 xpak_stdout = 0;
389
390 while ((i = GETOPT_LONG(QXPAK, qxpak, "")) != -1) {
391 switch (i) {
392 COMMON_GETOPTS_CASES(qxpak)
393 case 'l': action = XPAK_ACT_LIST; break;
394 case 'x': action = XPAK_ACT_EXTRACT; break;
395 case 'c': action = XPAK_ACT_CREATE; break;
396 case 'O': xpak_stdout = 1; break;
397 case 'd':
398 if (dir_fd != AT_FDCWD)
399 err("Only use -d once");
400 dir_fd = open(optarg, O_RDONLY|O_CLOEXEC);
401 break;
402 }
403 }
404 if (optind == argc || action == XPAK_ACT_NONE)
405 qxpak_usage(EXIT_FAILURE);
406
407 xpak = argv[optind++];
408 argc -= optind;
409 argv += optind;
410
411 switch (action) {
412 case XPAK_ACT_LIST: ret = xpak_list(dir_fd, xpak, argc, argv); break;
413 case XPAK_ACT_EXTRACT: ret = xpak_extract(dir_fd, xpak, argc, argv); break;
414 case XPAK_ACT_CREATE: ret = xpak_create(dir_fd, xpak, argc, argv); break;
415 default: ret = EXIT_FAILURE;
416 }
417
418 if (dir_fd != AT_FDCWD)
419 close(dir_fd);
420
421 return ret;
422 }
423
424 #else
425 DEFINE_APPLET_STUB(qxpak)
426 #endif

  ViewVC Help
Powered by ViewVC 1.1.20