0d06149321125dba09918e48ba7f41a8056eb749
[oweals/busybox.git] / archival / cpio.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini cpio implementation for busybox
4  *
5  * Copyright (C) 2001 by Glenn McGrath 
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * Limitations:
22  *              Doesn't check CRC's
23  *              Only supports new ASCII and CRC formats
24  *
25  */
26 #include <fcntl.h>
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include "unarchive.h"
32 #include "busybox.h"
33
34 typedef struct hardlinks_s {
35         file_header_t *entry;
36         int inode;
37         struct hardlinks_s *next;
38 } hardlinks_t;
39
40 extern int cpio_main(int argc, char **argv)
41 {
42         archive_handle_t *archive_handle;
43         int opt;
44
45         /* Initialise */
46         archive_handle = init_handle();
47         archive_handle->src_fd = fileno(stdin);
48         archive_handle->seek = seek_by_char;
49         archive_handle->action_header = header_list;
50
51         while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) {
52                 switch (opt) {
53                 case 'i': /* extract */
54                         archive_handle->action_data = data_extract_all;
55                         break;
56                 case 'd': /* create _leading_ directories */
57                         archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
58                         break;
59                 case 'm': /* preserve modification time */
60                         archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
61                         break;
62                 case 'v': /* verbosly list files */
63                         archive_handle->action_header = header_verbose_list;
64                         break;
65                 case 'u': /* unconditional */
66                         archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
67                         break;
68                 case 't': /* list files */
69                         archive_handle->action_header = header_list;
70                         break;
71                 case 'F':
72                         archive_handle->src_fd = xopen(optarg, O_RDONLY);
73                         archive_handle->seek = seek_by_jump;
74                         break;
75                 default:
76                         show_usage();
77                 }
78         }
79
80         while (optind < argc) {
81                 archive_handle->filter = filter_accept_list;
82                 archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]);
83                 optind++;
84         }
85
86         while (1) {
87                 static hardlinks_t *saved_hardlinks = NULL;
88                 static unsigned short pending_hardlinks = 0;
89                 file_header_t *file_header = archive_handle->file_header;
90                 char cpio_header[110];
91                 int namesize;
92                 char dummy[16];
93                 int major, minor, nlink, inode;
94                 char extract_flag;
95
96                 if (pending_hardlinks) { /* Deal with any pending hardlinks */
97                         hardlinks_t *tmp;
98                         hardlinks_t *oldtmp;
99
100                         tmp = saved_hardlinks;
101                         oldtmp = NULL;
102
103                         while (tmp) {
104                                 error_msg_and_die("need to fix this\n");
105                                 if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */
106                                         file_header = tmp->entry;
107                                         if (oldtmp) {
108                                                 oldtmp->next = tmp->next; /* Remove item from linked list */
109                                         } else {
110                                                 saved_hardlinks = tmp->next;
111                                         }
112                                         free(tmp);
113                                         continue;
114                                 }
115                                 oldtmp = tmp;
116                                 tmp = tmp->next;
117                         }
118                         pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */
119         }
120
121                 /* There can be padding before archive header */
122                 data_align(archive_handle, 4);
123
124                 if (archive_xread_all_eof(archive_handle, cpio_header, 110) == 0) {
125                         return(EXIT_FAILURE);
126                 }
127                 archive_handle->offset += 110;
128
129                 if (strncmp(&cpio_header[0], "07070", 5) != 0) {
130                         printf("cpio header is %x-%x-%x-%x-%x\n",
131                                 cpio_header[0],
132                                 cpio_header[1],
133                                 cpio_header[2],
134                                 cpio_header[3],
135                                 cpio_header[4]);
136                         error_msg_and_die("Unsupported cpio format");
137                 }
138                 
139                 if ((cpio_header[5] != '1') && (cpio_header[5] != '2')) {
140                         error_msg_and_die("Unsupported cpio format, use newc or crc");
141         }
142
143                 sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
144                         dummy, &inode, (unsigned int*)&file_header->mode, 
145                         (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
146                         &nlink, &file_header->mtime, &file_header->size,
147                         dummy, &major, &minor, &namesize, dummy);
148
149                 file_header->name = (char *) xmalloc(namesize + 1);
150                 archive_xread_all(archive_handle, file_header->name, namesize); /* Read in filename */
151                 file_header->name[namesize] = '\0';
152                 archive_handle->offset += namesize;
153
154                 /* Update offset amount and skip padding before file contents */
155                 data_align(archive_handle, 4);
156
157                 if (strcmp(file_header->name, "TRAILER!!!") == 0) {
158                         printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */
159                         if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
160                                 hardlinks_t *tmp = saved_hardlinks;
161                                 hardlinks_t *oldtmp = NULL;
162                                 while (tmp) {
163                                         error_msg("%s not created: cannot resolve hardlink", tmp->entry->name);
164                                         oldtmp = tmp;
165                                         tmp = tmp->next;
166                                         free (oldtmp->entry->name);
167                                         free (oldtmp->entry);
168                                         free (oldtmp);
169                                 }
170                                 saved_hardlinks = NULL;
171                                 pending_hardlinks = 0;
172         }
173                         return(EXIT_FAILURE);
174                 }
175
176                 if (S_ISLNK(file_header->mode)) {
177                         file_header->link_name = (char *) xmalloc(file_header->size + 1);
178                         archive_xread_all(archive_handle, file_header->link_name, file_header->size);
179                         file_header->link_name[file_header->size] = '\0';
180                         archive_handle->offset += file_header->size;
181                         file_header->size = 0; /* Stop possible seeks in future */
182                 }
183                 if (nlink > 1 && !S_ISDIR(file_header->mode)) {
184                         if (file_header->size == 0) { /* Put file on a linked list for later */
185                                 hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
186                                 new->next = saved_hardlinks;
187                                 new->inode = inode;
188                                 new->entry = file_header;
189                                 saved_hardlinks = new;
190                                 continue;
191                         } else { /* Found the file with data in */
192                                 hardlinks_t *tmp = saved_hardlinks;
193                                 pending_hardlinks = 1;
194                                 while (tmp) {
195                                         if (tmp->inode == inode) {
196                                                 tmp->entry->link_name = xstrdup(file_header->name);
197                                                 nlink--;
198                                         }
199                                         tmp = tmp->next;
200                                 }
201                                 if (nlink > 1) {
202                                         error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?");
203                                 }
204                         }
205                 }
206                 file_header->device = (major << 8) | minor;
207
208                 extract_flag = FALSE;
209                 if (archive_handle->filter(archive_handle->accept, archive_handle->reject, file_header->name) == EXIT_SUCCESS) {
210                         struct stat statbuf;
211
212                         extract_flag = TRUE;
213
214                         /* Check if the file already exists */
215                         if (lstat (file_header->name, &statbuf) == 0) {
216                                 if ((archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) || (statbuf.st_mtime < file_header->mtime)) {
217                                         /* Remove file if flag set or its older than the file to be extracted */
218                                         if (unlink(file_header->name) == -1) {
219                                                 perror_msg_and_die("Couldnt remove old file");
220                                         }
221                                 } else {
222                                         if (! archive_handle->flags & ARCHIVE_EXTRACT_QUIET) {
223                                                 error_msg("%s not created: newer or same age file exists", file_header->name);
224                                         }
225                                         extract_flag = FALSE;
226                                 }
227                         }
228                         archive_handle->action_header(file_header);
229                 }
230
231                 archive_handle->action_header(file_header);
232                 if (extract_flag) {
233                         archive_handle->action_data(archive_handle);
234                 } else {
235                         data_skip(archive_handle);
236                 }
237                 archive_handle->offset += file_header->size;
238         }
239
240         return(EXIT_SUCCESS);
241 }
242