ddff7a4f729d331354ef8d2179709a24ef171df6
[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 (!access(path, X_OK)) {
152         if (lstat(path, &sb))
153             return CPIO_STAT_FAILED;
154
155         if (S_ISDIR(sb.st_mode)) {
156             return 0;
157         } else if (S_ISLNK(sb.st_mode)) {
158             if (stat(path, &sb)) {
159                 if (errno != ENOENT) 
160                     return CPIO_STAT_FAILED;
161                 dounlink = 1;
162             } else {
163                 if (S_ISDIR(sb.st_mode))
164                     return 0;
165                 dounlink = 1;
166             }
167         } else {
168             dounlink = 1;
169         }
170
171         if (dounlink && unlink(path)) {
172             return CPIO_UNLINK_FAILED;
173         }
174     }
175
176     if (mkdir(path, 000))
177         return CPIO_MKDIR_FAILED;
178
179     return 0;
180 }
181
182 static int setInfo(struct cpioHeader * hdr) {
183     int rc = 0;
184     struct utimbuf stamp = { hdr->mtime, hdr->mtime };
185
186     if (!getuid() && !rc && chown(hdr->path, hdr->uid, hdr->gid))
187         rc = CPIO_CHOWN_FAILED;
188
189     if (!S_ISLNK(hdr->mode)) {
190         if (!rc && chmod(hdr->path, hdr->mode & 07777))
191             rc = CPIO_CHMOD_FAILED;
192         if (!rc && utime(hdr->path, &stamp))
193             rc = CPIO_UTIME_FAILED;
194     }
195
196     return rc;
197 }
198
199 static int checkDirectory(char * filename) {
200     static char * lastDir = NULL;
201     static int lastDirLength = 0;
202     static int lastDirAlloced = 0;
203     int length = strlen(filename);
204     char * buf;
205     char * chptr;
206     int rc = 0;
207
208     buf = alloca(length + 1);
209     strcpy(buf, filename);
210
211     for (chptr = buf + length - 1; chptr > buf; chptr--) {
212         if (*chptr == '/') break;
213     }
214
215     if (chptr == buf) return 0;     /* /filename - no directories */
216
217     *chptr = '\0';                  /* buffer is now just directories */
218
219     length = strlen(buf);
220     if (lastDirLength == length && !strcmp(buf, lastDir)) return 0;
221
222     if (lastDirAlloced < (length + 1)) {
223         lastDirAlloced = length + 100;
224         lastDir = realloc(lastDir, lastDirAlloced);
225     }
226
227     strcpy(lastDir, buf);
228     lastDirLength = length;
229
230     for (chptr = buf + 1; *chptr; chptr++) {
231         if (*chptr == '/') {
232             *chptr = '\0';
233             rc = createDirectory(buf);
234             *chptr = '/';
235             if (rc) return rc;
236         }
237     }
238     rc = createDirectory(buf);
239
240     return rc;
241 }
242
243 static int expandRegular(struct ourfd * fd, struct cpioHeader * hdr) {
244     int out;
245     char buf[16384];
246     int bytesRead;
247     int left = hdr->size;
248     int rc = 0;
249
250     if (!access(hdr->path, X_OK))
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     while (left) {
259         bytesRead = ourread(fd, buf, left < sizeof(buf) ? left : sizeof(buf));
260         if (bytesRead <= 0) {
261             rc = CPIO_READ_FAILED;
262             break;
263         }
264
265         if (write(out, buf, bytesRead) != bytesRead) {
266             rc = CPIO_READ_FAILED;
267             break;
268         }
269
270         left -= bytesRead;
271     }
272
273     close(out);
274     
275     return rc;
276 }
277
278 static int expandSymlink(struct ourfd * fd, struct cpioHeader * hdr) {
279     char buf[2048];
280
281     if (!access(hdr->path, X_OK))
282         if (unlink(hdr->path))
283             return CPIO_UNLINK_FAILED;
284
285     if ((hdr->size + 1)> sizeof(buf))
286         return CPIO_INTERNAL;
287
288     if (ourread(fd, buf, hdr->size) != hdr->size)
289         return CPIO_READ_FAILED;
290
291     buf[hdr->size] = '\0';
292
293     if (symlink(buf, hdr->path))
294         return CPIO_SYMLINK_FAILED;
295
296     return 0;
297 }
298
299 static int expandFifo(struct ourfd * fd, struct cpioHeader * hdr) {
300     struct stat sb;
301
302     if (!access(hdr->path, X_OK)) {
303         if (lstat(hdr->path, &sb))
304             return CPIO_STAT_FAILED;
305
306         if (S_ISFIFO(sb.st_mode)) return 0;
307
308         if (unlink(hdr->path))
309             return CPIO_UNLINK_FAILED;
310     }
311
312     if (mkfifo(hdr->path, 0))
313         return CPIO_MKFIFO_FAILED;
314
315     return 0; 
316 }
317
318 static int expandDevice(struct ourfd * fd, struct cpioHeader * hdr) {
319     if (!access(hdr->path, X_OK))
320         if (unlink(hdr->path))
321             return CPIO_UNLINK_FAILED;
322
323     if (mknod(hdr->path, hdr->mode & (~0777), hdr->rdev))
324         return CPIO_MKNOD_FAILED;
325     
326     return 0;
327 }
328
329 int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings, 
330                        int numMappings, cpioCallback cb, char ** failedFile) {
331     struct cpioHeader ch;
332     struct ourfd fd;
333     int rc = 0;
334     struct cpioFileMapping * map = NULL;
335     struct cpioFileMapping needle;
336     mode_t cpioMode;
337     int olderr;
338
339     fd.fd = stream;
340     fd.pos = 0;
341
342     do {
343         if ((rc = getNextHeader(&fd, &ch))) {
344             printf("error %d reading header: %s\n", rc, strerror(errno));
345             exit(1);
346         }
347
348         if (!strcmp(ch.path, TRAILER)) {
349             free(ch.path);
350             break;
351         }
352
353         if (mappings) {
354             needle.archivePath = ch.path;
355             map = bsearch(&needle, mappings, numMappings, sizeof(needle),
356                           cpioFileMapCmp);
357         }
358
359         if (!mappings || map) {
360             cpioMode = ch.mode;
361
362             if (map) {
363                 if (map->mapFlags & CPIO_MAP_PATH) {
364                     free(ch.path);
365                     ch.path = strdup(map->finalPath);
366                 } 
367
368                 if (map->mapFlags & CPIO_MAP_MODE)
369                     ch.mode = map->finalMode;
370                 if (map->mapFlags & CPIO_MAP_UID)
371                     ch.uid = map->finalUid;
372                 if (map->mapFlags & CPIO_MAP_GID)
373                     ch.gid = map->finalGid;
374             }
375
376             rc = checkDirectory(ch.path);
377
378             if (!rc) {
379                 if (S_ISREG(ch.mode))
380                     rc = expandRegular(&fd, &ch);
381                 else if (S_ISDIR(ch.mode))
382                     rc = createDirectory(ch.path);
383                 else if (S_ISLNK(ch.mode))
384                     rc = expandSymlink(&fd, &ch);
385                 else if (S_ISFIFO(ch.mode))
386                     rc = expandFifo(&fd, &ch);
387                 else if (S_ISCHR(ch.mode) || S_ISBLK(ch.mode))
388                     rc = expandDevice(&fd, &ch);
389                 else if (S_ISSOCK(ch.mode)) {
390                     /* should we do something here??? */
391                     rc = 0;
392                 } else {
393                     rc = CPIO_INTERNAL;
394                 }
395             }
396
397             if (!rc)
398                 rc = setInfo(&ch);
399
400             if (rc) {
401                 *failedFile = strdup(ch.path);
402
403                 olderr = errno;
404                 unlink(ch.path);
405                 errno = olderr;
406             }
407         }
408
409         padfd(&fd, 4);
410         free(ch.path);
411     } while (1 && !rc);
412
413     return rc;
414 }