Patch from Randolfe Averty to fixup package conflict checks, cleanup some memory...
[oweals/busybox.git] / archival / libunarchive / unarchive.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU Library General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16
17 #include <errno.h>
18 #include <fnmatch.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <utime.h>
24 #include <sys/wait.h>
25 #include <signal.h>
26 #include "unarchive.h"
27 #include "busybox.h"
28
29 /* Extract the data postioned at src_stream to either filesystem, stdout or 
30  * buffer depending on the value of 'function' which is defined in libbb.h 
31  *
32  * prefix doesnt have to be just a directory, it may prefix the filename as well.
33  *
34  * e.g. '/var/lib/dpkg/info/dpkg.' will extract all files to the base bath 
35  * '/var/lib/dpkg/info/' and all files/dirs created in that dir will have 
36  * 'dpkg.' as their prefix
37  *
38  * For this reason if prefix does point to a dir then it must end with a
39  * trailing '/' or else the last dir will be assumed to be the file prefix 
40  */
41 char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *file_entry,
42  const int function, const char *prefix)
43 {
44         FILE *dst_stream = NULL;
45         char *full_name = NULL;
46         char *buffer = NULL;
47         struct utimbuf t;
48
49         /* prefix doesnt have to be a proper path it may prepend 
50          * the filename as well */
51         if (prefix != NULL) {
52                 /* strip leading '/' in filename to extract as prefix may not be dir */
53                 /* Cant use concat_path_file here as prefix might not be a directory */
54                 char *path = file_entry->name;
55                 if (strncmp("./", path, 2) == 0) {
56                         path += 2;
57                         if (strlen(path) == 0) {
58                                 return(NULL);
59                         }
60                 }
61                 full_name = xmalloc(strlen(prefix) + strlen(path) + 1);
62                 strcpy(full_name, prefix);
63                 strcat(full_name, path);
64         } else {
65                 full_name = file_entry->name;
66         }
67         if (function & extract_to_stdout) {
68                 if (S_ISREG(file_entry->mode)) {
69                         copy_file_chunk(src_stream, out_stream, file_entry->size);                      
70                         archive_offset += file_entry->size;
71                 }
72         }
73         else if (function & extract_one_to_buffer) { 
74                 if (S_ISREG(file_entry->mode)) {
75                         buffer = (char *) xmalloc(file_entry->size + 1);
76                         fread(buffer, 1, file_entry->size, src_stream);
77                         buffer[file_entry->size] = '\0';
78                         archive_offset += file_entry->size;
79                         return(buffer);
80                 }
81         }
82         else if (function & extract_all_to_fs) {
83                 struct stat oldfile;
84                 int stat_res;
85                 stat_res = lstat (full_name, &oldfile);
86                 if (stat_res == 0) { /* The file already exists */
87                         if ((function & extract_unconditional) || (oldfile.st_mtime < file_entry->mtime)) {
88                                 if (!S_ISDIR(oldfile.st_mode)) {
89                                         unlink(full_name); /* Directories might not be empty etc */
90                                 }
91                         } else {
92                                 if ((function & extract_quiet) != extract_quiet) {
93                                         error_msg("%s not created: newer or same age file exists", file_entry->name);
94                                 }
95                                 seek_sub_file(src_stream, file_entry->size);
96                                 return (NULL);
97                         }
98                 }
99                 if (function & extract_create_leading_dirs) { /* Create leading directories with default umask */
100                         char *buf, *parent;
101                         buf = xstrdup(full_name);
102                         parent = dirname(buf);
103                         if (make_directory (parent, -1, FILEUTILS_RECUR) != 0) {
104                                 if ((function & extract_quiet) != extract_quiet) {
105                                         error_msg("couldn't create leading directories");
106                                 }
107                         }
108                         free (buf);
109                 }
110                 switch(file_entry->mode & S_IFMT) {
111                         case S_IFREG:
112                                 if (file_entry->link_name) { /* Found a cpio hard link */
113                                         if (link(file_entry->link_name, full_name) != 0) {
114                                                 if ((function & extract_quiet) != extract_quiet) {
115                                                         perror_msg("Cannot link from %s to '%s'",
116                                                                 file_entry->name, file_entry->link_name);
117                                                 }
118                                         }
119                                 } else {
120                                         if ((dst_stream = wfopen(full_name, "w")) == NULL) {
121                                                 seek_sub_file(src_stream, file_entry->size);
122                                                 return NULL;
123                                         }
124                                         archive_offset += file_entry->size;
125                                         if (file_entry->extract_func) file_entry->extract_func(src_stream, dst_stream);
126                                         else copy_file_chunk(src_stream, dst_stream, file_entry->size);                 
127                                         fclose(dst_stream);
128                                 }
129                                 break;
130                         case S_IFDIR:
131                                 if (stat_res != 0) {
132                                         if (mkdir(full_name, file_entry->mode) < 0) {
133                                                 if ((function & extract_quiet) != extract_quiet) {
134                                                         perror_msg("extract_archive: %s", full_name);
135                                                 }
136                                         }
137                                 }
138                                 break;
139                         case S_IFLNK:
140                                 if (symlink(file_entry->link_name, full_name) < 0) {
141                                         if ((function & extract_quiet) != extract_quiet) {
142                                                 perror_msg("Cannot create symlink from %s to '%s'", file_entry->name, file_entry->link_name);
143                                         }
144                                         return NULL;
145                                 }
146                                 break;
147                         case S_IFSOCK:
148                         case S_IFBLK:
149                         case S_IFCHR:
150                         case S_IFIFO:
151                                 if (mknod(full_name, file_entry->mode, file_entry->device) == -1) {
152                                         if ((function & extract_quiet) != extract_quiet) {
153                                                 perror_msg("Cannot create node %s", file_entry->name);
154                                         }
155                                         return NULL;
156                                 }
157                                 break;
158                 }
159
160                 /* Changing a symlink's properties normally changes the properties of the 
161                  * file pointed to, so dont try and change the date or mode, lchown does
162                  * does the right thing, but isnt available in older versions of libc */
163                 if (S_ISLNK(file_entry->mode)) {
164 #if (__GLIBC__ > 2) && (__GLIBC_MINOR__ > 1)
165                         lchown(full_name, file_entry->uid, file_entry->gid);
166 #endif
167                 } else {
168                         if (function & extract_preserve_date) {
169                                 t.actime = file_entry->mtime;
170                                 t.modtime = file_entry->mtime;
171                                 utime(full_name, &t);
172                         }
173                         chmod(full_name, file_entry->mode);
174                         chown(full_name, file_entry->uid, file_entry->gid);
175                 }
176         } else {
177                 /* If we arent extracting data we have to skip it, 
178                  * if data size is 0 then then just do it anyway
179                  * (saves testing for it) */
180                 seek_sub_file(src_stream, file_entry->size);
181         }
182
183         /* extract_list and extract_verbose_list can be used in conjunction
184          * with one of the above four extraction functions, so do this seperately */
185         if (function & extract_verbose_list) {
186                 fprintf(out_stream, "%s %d/%d %8d %s ", mode_string(file_entry->mode), 
187                         file_entry->uid, file_entry->gid,
188                         (int) file_entry->size, time_string(file_entry->mtime));
189         }
190         if ((function & extract_list) || (function & extract_verbose_list)){
191                 /* fputs doesnt add a trailing \n, so use fprintf */
192                 fprintf(out_stream, "%s\n", full_name);
193         }
194
195         if (prefix != NULL) {
196           free(full_name);
197         }
198
199         return(NULL); /* Maybe we should say if failed */
200 }
201
202 char *unarchive(FILE *src_stream, FILE *out_stream, file_header_t *(*get_headers)(FILE *),
203         const int extract_function, const char *prefix, char **include_name, char **exclude_name)
204 {
205         file_header_t *file_entry;
206         int extract_flag = TRUE;
207         int i;
208         char *buffer = NULL;
209 #ifdef CONFIG_FEATURE_UNARCHIVE_TAPE
210         int pid, tape_pipe[2];
211
212         if (pipe(tape_pipe) != 0) error_msg_and_die("Can't create pipe\n");
213         if ((pid = fork()) == -1) error_msg_and_die("Fork failed\n");
214         if (pid==0) { /* child process */
215             close(tape_pipe[0]); /* We don't wan't to read from the pipe */
216             copyfd(fileno(src_stream), tape_pipe[1]);
217             close(tape_pipe[1]); /* Send EOF */
218             exit(0);
219             /* notreached */
220         }
221         close(tape_pipe[1]); /* Don't want to write down the pipe */
222         fclose(src_stream);
223         src_stream = fdopen(tape_pipe[0], "r");
224 #endif
225         archive_offset = 0;
226         while ((file_entry = get_headers(src_stream)) != NULL) {
227
228                 if (include_name != NULL) {
229                         extract_flag = FALSE;
230                         for(i = 0; include_name[i] != 0; i++) {
231                                 if (fnmatch(include_name[i], file_entry->name, FNM_LEADING_DIR) == 0) {
232                                         extract_flag = TRUE;
233                                         break;
234                                 }
235                         }
236                 } else {
237                         extract_flag = TRUE;
238                 }
239
240                 /* If the file entry is in the exclude list dont extract it */
241                 if (exclude_name != NULL) {
242                         for(i = 0; exclude_name[i] != 0; i++) {
243                                 if (fnmatch(exclude_name[i], file_entry->name, FNM_LEADING_DIR) == 0) {
244                                         extract_flag = FALSE;
245                                         break;
246                                 }
247                         }
248                 }
249
250                 if (extract_flag) {
251                         buffer = extract_archive(src_stream, out_stream, file_entry, extract_function, prefix);
252                 } else {
253                         /* seek past the data entry */
254                         seek_sub_file(src_stream, file_entry->size);
255                 }
256                 free(file_entry->name);
257                 free(file_entry->link_name);
258                 free(file_entry);
259         }
260 #ifdef CONFIG_FEATURE_UNARCHIVE_TAPE
261         kill(pid, SIGTERM);
262         waitpid(pid, NULL, 0);
263 #endif
264         return(buffer);
265 }