unzip: optional support for bzip2 and lzma
[oweals/busybox.git] / archival / libarchive / get_header_cpio.c
1 /* vi: set sw=4 ts=4: */
2 /* Copyright 2002 Laurence Anderson
3  *
4  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5  */
6
7 #include "libbb.h"
8 #include "bb_archive.h"
9
10 typedef struct hardlinks_t {
11         struct hardlinks_t *next;
12         int inode; /* TODO: must match maj/min too! */
13         int mode ;
14         int mtime; /* These three are useful only in corner case */
15         int uid  ; /* of hardlinks with zero size body */
16         int gid  ;
17         char name[1];
18 } hardlinks_t;
19
20 char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
21 {
22         file_header_t *file_header = archive_handle->file_header;
23         char cpio_header[110];
24         int namesize;
25         int major, minor, nlink, mode, inode;
26         unsigned size, uid, gid, mtime;
27
28         /* There can be padding before archive header */
29         data_align(archive_handle, 4);
30
31         size = full_read(archive_handle->src_fd, cpio_header, 110);
32         if (size == 0) {
33                 goto create_hardlinks;
34         }
35         if (size != 110) {
36                 bb_error_msg_and_die("short read");
37         }
38         archive_handle->offset += 110;
39
40         if (!is_prefixed_with(&cpio_header[0], "07070")
41          || (cpio_header[5] != '1' && cpio_header[5] != '2')
42         ) {
43                 bb_error_msg_and_die("unsupported cpio format, use newc or crc");
44         }
45
46         if (sscanf(cpio_header + 6,
47                         "%8x" "%8x" "%8x" "%8x"
48                         "%8x" "%8x" "%8x" /*maj,min:*/ "%*16c"
49                         /*rmaj,rmin:*/"%8x" "%8x" "%8x" /*chksum: "%*8c"*/,
50                         &inode, &mode, &uid, &gid,
51                         &nlink, &mtime, &size,
52                         &major, &minor, &namesize) != 10)
53                 bb_error_msg_and_die("damaged cpio file");
54         file_header->mode = mode;
55         /* "cpio -R USER:GRP" support: */
56         if (archive_handle->cpio__owner.uid != (uid_t)-1L)
57                 uid = archive_handle->cpio__owner.uid;
58         if (archive_handle->cpio__owner.gid != (gid_t)-1L)
59                 gid = archive_handle->cpio__owner.gid;
60         file_header->uid = uid;
61         file_header->gid = gid;
62         file_header->mtime = mtime;
63         file_header->size = size;
64
65         namesize &= 0x1fff; /* paranoia: limit names to 8k chars */
66         file_header->name = xzalloc(namesize + 1);
67         /* Read in filename */
68         xread(archive_handle->src_fd, file_header->name, namesize);
69         if (file_header->name[0] == '/') {
70                 /* Testcase: echo /etc/hosts | cpio -pvd /tmp
71                  * Without this code, it tries to unpack /etc/hosts
72                  * into "/etc/hosts", not "etc/hosts".
73                  */
74                 char *p = file_header->name;
75                 do p++; while (*p == '/');
76                 overlapping_strcpy(file_header->name, p);
77         }
78         archive_handle->offset += namesize;
79
80         /* Update offset amount and skip padding before file contents */
81         data_align(archive_handle, 4);
82
83         if (strcmp(file_header->name, cpio_TRAILER) == 0) {
84                 /* Always round up. ">> 9" divides by 512 */
85                 archive_handle->cpio__blocks = (uoff_t)(archive_handle->offset + 511) >> 9;
86                 goto create_hardlinks;
87         }
88
89         file_header->link_target = NULL;
90         if (S_ISLNK(file_header->mode)) {
91                 file_header->size &= 0x1fff; /* paranoia: limit names to 8k chars */
92                 file_header->link_target = xzalloc(file_header->size + 1);
93                 xread(archive_handle->src_fd, file_header->link_target, file_header->size);
94                 archive_handle->offset += file_header->size;
95                 file_header->size = 0; /* Stop possible seeks in future */
96         }
97
98 // TODO: data_extract_all can't deal with hardlinks to non-files...
99 // when fixed, change S_ISREG to !S_ISDIR here
100
101         if (nlink > 1 && S_ISREG(file_header->mode)) {
102                 hardlinks_t *new = xmalloc(sizeof(*new) + namesize);
103                 new->inode = inode;
104                 new->mode  = mode ;
105                 new->mtime = mtime;
106                 new->uid   = uid  ;
107                 new->gid   = gid  ;
108                 strcpy(new->name, file_header->name);
109                 /* Put file on a linked list for later */
110                 if (size == 0) {
111                         new->next = archive_handle->cpio__hardlinks_to_create;
112                         archive_handle->cpio__hardlinks_to_create = new;
113                         return EXIT_SUCCESS; /* Skip this one */
114                         /* TODO: this breaks cpio -t (it does not show hardlinks) */
115                 }
116                 new->next = archive_handle->cpio__created_hardlinks;
117                 archive_handle->cpio__created_hardlinks = new;
118         }
119         file_header->device = makedev(major, minor);
120
121         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
122                 archive_handle->action_data(archive_handle);
123 //TODO: run "echo /etc/hosts | cpio -pv /tmp" twice. On 2nd run:
124 //cpio: etc/hosts not created: newer or same age file exists
125 //etc/hosts  <-- should NOT show it
126 //2 blocks <-- should say "0 blocks"
127                 archive_handle->action_header(file_header);
128         } else {
129                 data_skip(archive_handle);
130         }
131
132         archive_handle->offset += file_header->size;
133
134         free(file_header->link_target);
135         free(file_header->name);
136         file_header->link_target = NULL;
137         file_header->name = NULL;
138
139         return EXIT_SUCCESS;
140
141  create_hardlinks:
142         free(file_header->link_target);
143         free(file_header->name);
144
145         while (archive_handle->cpio__hardlinks_to_create) {
146                 hardlinks_t *cur;
147                 hardlinks_t *make_me = archive_handle->cpio__hardlinks_to_create;
148
149                 archive_handle->cpio__hardlinks_to_create = make_me->next;
150
151                 memset(file_header, 0, sizeof(*file_header));
152                 file_header->mtime = make_me->mtime;
153                 file_header->name = make_me->name;
154                 file_header->mode = make_me->mode;
155                 file_header->uid = make_me->uid;
156                 file_header->gid = make_me->gid;
157                 /*file_header->size = 0;*/
158                 /*file_header->link_target = NULL;*/
159
160                 /* Try to find a file we are hardlinked to */
161                 cur = archive_handle->cpio__created_hardlinks;
162                 while (cur) {
163                         /* TODO: must match maj/min too! */
164                         if (cur->inode == make_me->inode) {
165                                 file_header->link_target = cur->name;
166                                  /* link_target != NULL, size = 0: "I am a hardlink" */
167                                 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS)
168                                         archive_handle->action_data(archive_handle);
169                                 free(make_me);
170                                 goto next_link;
171                         }
172                         cur = cur->next;
173                 }
174                 /* Oops... no file with such inode was created... do it now
175                  * (happens when hardlinked files are empty (zero length)) */
176                 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS)
177                         archive_handle->action_data(archive_handle);
178                 /* Move to the list of created hardlinked files */
179                 make_me->next = archive_handle->cpio__created_hardlinks;
180                 archive_handle->cpio__created_hardlinks = make_me;
181  next_link: ;
182         }
183
184         while (archive_handle->cpio__created_hardlinks) {
185                 hardlinks_t *p = archive_handle->cpio__created_hardlinks;
186                 archive_handle->cpio__created_hardlinks = p->next;
187                 free(p);
188         }
189
190         return EXIT_FAILURE; /* "No more files to process" */
191 }