Initial public busybox upstream commit
[busybox4maemo] / archival / libunarchive / 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 tarball for details.
5  */
6
7 #include "libbb.h"
8 #include "unarchive.h"
9
10 typedef struct hardlinks_s {
11         char *name;
12         int inode;
13         struct hardlinks_s *next;
14 } hardlinks_t;
15
16 char get_header_cpio(archive_handle_t *archive_handle)
17 {
18         static hardlinks_t *saved_hardlinks = NULL;
19         static unsigned pending_hardlinks = 0;
20         static int inode;
21
22         file_header_t *file_header = archive_handle->file_header;
23         char cpio_header[110];
24         int namesize;
25         char dummy[16];
26         int major, minor, nlink;
27
28         if (pending_hardlinks) { /* Deal with any pending hardlinks */
29                 hardlinks_t *tmp, *oldtmp;
30
31                 tmp = saved_hardlinks;
32                 oldtmp = NULL;
33
34                 file_header->link_target = file_header->name;
35                 file_header->size = 0;
36
37                 while (tmp) {
38                         if (tmp->inode != inode) {
39                                 tmp = tmp->next;
40                                 continue;
41                         }
42
43                         file_header->name = tmp->name;
44
45                         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
46                                 archive_handle->action_data(archive_handle);
47                                 archive_handle->action_header(archive_handle->file_header);
48                         }
49
50                         pending_hardlinks--;
51
52                         oldtmp = tmp;
53                         tmp = tmp->next;
54                         free(oldtmp->name);
55                         free(oldtmp);
56                         if (oldtmp == saved_hardlinks)
57                                 saved_hardlinks = tmp;
58                 }
59
60                 file_header->name = file_header->link_target;
61
62                 if (pending_hardlinks > 1) {
63                         bb_error_msg("error resolving hardlink: archive made by GNU cpio 2.0-2.2?");
64                 }
65
66                 /* No more pending hardlinks, read next file entry */
67                 pending_hardlinks = 0;
68         }
69
70         /* There can be padding before archive header */
71         data_align(archive_handle, 4);
72
73         if (archive_xread_all_eof(archive_handle, (unsigned char*)cpio_header, 110) == 0) {
74                 return EXIT_FAILURE;
75         }
76         archive_handle->offset += 110;
77
78         if (strncmp(&cpio_header[0], "07070", 5) != 0
79          || (cpio_header[5] != '1' && cpio_header[5] != '2')
80         ) {
81                 bb_error_msg_and_die("unsupported cpio format, use newc or crc");
82         }
83
84         {
85                 unsigned long tmpsize;
86                 sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
87                     dummy, &inode, (unsigned int*)&file_header->mode,
88                     (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
89                     &nlink, &file_header->mtime, &tmpsize,
90                     dummy, &major, &minor, &namesize, dummy);
91                 file_header->size = tmpsize;
92         }
93
94         free(file_header->name);
95         file_header->name = xzalloc(namesize + 1);
96         /* Read in filename */
97         xread(archive_handle->src_fd, file_header->name, namesize);
98         archive_handle->offset += namesize;
99
100         /* Update offset amount and skip padding before file contents */
101         data_align(archive_handle, 4);
102
103         if (strcmp(file_header->name, "TRAILER!!!") == 0) {
104                 /* Always round up */
105                 printf("%d blocks\n", (int) (archive_handle->offset % 512 ?
106                                              archive_handle->offset / 512 + 1 :
107                                              archive_handle->offset / 512
108                                             ));
109                 if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
110                         hardlinks_t *tmp = saved_hardlinks;
111                         hardlinks_t *oldtmp = NULL;
112                         while (tmp) {
113                                 bb_error_msg("%s not created: cannot resolve hardlink", tmp->name);
114                                 oldtmp = tmp;
115                                 tmp = tmp->next;
116                                 free(oldtmp->name);
117                                 free(oldtmp);
118                         }
119                         saved_hardlinks = NULL;
120                         pending_hardlinks = 0;
121                 }
122                 return EXIT_FAILURE;
123         }
124
125         if (S_ISLNK(file_header->mode)) {
126                 file_header->link_target = xzalloc(file_header->size + 1);
127                 xread(archive_handle->src_fd, file_header->link_target, file_header->size);
128                 archive_handle->offset += file_header->size;
129                 file_header->size = 0; /* Stop possible seeks in future */
130         } else {
131                 file_header->link_target = NULL;
132         }
133         if (nlink > 1 && !S_ISDIR(file_header->mode)) {
134                 if (file_header->size == 0) { /* Put file on a linked list for later */
135                         hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
136                         new->next = saved_hardlinks;
137                         new->inode = inode;
138                         /* name current allocated, freed later */
139                         new->name = file_header->name;
140                         file_header->name = NULL;
141                         saved_hardlinks = new;
142                         return EXIT_SUCCESS; /* Skip this one */
143                 }
144                 /* Found the file with data in */
145                 pending_hardlinks = nlink;
146         }
147         file_header->device = makedev(major, minor);
148
149         if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
150                 archive_handle->action_data(archive_handle);
151                 archive_handle->action_header(archive_handle->file_header);
152         } else {
153                 data_skip(archive_handle);
154         }
155
156         archive_handle->offset += file_header->size;
157
158         free(file_header->link_target);
159
160         return EXIT_SUCCESS;
161 }