less: document -S flag and make it independently configurable
[oweals/busybox.git] / archival / libarchive / data_extract_all.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  */
5
6 #include "libbb.h"
7 #include "bb_archive.h"
8
9 void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
10 {
11         file_header_t *file_header = archive_handle->file_header;
12         int dst_fd;
13         int res;
14
15 #if ENABLE_FEATURE_TAR_SELINUX
16         char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
17         if (!sctx)
18                 sctx = archive_handle->tar__sctx[PAX_GLOBAL];
19         if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
20                 setfscreatecon(sctx);
21                 free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
22                 archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
23         }
24 #endif
25
26         if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
27                 char *slash = strrchr(file_header->name, '/');
28                 if (slash) {
29                         *slash = '\0';
30                         bb_make_directory(file_header->name, -1, FILEUTILS_RECUR);
31                         *slash = '/';
32                 }
33         }
34
35         if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
36                 /* Remove the entry if it exists */
37                 if (!S_ISDIR(file_header->mode)) {
38                         /* Is it hardlink?
39                          * We encode hard links as regular files of size 0 with a symlink */
40                         if (S_ISREG(file_header->mode)
41                          && file_header->link_target
42                          && file_header->size == 0
43                         ) {
44                                 /* Ugly special case:
45                                  * tar cf t.tar hardlink1 hardlink2 hardlink1
46                                  * results in this tarball structure:
47                                  * hardlink1
48                                  * hardlink2 -> hardlink1
49                                  * hardlink1 -> hardlink1 <== !!!
50                                  */
51                                 if (strcmp(file_header->link_target, file_header->name) == 0)
52                                         goto ret;
53                         }
54                         /* Proceed with deleting */
55                         if (unlink(file_header->name) == -1
56                          && errno != ENOENT
57                         ) {
58                                 bb_perror_msg_and_die("can't remove old file %s",
59                                                 file_header->name);
60                         }
61                 }
62         }
63         else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
64                 /* Remove the existing entry if its older than the extracted entry */
65                 struct stat existing_sb;
66                 if (lstat(file_header->name, &existing_sb) == -1) {
67                         if (errno != ENOENT) {
68                                 bb_perror_msg_and_die("can't stat old file");
69                         }
70                 }
71                 else if (existing_sb.st_mtime >= file_header->mtime) {
72                         if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
73                          && !S_ISDIR(file_header->mode)
74                         ) {
75                                 bb_error_msg("%s not created: newer or "
76                                         "same age file exists", file_header->name);
77                         }
78                         data_skip(archive_handle);
79                         goto ret;
80                 }
81                 else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
82                         bb_perror_msg_and_die("can't remove old file %s",
83                                         file_header->name);
84                 }
85         }
86
87         /* Handle hard links separately
88          * We encode hard links as regular files of size 0 with a symlink */
89         if (S_ISREG(file_header->mode)
90          && file_header->link_target
91          && file_header->size == 0
92         ) {
93                 /* hard link */
94                 res = link(file_header->link_target, file_header->name);
95                 if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
96                         bb_perror_msg("can't create %slink "
97                                         "from %s to %s", "hard",
98                                         file_header->name,
99                                         file_header->link_target);
100                 }
101                 /* Hardlinks have no separate mode/ownership, skip chown/chmod */
102                 goto ret;
103         }
104
105         /* Create the filesystem entry */
106         switch (file_header->mode & S_IFMT) {
107         case S_IFREG: {
108                 /* Regular file */
109                 char *dst_name;
110                 int flags = O_WRONLY | O_CREAT | O_EXCL;
111                 if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
112                         flags = O_WRONLY | O_CREAT | O_TRUNC;
113                 dst_name = file_header->name;
114 #ifdef ARCHIVE_REPLACE_VIA_RENAME
115                 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
116                         /* rpm-style temp file name */
117                         dst_name = xasprintf("%s;%x", dst_name, (int)getpid());
118 #endif
119                 dst_fd = xopen3(dst_name,
120                         flags,
121                         file_header->mode
122                         );
123                 bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
124                 close(dst_fd);
125 #ifdef ARCHIVE_REPLACE_VIA_RENAME
126                 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
127                         xrename(dst_name, file_header->name);
128                         free(dst_name);
129                 }
130 #endif
131                 break;
132         }
133         case S_IFDIR:
134                 res = mkdir(file_header->name, file_header->mode);
135                 if ((res == -1)
136                  && (errno != EISDIR) /* btw, Linux doesn't return this */
137                  && (errno != EEXIST)
138                  && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
139                 ) {
140                         bb_perror_msg("can't make dir %s", file_header->name);
141                 }
142                 break;
143         case S_IFLNK:
144                 /* Symlink */
145 //TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
146                 res = symlink(file_header->link_target, file_header->name);
147                 if ((res == -1)
148                  && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
149                 ) {
150                         bb_perror_msg("can't create %slink "
151                                 "from %s to %s", "sym",
152                                 file_header->name,
153                                 file_header->link_target);
154                 }
155                 break;
156         case S_IFSOCK:
157         case S_IFBLK:
158         case S_IFCHR:
159         case S_IFIFO:
160                 res = mknod(file_header->name, file_header->mode, file_header->device);
161                 if ((res == -1)
162                  && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
163                 ) {
164                         bb_perror_msg("can't create node %s", file_header->name);
165                 }
166                 break;
167         default:
168                 bb_error_msg_and_die("unrecognized file type");
169         }
170
171         if (!S_ISLNK(file_header->mode)) {
172                 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) {
173                         uid_t uid = file_header->uid;
174                         gid_t gid = file_header->gid;
175 #if ENABLE_FEATURE_TAR_UNAME_GNAME
176                         if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
177                                 if (file_header->tar__uname) {
178 //TODO: cache last name/id pair?
179                                         struct passwd *pwd = getpwnam(file_header->tar__uname);
180                                         if (pwd) uid = pwd->pw_uid;
181                                 }
182                                 if (file_header->tar__gname) {
183                                         struct group *grp = getgrnam(file_header->tar__gname);
184                                         if (grp) gid = grp->gr_gid;
185                                 }
186                         }
187 #endif
188                         /* GNU tar 1.15.1 uses chown, not lchown */
189                         chown(file_header->name, uid, gid);
190                 }
191                 /* uclibc has no lchmod, glibc is even stranger -
192                  * it has lchmod which seems to do nothing!
193                  * so we use chmod... */
194                 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
195                         chmod(file_header->name, file_header->mode);
196                 }
197                 if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
198                         struct timeval t[2];
199
200                         t[1].tv_sec = t[0].tv_sec = file_header->mtime;
201                         t[1].tv_usec = t[0].tv_usec = 0;
202                         utimes(file_header->name, t);
203                 }
204         }
205
206  ret: ;
207 #if ENABLE_FEATURE_TAR_SELINUX
208         if (sctx) {
209                 /* reset the context after creating an entry */
210                 setfscreatecon(NULL);
211         }
212 #endif
213 }