]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libarchive/archive_write_set_format_ar.c
Mark a few additional functions that are/are not available on FreeBSD.
[FreeBSD/FreeBSD.git] / lib / libarchive / archive_write_set_format_ar.c
1 /*-
2  * Copyright (c) 2007 Kai Wang
3  * Copyright (c) 2007 Tim Kientzle
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer
11  *    in this position and unchanged.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "archive_platform.h"
29 __FBSDID("$FreeBSD$");
30
31 #ifdef HAVE_ERRNO_H
32 #include <errno.h>
33 #endif
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
40
41 #include "archive.h"
42 #include "archive_entry.h"
43 #include "archive_private.h"
44 #include "archive_write_private.h"
45
46 struct ar_w {
47         uint64_t         entry_bytes_remaining;
48         uint64_t         entry_padding;
49         int              is_strtab;
50         int              has_strtab;
51         char            *strtab;
52 };
53
54 /*
55  * Define structure of the "ar" header.
56  */
57 #define AR_name_offset 0
58 #define AR_name_size 16
59 #define AR_date_offset 16
60 #define AR_date_size 12
61 #define AR_uid_offset 28
62 #define AR_uid_size 6
63 #define AR_gid_offset 34
64 #define AR_gid_size 6
65 #define AR_mode_offset 40
66 #define AR_mode_size 8
67 #define AR_size_offset 48
68 #define AR_size_size 10
69 #define AR_fmag_offset 58
70 #define AR_fmag_size 2
71
72 static int               archive_write_set_format_ar(struct archive_write *);
73 static int               archive_write_ar_header(struct archive_write *,
74                              struct archive_entry *);
75 static ssize_t           archive_write_ar_data(struct archive_write *,
76                              const void *buff, size_t s);
77 static int               archive_write_ar_destroy(struct archive_write *);
78 static int               archive_write_ar_finish(struct archive_write *);
79 static int               archive_write_ar_finish_entry(struct archive_write *);
80 static const char       *ar_basename(const char *path);
81 static int               format_octal(int64_t v, char *p, int s);
82 static int               format_decimal(int64_t v, char *p, int s);
83
84 int
85 archive_write_set_format_ar_bsd(struct archive *_a)
86 {
87         struct archive_write *a = (struct archive_write *)_a;
88         int r = archive_write_set_format_ar(a);
89         if (r == ARCHIVE_OK) {
90                 a->archive_format = ARCHIVE_FORMAT_AR_BSD;
91                 a->archive_format_name = "ar (BSD)";
92         }
93         return (r);
94 }
95
96 int
97 archive_write_set_format_ar_svr4(struct archive *_a)
98 {
99         struct archive_write *a = (struct archive_write *)_a;
100         int r = archive_write_set_format_ar(a);
101         if (r == ARCHIVE_OK) {
102                 a->archive_format = ARCHIVE_FORMAT_AR_GNU;
103                 a->archive_format_name = "ar (GNU/SVR4)";
104         }
105         return (r);
106 }
107
108 /*
109  * Generic initialization.
110  */
111 static int
112 archive_write_set_format_ar(struct archive_write *a)
113 {
114         struct ar_w *ar;
115
116         /* If someone else was already registered, unregister them. */
117         if (a->format_destroy != NULL)
118                 (a->format_destroy)(a);
119
120         ar = (struct ar_w *)malloc(sizeof(*ar));
121         if (ar == NULL) {
122                 archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");
123                 return (ARCHIVE_FATAL);
124         }
125         memset(ar, 0, sizeof(*ar));
126         a->format_data = ar;
127
128         a->format_write_header = archive_write_ar_header;
129         a->format_write_data = archive_write_ar_data;
130         a->format_finish = archive_write_ar_finish;
131         a->format_destroy = archive_write_ar_destroy;
132         a->format_finish_entry = archive_write_ar_finish_entry;
133         return (ARCHIVE_OK);
134 }
135
136 static int
137 archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
138 {
139         int ret, append_fn;
140         char buff[60];
141         char *ss, *se;
142         struct ar_w *ar;
143         const char *pathname;
144         const char *filename;
145
146         ret = 0;
147         append_fn = 0;
148         ar = (struct ar_w *)a->format_data;
149         ar->is_strtab = 0;
150         filename = NULL;
151
152         /*
153          * Reject files with empty name.
154          */
155         pathname = archive_entry_pathname(entry);
156         if (*pathname == '\0') {
157                 archive_set_error(&a->archive, EINVAL,
158                     "Invalid filename");
159                 return (ARCHIVE_WARN);
160         }
161
162         /*
163          * If we are now at the beginning of the archive,
164          * we need first write the ar global header.
165          */
166         if (a->archive.file_position == 0)
167                 (a->compressor.write)(a, "!<arch>\n", 8);
168
169         memset(buff, ' ', 60);
170         strncpy(&buff[AR_fmag_offset], "`\n", 2);
171
172         if (strcmp(pathname, "/") == 0 ) {
173                 /* Entry is archive symbol table in GNU format */
174                 buff[AR_name_offset] = '/';
175                 goto stat;
176         }
177         if (strcmp(pathname, "__.SYMDEF") == 0) {
178                 /* Entry is archive symbol table in BSD format */
179                 strncpy(buff + AR_name_offset, "__.SYMDEF", 9);
180                 goto stat;
181         }
182         if (strcmp(pathname, "//") == 0) {
183                 /*
184                  * Entry is archive filename table, inform that we should
185                  * collect strtab in next _data call.
186                  */
187                 ar->is_strtab = 1;
188                 buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
189                 /*
190                  * For archive string table, only ar_size filed should
191                  * be set.
192                  */
193                 goto size;
194         }
195
196         /*
197          * Otherwise, entry is a normal archive member.
198          * Strip leading paths from filenames, if any.
199          */
200         if ((filename = ar_basename(pathname)) == NULL) {
201                 /* Reject filenames with trailing "/" */
202                 archive_set_error(&a->archive, EINVAL,
203                     "Invalid filename");
204                 return (ARCHIVE_WARN);
205         }
206
207         if (a->archive_format == ARCHIVE_FORMAT_AR_GNU) {
208                 /*
209                  * SVR4/GNU variant use a "/" to mark then end of the filename,
210                  * make it possible to have embedded spaces in the filename.
211                  * So, the longest filename here (without extension) is
212                  * actually 15 bytes.
213                  */
214                 if (strlen(filename) <= 15) {
215                         strncpy(&buff[AR_name_offset], 
216                             filename, strlen(filename));
217                         buff[AR_name_offset + strlen(filename)] = '/';
218                 } else {
219                         /*
220                          * For filename longer than 15 bytes, GNU variant
221                          * makes use of a string table and instead stores the
222                          * offset of the real filename to in the ar_name field.
223                          * The string table should have been written before.
224                          */
225                         if (ar->has_strtab <= 0) {
226                                 archive_set_error(&a->archive, EINVAL,
227                                     "Can't find string table");
228                                 return (ARCHIVE_WARN);
229                         }
230
231                         se = (char *)malloc(strlen(filename) + 3);
232                         if (se == NULL) {
233                                 archive_set_error(&a->archive, ENOMEM,
234                                     "Can't allocate filename buffer");
235                                 return (ARCHIVE_FATAL);
236                         }
237
238                         strncpy(se, filename, strlen(filename));
239                         strcpy(se + strlen(filename), "/\n");
240
241                         ss = strstr(ar->strtab, se);
242                         free(se);
243
244                         if (ss == NULL) {
245                                 archive_set_error(&a->archive, EINVAL,
246                                     "Invalid string table");
247                                 return (ARCHIVE_WARN);
248                         }
249
250                         /*
251                          * GNU variant puts "/" followed by digits into
252                          * ar_name field. These digits indicates the real
253                          * filename string's offset to the string table.
254                          */
255                         buff[AR_name_offset] = '/';
256                         if (format_decimal(ss - ar->strtab,
257                             buff + AR_name_offset + 1,
258                             AR_name_size - 1)) {
259                                 archive_set_error(&a->archive, ERANGE,
260                                     "string table offset too large");
261                                 return (ARCHIVE_WARN);
262                         }
263                 }
264         } else if (a->archive_format == ARCHIVE_FORMAT_AR_BSD) {
265                 /*
266                  * BSD variant: for any file name which is more than
267                  * 16 chars or contains one or more embedded space(s), the
268                  * string "#1/" followed by the ASCII length of the name is
269                  * put into the ar_name field. The file size (stored in the
270                  * ar_size field) is incremented by the length of the name.
271                  * The name is then written immediately following the
272                  * archive header.
273                  */
274                 if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
275                         strncpy(&buff[AR_name_offset], filename, strlen(filename));
276                         buff[AR_name_offset + strlen(filename)] = ' ';
277                 }
278                 else {
279                         strncpy(buff + AR_name_offset, "#1/", 3);
280                         if (format_decimal(strlen(filename),
281                             buff + AR_name_offset + 3,
282                             AR_name_size - 3)) {
283                                 archive_set_error(&a->archive, ERANGE,
284                                     "File name too long");
285                                 return (ARCHIVE_WARN);
286                         }
287                         append_fn = 1;
288                         archive_entry_set_size(entry,
289                             archive_entry_size(entry) + strlen(filename));
290                 }
291         }
292
293 stat:
294         if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
295                 archive_set_error(&a->archive, ERANGE,
296                     "File modification time too large");
297                 return (ARCHIVE_WARN);
298         }
299         if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
300                 archive_set_error(&a->archive, ERANGE,
301                     "Numeric user ID too large");
302                 return (ARCHIVE_WARN);
303         }
304         if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
305                 archive_set_error(&a->archive, ERANGE,
306                     "Numeric group ID too large");
307                 return (ARCHIVE_WARN);
308         }
309         if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
310                 archive_set_error(&a->archive, ERANGE,
311                     "Numeric mode too large");
312                 return (ARCHIVE_WARN);
313         }
314         /*
315          * Sanity Check: A non-pseudo archive member should always be
316          * a regular file.
317          */
318         if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
319                 archive_set_error(&a->archive, EINVAL,
320                     "Regular file required for non-pseudo member");
321                 return (ARCHIVE_WARN);
322         }
323
324 size:
325         if (format_decimal(archive_entry_size(entry), buff + AR_size_offset,
326             AR_size_size)) {
327                 archive_set_error(&a->archive, ERANGE,
328                     "File size out of range");
329                 return (ARCHIVE_WARN);
330         }
331
332         ret = (a->compressor.write)(a, buff, 60);
333         if (ret != ARCHIVE_OK)
334                 return (ret);
335
336         ar->entry_bytes_remaining = archive_entry_size(entry);
337         ar->entry_padding = ar->entry_bytes_remaining % 2;
338
339         if (append_fn > 0) {
340                 ret = (a->compressor.write)(a, filename, strlen(filename));
341                 if (ret != ARCHIVE_OK)
342                         return (ret);
343                 ar->entry_bytes_remaining -= strlen(filename);
344         }
345
346         return (ARCHIVE_OK);
347 }
348
349 static ssize_t
350 archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
351 {
352         struct ar_w *ar;
353         int ret;
354
355         ar = (struct ar_w *)a->format_data;
356         if (s > ar->entry_bytes_remaining)
357                 s = ar->entry_bytes_remaining;
358
359         if (ar->is_strtab > 0) {
360                 if (ar->has_strtab > 0) {
361                         archive_set_error(&a->archive, EINVAL,
362                             "More than one string tables exist");
363                         return (ARCHIVE_WARN);
364                 }
365
366                 ar->strtab = (char *)malloc(s);
367                 if (ar->strtab == NULL) {
368                         archive_set_error(&a->archive, ENOMEM,
369                             "Can't allocate strtab buffer");
370                         return (ARCHIVE_FATAL);
371                 }
372                 strncpy(ar->strtab, buff, s);
373                 ar->has_strtab = 1;
374         }
375
376         ret = (a->compressor.write)(a, buff, s);
377         if (ret != ARCHIVE_OK)
378                 return (ret);
379
380         ar->entry_bytes_remaining -= s;
381         return (s);
382 }
383
384 static int
385 archive_write_ar_destroy(struct archive_write *a)
386 {
387         struct ar_w *ar;
388
389         ar = (struct ar_w *)a->format_data;
390
391         if (ar->has_strtab > 0) {
392                 free(ar->strtab);
393                 ar->strtab = NULL;
394         }
395
396         free(ar);
397         a->format_data = NULL;
398         return (ARCHIVE_OK);
399 }
400
401 static int
402 archive_write_ar_finish(struct archive_write *a)
403 {
404         int ret;
405
406         /*
407          * If we haven't written anything yet, we need to write
408          * the ar global header now to make it a valid ar archive.
409          */
410         if (a->archive.file_position == 0) {
411                 ret = (a->compressor.write)(a, "!<arch>\n", 8);
412                 return (ret);
413         }
414
415         return (ARCHIVE_OK);
416 }
417
418 static int
419 archive_write_ar_finish_entry(struct archive_write *a)
420 {
421         struct ar_w *ar;
422         int ret;
423
424         ar = (struct ar_w *)a->format_data;
425
426         if (ar->entry_bytes_remaining != 0) {
427                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
428                     "Entry remaining bytes larger than 0");
429                 return (ARCHIVE_WARN);
430         }
431
432         if (ar->entry_padding == 0) {
433                 return (ARCHIVE_OK);
434         }
435
436         if (ar->entry_padding != 1) {
437                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
438                     "Padding wrong size: %d should be 1 or 0",
439                     ar->entry_padding);
440                 return (ARCHIVE_WARN);
441         }
442
443         ret = (a->compressor.write)(a, "\n", 1);
444         return (ret);
445 }
446
447 /*
448  * Format a number into the specified field using base-8.
449  * NB: This version is slightly different from the one in
450  * _ustar.c
451  */
452 static int
453 format_octal(int64_t v, char *p, int s)
454 {
455         int len;
456         char *h;
457
458         len = s;
459         h = p;
460
461         /* Octal values can't be negative, so use 0. */
462         if (v < 0) {
463                 while (len-- > 0)
464                         *p++ = '0';
465                 return (-1);
466         }
467
468         p += s;         /* Start at the end and work backwards. */
469         do {
470                 *--p = (char)('0' + (v & 7));
471                 v >>= 3;
472         } while (--s > 0 && v > 0);
473
474         if (v == 0) {
475                 memmove(h, p, len - s);
476                 p = h + len - s;
477                 while (s-- > 0)
478                         *p++ = ' ';
479                 return (0);
480         }
481         /* If it overflowed, fill field with max value. */
482         while (len-- > 0)
483                 *p++ = '7';
484
485         return (-1);
486 }
487
488 /*
489  * Format a number into the specified field using base-10.
490  */
491 static int
492 format_decimal(int64_t v, char *p, int s)
493 {
494         int len;
495         char *h;
496
497         len = s;
498         h = p;
499
500         /* Negative values in ar header are meaningless , so use 0. */
501         if (v < 0) {
502                 while (len-- > 0)
503                         *p++ = '0';
504                 return (-1);
505         }
506
507         p += s;
508         do {
509                 *--p = (char)('0' + (v % 10));
510                 v /= 10;
511         } while (--s > 0 && v > 0);
512
513         if (v == 0) {
514                 memmove(h, p, len - s);
515                 p = h + len - s;
516                 while (s-- > 0)
517                         *p++ = ' ';
518                 return (0);
519         }
520         /* If it overflowed, fill field with max value. */
521         while (len-- > 0)
522                 *p++ = '9';
523
524         return (-1);
525 }
526
527 static const char *
528 ar_basename(const char *path)
529 {
530         const char *endp, *startp;
531
532         endp = path + strlen(path) - 1;
533         /*
534          * For filename with trailing slash(es), we return
535          * NULL indicating an error.
536          */
537         if (*endp == '/')
538                 return (NULL);
539
540         /* Find the start of the base */
541         startp = endp;
542         while (startp > path && *(startp - 1) != '/')
543                 startp--;
544         
545         return (startp);
546 }