7bc55411142faa32685fbf1d61d9a3472eed80b6
[platform/upstream/rpm.git] / lib / cpio.c
1 #include <alloca.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <utime.h>
11
12 #include "config.h"
13 #include "cpio.h"
14 #include "miscfn.h"
15
16 #if MAJOR_IN_SYSMACROS 
17 #include <sys/sysmacros.h>
18 #elif MAJOR_IN_MKDEV 
19 #include <sys/mkdev.h>
20 #endif
21
22 #define CPIO_CRC_MAGIC  "070702"
23 #define TRAILER         "TRAILER!!!"
24
25 /* FIXME: We don't translate between cpio and system mode bits! These
26    should both be the same, but really odd things are going to happen if
27    that's not true! */
28
29 /* We need to maintain our oun file pointer to allow padding */
30 struct ourfd {
31     gzFile fd;
32     int pos;
33 };
34
35 struct cpioCrcPhysicalHeader {
36     char magic[6];
37     char inode[8];
38     char mode[8];
39     char uid[8];
40     char gid[8];
41     char nlink[8];
42     char mtime[8];
43     char filesize[8];
44     char devMajor[8];
45     char devMinor[8];
46     char rdevMajor[8];
47     char rdevMinor[8];
48     char namesize[8];
49     char checksum[8];                   /* ignored !! */
50 };
51
52 struct cpioHeader {
53     ino_t inode;
54     mode_t mode;
55     uid_t uid;
56     gid_t gid;
57     int nlink;
58     time_t mtime;
59     long size;
60     dev_t dev, rdev;
61     char * path;
62 };
63
64 static inline loff_t ourread(struct ourfd * thefd, void * buf, size_t size) {
65     loff_t i;
66
67     i = gzread(thefd->fd, buf, size);
68     thefd->pos += i;
69     
70     return i;
71 }
72
73 static inline void padfd(struct ourfd * fd, int modulo) {
74     int buf[10];
75     int amount;
76     
77     amount = (modulo - fd->pos % modulo) % modulo;
78     ourread(fd, buf, amount);
79 }
80
81 static int strntoul(const char * str, char ** endptr, int base, int num) {
82     char * buf;
83
84     buf = alloca(num + 1);
85     strncpy(buf, str, num);
86     buf[num] = '\0';
87
88     return strtoul(buf, endptr, base);
89 }
90
91 #define GET_NUM_FIELD(phys, log) \
92         log = strntoul(phys, &end, 16, sizeof(phys)); \
93         if (*end) return CPIO_BAD_HEADER;
94
95 static int getNextHeader(struct ourfd * fd, struct cpioHeader * chPtr) {
96     struct cpioCrcPhysicalHeader physHeader;
97     int nameSize;
98     char * end;
99     int major, minor;
100
101     if (ourread(fd, &physHeader, sizeof(physHeader)) != sizeof(physHeader)) 
102         return CPIO_READ_FAILED;
103
104     if (strncmp(CPIO_CRC_MAGIC, physHeader.magic, strlen(CPIO_CRC_MAGIC))) 
105         return CPIO_BAD_MAGIC;
106
107     
108     GET_NUM_FIELD(physHeader.inode, chPtr->inode);
109     GET_NUM_FIELD(physHeader.mode, chPtr->mode);
110     GET_NUM_FIELD(physHeader.uid, chPtr->uid);
111     GET_NUM_FIELD(physHeader.gid, chPtr->gid);
112     GET_NUM_FIELD(physHeader.nlink, chPtr->nlink);
113     GET_NUM_FIELD(physHeader.mtime, chPtr->mtime);
114     GET_NUM_FIELD(physHeader.filesize, chPtr->size);
115
116     GET_NUM_FIELD(physHeader.devMajor, major);
117     GET_NUM_FIELD(physHeader.devMinor, minor);
118     chPtr->dev = makedev(major, minor);
119
120     GET_NUM_FIELD(physHeader.rdevMajor, major);
121     GET_NUM_FIELD(physHeader.rdevMinor, minor);
122     chPtr->rdev = makedev(major, minor);
123
124     GET_NUM_FIELD(physHeader.namesize, nameSize);
125
126     chPtr->path = malloc(nameSize + 1);
127     if (ourread(fd, chPtr->path, nameSize) != nameSize) {
128         free(chPtr->path);
129         return CPIO_BAD_HEADER;
130     }
131
132     chPtr->path[nameSize] = '\0';
133
134     padfd(fd, 4);
135
136     return 0;
137 }
138
139 int cpioFileMapCmp(const void * a, const void * b) {
140     const struct cpioFileMapping * first = a;
141     const struct cpioFileMapping * second = b;
142
143     return (strcmp(first->archivePath, second->archivePath));
144 }
145
146 /* This could trash files in the path! I'm not sure that's a good thing */
147 static int createDirectory(char * path) {
148     struct stat sb;
149     int dounlink;
150
151     if (!lstat(path, &sb)) {
152         if (S_ISDIR(sb.st_mode)) {
153             return 0;
154         } else if (S_ISLNK(sb.st_mode)) {
155             if (stat(path, &sb)) {
156                 if (errno != ENOENT) 
157                     return CPIO_STAT_FAILED;
158                 dounlink = 1;
159             } else {
160                 if (S_ISDIR(sb.st_mode))
161                     return 0;
162                 dounlink = 1;
163             }
164         } else {
165             dounlink = 1;
166         }
167
168         if (dounlink && unlink(path)) {
169             return CPIO_UNLINK_FAILED;
170         }
171     }
172
173     if (mkdir(path, 000))
174         return CPIO_MKDIR_FAILED;
175
176     return 0;
177 }
178
179 static int setInfo(struct cpioHeader * hdr) {
180     int rc = 0;
181     struct utimbuf stamp = { hdr->mtime, hdr->mtime };
182
183     if (!getuid() && !rc && chown(hdr->path, hdr->uid, hdr->gid))
184         rc = CPIO_CHOWN_FAILED;
185
186     if (!S_ISLNK(hdr->mode)) {
187         if (!rc && chmod(hdr->path, hdr->mode & 07777))
188             rc = CPIO_CHMOD_FAILED;
189         if (!rc && utime(hdr->path, &stamp))
190             rc = CPIO_UTIME_FAILED;
191     }
192
193     return rc;
194 }
195
196 static int checkDirectory(char * filename) {
197     static char * lastDir = NULL;
198     static int lastDirLength = 0;
199     static int lastDirAlloced = 0;
200     int length = strlen(filename);
201     char * buf;
202     char * chptr;
203     int rc = 0;
204
205     buf = alloca(length + 1);
206     strcpy(buf, filename);
207
208     for (chptr = buf + length - 1; chptr > buf; chptr--) {
209         if (*chptr == '/') break;
210     }
211
212     if (chptr == buf) return 0;     /* /filename - no directories */
213
214     *chptr = '\0';                  /* buffer is now just directories */
215
216     length = strlen(buf);
217     if (lastDirLength == length && !strcmp(buf, lastDir)) return 0;
218
219     if (lastDirAlloced < (length + 1)) {
220         lastDirAlloced = length + 100;
221         lastDir = realloc(lastDir, lastDirAlloced);
222     }
223
224     strcpy(lastDir, buf);
225     lastDirLength = length;
226
227     for (chptr = buf + 1; *chptr; chptr++) {
228         if (*chptr == '/') {
229             *chptr = '\0';
230             rc = createDirectory(buf);
231             *chptr = '/';
232             if (rc) return rc;
233         }
234     }
235     rc = createDirectory(buf);
236
237     return rc;
238 }
239
240 static int expandRegular(struct ourfd * fd, struct cpioHeader * hdr,
241                          cpioCallback cb, void * cbData) {
242     int out;
243     char buf[8192];
244     int bytesRead;
245     int left = hdr->size;
246     int rc = 0;
247     struct cpioCallbackInfo cbInfo;
248     struct stat sb;
249
250     if (!lstat(hdr->path, &sb))
251         if (unlink(hdr->path))
252             return CPIO_UNLINK_FAILED;
253
254     out = open(hdr->path, O_CREAT | O_WRONLY, 0);
255     if (out < 0) 
256         return CPIO_OPEN_FAILED;
257
258     cbInfo.file = hdr->path;
259     cbInfo.fileSize = hdr->size;
260
261     while (left) {
262         bytesRead = ourread(fd, buf, left < sizeof(buf) ? left : sizeof(buf));
263         if (bytesRead <= 0) {
264             rc = CPIO_READ_FAILED;
265             break;
266         }
267
268         if (write(out, buf, bytesRead) != bytesRead) {
269             rc = CPIO_READ_FAILED;
270             break;
271         }
272
273         left -= bytesRead;
274
275         /* don't call this with fileSize == fileComplete */
276         if (!rc && cb && left) {
277             cbInfo.fileComplete = hdr->size - left;
278             cbInfo.bytesProcessed = fd->pos;
279             cb(&cbInfo, cbData);
280         }
281     }
282
283     close(out);
284     
285     return rc;
286 }
287
288 static int expandSymlink(struct ourfd * fd, struct cpioHeader * hdr) {
289     char buf[2048];
290     struct stat sb;
291
292     if (!lstat(hdr->path, &sb))
293         if (unlink(hdr->path))
294             return CPIO_UNLINK_FAILED;
295
296     if ((hdr->size + 1)> sizeof(buf))
297         return CPIO_INTERNAL;
298
299     if (ourread(fd, buf, hdr->size) != hdr->size)
300         return CPIO_READ_FAILED;
301
302     buf[hdr->size] = '\0';
303
304     if (symlink(buf, hdr->path))
305         return CPIO_SYMLINK_FAILED;
306
307     return 0;
308 }
309
310 static int expandFifo(struct ourfd * fd, struct cpioHeader * hdr) {
311     struct stat sb;
312
313     if (!lstat(hdr->path, &sb)) {
314         if (S_ISFIFO(sb.st_mode)) return 0;
315
316         if (unlink(hdr->path))
317             return CPIO_UNLINK_FAILED;
318     }
319
320     if (mkfifo(hdr->path, 0))
321         return CPIO_MKFIFO_FAILED;
322
323     return 0; 
324 }
325
326 static int expandDevice(struct ourfd * fd, struct cpioHeader * hdr) {
327     struct stat sb;
328
329     if (!lstat(hdr->path, &sb))
330         if (unlink(hdr->path))
331             return CPIO_UNLINK_FAILED;
332
333     if (mknod(hdr->path, hdr->mode & (~0777), hdr->rdev))
334         return CPIO_MKNOD_FAILED;
335     
336     return 0;
337 }
338
339 int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings, 
340                        int numMappings, cpioCallback cb, void * cbData,
341                        char ** failedFile) {
342     struct cpioHeader ch;
343     struct ourfd fd;
344     int rc = 0;
345     struct cpioFileMapping * map = NULL;
346     struct cpioFileMapping needle;
347     mode_t cpioMode;
348     int olderr;
349     struct cpioCallbackInfo cbInfo;
350
351     fd.fd = stream;
352     fd.pos = 0;
353
354     do {
355         if ((rc = getNextHeader(&fd, &ch))) {
356             printf("error %d reading header: %s\n", rc, strerror(errno));
357             exit(1);
358         }
359
360         if (!strcmp(ch.path, TRAILER)) {
361             free(ch.path);
362             break;
363         }
364
365         if (mappings) {
366             needle.archivePath = ch.path;
367             map = bsearch(&needle, mappings, numMappings, sizeof(needle),
368                           cpioFileMapCmp);
369         }
370
371         if (!mappings || map) {
372             cpioMode = ch.mode;
373
374             if (map) {
375                 if (map->mapFlags & CPIO_MAP_PATH) {
376                     free(ch.path);
377                     ch.path = strdup(map->finalPath);
378                 } 
379
380                 if (map->mapFlags & CPIO_MAP_MODE)
381                     ch.mode = map->finalMode;
382                 if (map->mapFlags & CPIO_MAP_UID)
383                     ch.uid = map->finalUid;
384                 if (map->mapFlags & CPIO_MAP_GID)
385                     ch.gid = map->finalGid;
386             }
387
388             rc = checkDirectory(ch.path);
389
390             if (!rc) {
391                 if (S_ISREG(ch.mode))
392                     rc = expandRegular(&fd, &ch, cb, cbData);
393                 else if (S_ISDIR(ch.mode))
394                     rc = createDirectory(ch.path);
395                 else if (S_ISLNK(ch.mode))
396                     rc = expandSymlink(&fd, &ch);
397                 else if (S_ISFIFO(ch.mode))
398                     rc = expandFifo(&fd, &ch);
399                 else if (S_ISCHR(ch.mode) || S_ISBLK(ch.mode))
400                     rc = expandDevice(&fd, &ch);
401                 else if (S_ISSOCK(ch.mode)) {
402                     /* should we do something here??? */
403                     rc = 0;
404                 } else {
405                     rc = CPIO_INTERNAL;
406                 }
407             }
408
409             if (!rc)
410                 rc = setInfo(&ch);
411
412             if (rc) {
413                 *failedFile = strdup(ch.path);
414
415                 olderr = errno;
416                 unlink(ch.path);
417                 errno = olderr;
418             }
419         }
420
421         padfd(&fd, 4);
422
423         if (!rc && cb) {
424             cbInfo.file = ch.path;
425             cbInfo.fileSize = ch.size;
426             cbInfo.fileComplete = ch.size;
427             cbInfo.bytesProcessed = fd.pos;
428             cb(&cbInfo, cbData);
429         }
430
431         free(ch.path);
432     } while (1 && !rc);
433
434     return rc;
435 }