Added support for hard links.
[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 hardLink {
36     char ** files;              /* there are nlink of these */
37     dev_t dev;
38     ino_t inode;
39     int nlink;                  
40     int linksLeft;
41     int createdPath;
42     struct hardLink * next;
43 };
44
45 struct cpioCrcPhysicalHeader {
46     char magic[6];
47     char inode[8];
48     char mode[8];
49     char uid[8];
50     char gid[8];
51     char nlink[8];
52     char mtime[8];
53     char filesize[8];
54     char devMajor[8];
55     char devMinor[8];
56     char rdevMajor[8];
57     char rdevMinor[8];
58     char namesize[8];
59     char checksum[8];                   /* ignored !! */
60 };
61
62 struct cpioHeader {
63     ino_t inode;
64     mode_t mode;
65     uid_t uid;
66     gid_t gid;
67     int nlink;
68     time_t mtime;
69     long size;
70     dev_t dev, rdev;
71     char * path;
72 };
73
74 static inline loff_t ourread(struct ourfd * thefd, void * buf, size_t size) {
75     loff_t i;
76
77     i = gzread(thefd->fd, buf, size);
78     thefd->pos += i;
79     
80     return i;
81 }
82
83 static inline void padfd(struct ourfd * fd, int modulo) {
84     int buf[10];
85     int amount;
86     
87     amount = (modulo - fd->pos % modulo) % modulo;
88     ourread(fd, buf, amount);
89 }
90
91 static int strntoul(const char * str, char ** endptr, int base, int num) {
92     char * buf;
93
94     buf = alloca(num + 1);
95     strncpy(buf, str, num);
96     buf[num] = '\0';
97
98     return strtoul(buf, endptr, base);
99 }
100
101 #define GET_NUM_FIELD(phys, log) \
102         log = strntoul(phys, &end, 16, sizeof(phys)); \
103         if (*end) return CPIO_BAD_HEADER;
104
105 static int getNextHeader(struct ourfd * fd, struct cpioHeader * chPtr) {
106     struct cpioCrcPhysicalHeader physHeader;
107     int nameSize;
108     char * end;
109     int major, minor;
110
111     if (ourread(fd, &physHeader, sizeof(physHeader)) != sizeof(physHeader)) 
112         return CPIO_READ_FAILED;
113
114     if (strncmp(CPIO_CRC_MAGIC, physHeader.magic, strlen(CPIO_CRC_MAGIC))) 
115         return CPIO_BAD_MAGIC;
116
117     
118     GET_NUM_FIELD(physHeader.inode, chPtr->inode);
119     GET_NUM_FIELD(physHeader.mode, chPtr->mode);
120     GET_NUM_FIELD(physHeader.uid, chPtr->uid);
121     GET_NUM_FIELD(physHeader.gid, chPtr->gid);
122     GET_NUM_FIELD(physHeader.nlink, chPtr->nlink);
123     GET_NUM_FIELD(physHeader.mtime, chPtr->mtime);
124     GET_NUM_FIELD(physHeader.filesize, chPtr->size);
125
126     GET_NUM_FIELD(physHeader.devMajor, major);
127     GET_NUM_FIELD(physHeader.devMinor, minor);
128     chPtr->dev = makedev(major, minor);
129
130     GET_NUM_FIELD(physHeader.rdevMajor, major);
131     GET_NUM_FIELD(physHeader.rdevMinor, minor);
132     chPtr->rdev = makedev(major, minor);
133
134     GET_NUM_FIELD(physHeader.namesize, nameSize);
135
136     chPtr->path = malloc(nameSize + 1);
137     if (ourread(fd, chPtr->path, nameSize) != nameSize) {
138         free(chPtr->path);
139         return CPIO_BAD_HEADER;
140     }
141
142     chPtr->path[nameSize] = '\0';
143
144     padfd(fd, 4);
145
146     return 0;
147 }
148
149 int cpioFileMapCmp(const void * a, const void * b) {
150     const struct cpioFileMapping * first = a;
151     const struct cpioFileMapping * second = b;
152
153     return (strcmp(first->archivePath, second->archivePath));
154 }
155
156 /* This could trash files in the path! I'm not sure that's a good thing */
157 static int createDirectory(char * path) {
158     struct stat sb;
159     int dounlink;
160
161     if (!lstat(path, &sb)) {
162         if (S_ISDIR(sb.st_mode)) {
163             return 0;
164         } else if (S_ISLNK(sb.st_mode)) {
165             if (stat(path, &sb)) {
166                 if (errno != ENOENT) 
167                     return CPIO_STAT_FAILED;
168                 dounlink = 1;
169             } else {
170                 if (S_ISDIR(sb.st_mode))
171                     return 0;
172                 dounlink = 1;
173             }
174         } else {
175             dounlink = 1;
176         }
177
178         if (dounlink && unlink(path)) {
179             return CPIO_UNLINK_FAILED;
180         }
181     }
182
183     if (mkdir(path, 000))
184         return CPIO_MKDIR_FAILED;
185
186     return 0;
187 }
188
189 static int setInfo(struct cpioHeader * hdr) {
190     int rc = 0;
191     struct utimbuf stamp = { hdr->mtime, hdr->mtime };
192
193     if (!getuid() && !rc && chown(hdr->path, hdr->uid, hdr->gid))
194         rc = CPIO_CHOWN_FAILED;
195
196     if (!S_ISLNK(hdr->mode)) {
197         if (!rc && chmod(hdr->path, hdr->mode & 07777))
198             rc = CPIO_CHMOD_FAILED;
199         if (!rc && utime(hdr->path, &stamp))
200             rc = CPIO_UTIME_FAILED;
201     }
202
203     return rc;
204 }
205
206 static int checkDirectory(char * filename) {
207     static char * lastDir = NULL;
208     static int lastDirLength = 0;
209     static int lastDirAlloced = 0;
210     int length = strlen(filename);
211     char * buf;
212     char * chptr;
213     int rc = 0;
214
215     buf = alloca(length + 1);
216     strcpy(buf, filename);
217
218     for (chptr = buf + length - 1; chptr > buf; chptr--) {
219         if (*chptr == '/') break;
220     }
221
222     if (chptr == buf) return 0;     /* /filename - no directories */
223
224     *chptr = '\0';                  /* buffer is now just directories */
225
226     length = strlen(buf);
227     if (lastDirLength == length && !strcmp(buf, lastDir)) return 0;
228
229     if (lastDirAlloced < (length + 1)) {
230         lastDirAlloced = length + 100;
231         lastDir = realloc(lastDir, lastDirAlloced);
232     }
233
234     strcpy(lastDir, buf);
235     lastDirLength = length;
236
237     for (chptr = buf + 1; *chptr; chptr++) {
238         if (*chptr == '/') {
239             *chptr = '\0';
240             rc = createDirectory(buf);
241             *chptr = '/';
242             if (rc) return rc;
243         }
244     }
245     rc = createDirectory(buf);
246
247     return rc;
248 }
249
250 static int expandRegular(struct ourfd * fd, struct cpioHeader * hdr,
251                          cpioCallback cb, void * cbData) {
252     int out;
253     char buf[8192];
254     int bytesRead;
255     int left = hdr->size;
256     int rc = 0;
257     struct cpioCallbackInfo cbInfo;
258     struct stat sb;
259
260     if (!lstat(hdr->path, &sb))
261         if (unlink(hdr->path))
262             return CPIO_UNLINK_FAILED;
263
264     out = open(hdr->path, O_CREAT | O_WRONLY, 0);
265     if (out < 0) 
266         return CPIO_OPEN_FAILED;
267
268     cbInfo.file = hdr->path;
269     cbInfo.fileSize = hdr->size;
270
271     while (left) {
272         bytesRead = ourread(fd, buf, left < sizeof(buf) ? left : sizeof(buf));
273         if (bytesRead <= 0) {
274             rc = CPIO_READ_FAILED;
275             break;
276         }
277
278         if (write(out, buf, bytesRead) != bytesRead) {
279             rc = CPIO_READ_FAILED;
280             break;
281         }
282
283         left -= bytesRead;
284
285         /* don't call this with fileSize == fileComplete */
286         if (!rc && cb && left) {
287             cbInfo.fileComplete = hdr->size - left;
288             cbInfo.bytesProcessed = fd->pos;
289             cb(&cbInfo, cbData);
290         }
291     }
292
293     close(out);
294     
295     return rc;
296 }
297
298 static int expandSymlink(struct ourfd * fd, struct cpioHeader * hdr) {
299     char buf[2048];
300     struct stat sb;
301
302     if (!lstat(hdr->path, &sb))
303         if (unlink(hdr->path))
304             return CPIO_UNLINK_FAILED;
305
306     if ((hdr->size + 1)> sizeof(buf))
307         return CPIO_INTERNAL;
308
309     if (ourread(fd, buf, hdr->size) != hdr->size)
310         return CPIO_READ_FAILED;
311
312     buf[hdr->size] = '\0';
313
314     if (symlink(buf, hdr->path))
315         return CPIO_SYMLINK_FAILED;
316
317     return 0;
318 }
319
320 static int expandFifo(struct ourfd * fd, struct cpioHeader * hdr) {
321     struct stat sb;
322
323     if (!lstat(hdr->path, &sb)) {
324         if (S_ISFIFO(sb.st_mode)) return 0;
325
326         if (unlink(hdr->path))
327             return CPIO_UNLINK_FAILED;
328     }
329
330     if (mkfifo(hdr->path, 0))
331         return CPIO_MKFIFO_FAILED;
332
333     return 0; 
334 }
335
336 static int expandDevice(struct ourfd * fd, struct cpioHeader * hdr) {
337     struct stat sb;
338
339     if (!lstat(hdr->path, &sb))
340         if (unlink(hdr->path))
341             return CPIO_UNLINK_FAILED;
342
343     if (mknod(hdr->path, hdr->mode & (~0777), hdr->rdev))
344         return CPIO_MKNOD_FAILED;
345     
346     return 0;
347 }
348
349 static void freeLink(struct hardLink * li) {
350     int i;
351
352     for (i = 0; i < li->nlink; i++) {
353         if (li->files[i]) free(li->files[i]);
354     }
355     free(li->files);
356 }
357
358 static int createLinks(struct hardLink * li, char ** failedFile) {
359     int i;
360     struct stat sb;
361
362     for (i = 0; i < li->nlink; i++) {
363         if (i == li->createdPath) continue;
364         if (!li->files[i]) continue;
365
366         if (!lstat(li->files[i], &sb)) {
367             if (unlink(li->files[i])) {
368                 *failedFile = strdup(li->files[i]);
369                 return CPIO_UNLINK_FAILED;
370             }
371         }
372
373         if (link(li->files[li->createdPath], li->files[i])) {
374             *failedFile = strdup(li->files[i]);
375             return CPIO_LINK_FAILED;
376         }
377
378         free(li->files[i]);
379         li->files[i] = NULL;
380         li->linksLeft--;
381     }
382
383     return 0;
384 }
385
386 int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings, 
387                        int numMappings, cpioCallback cb, void * cbData,
388                        char ** failedFile) {
389     struct cpioHeader ch;
390     struct ourfd fd;
391     int rc = 0;
392     int linkNum = 0;
393     struct cpioFileMapping * map = NULL;
394     struct cpioFileMapping needle;
395     mode_t cpioMode;
396     int olderr;
397     struct cpioCallbackInfo cbInfo;
398     struct hardLink * links = NULL;
399     struct hardLink * li = NULL;
400
401     fd.fd = stream;
402     fd.pos = 0;
403
404     *failedFile = NULL;
405
406     do {
407         if ((rc = getNextHeader(&fd, &ch))) {
408             printf("error %d reading header: %s\n", rc, strerror(errno));
409             exit(1);
410         }
411
412         if (!strcmp(ch.path, TRAILER)) {
413             free(ch.path);
414             break;
415         }
416
417         if (mappings) {
418             needle.archivePath = ch.path;
419             map = bsearch(&needle, mappings, numMappings, sizeof(needle),
420                           cpioFileMapCmp);
421         }
422
423         if (!mappings || map) {
424             cpioMode = ch.mode;
425
426             if (map) {
427                 if (map->mapFlags & CPIO_MAP_PATH) {
428                     free(ch.path);
429                     ch.path = strdup(map->finalPath);
430                 } 
431
432                 if (map->mapFlags & CPIO_MAP_MODE)
433                     ch.mode = map->finalMode;
434                 if (map->mapFlags & CPIO_MAP_UID)
435                     ch.uid = map->finalUid;
436                 if (map->mapFlags & CPIO_MAP_GID)
437                     ch.gid = map->finalGid;
438             }
439
440             /* This won't get hard linked symlinks right, but I can't seem 
441                to create those anyway */
442
443             if (ch.nlink > 1) {
444                 li = links;
445                 for (li = links; li; li = li->next) {
446                     if (li->inode == ch.inode && li->dev == ch.dev) break;
447                 }
448
449                 if (!li) {
450                     li = malloc(sizeof(*li));
451                     li->inode = ch.inode;
452                     li->dev = ch.dev;
453                     li->nlink = ch.nlink;
454                     li->linksLeft = ch.nlink;
455                     li->createdPath = -1;
456                     li->files = calloc(sizeof(char *), li->nlink);
457                     li->next = links;
458                     links = li;
459                 }
460
461                 for (linkNum = 0; linkNum < li->nlink; linkNum++)
462                     if (!li->files[linkNum]) break;
463                 li->files[linkNum] = strdup(ch.path);
464             }
465                 
466             if ((ch.nlink > 1) && S_ISREG(ch.mode) && !ch.size &&
467                 li->createdPath == -1) {
468                 /* defer file creation */
469             } else if ((ch.nlink > 1) && (li->createdPath != -1)) {
470                 createLinks(li, failedFile);
471             } else {
472                 rc = checkDirectory(ch.path);
473
474                 if (!rc) {
475                     if (S_ISREG(ch.mode))
476                         rc = expandRegular(&fd, &ch, cb, cbData);
477                     else if (S_ISDIR(ch.mode))
478                         rc = createDirectory(ch.path);
479                     else if (S_ISLNK(ch.mode))
480                         rc = expandSymlink(&fd, &ch);
481                     else if (S_ISFIFO(ch.mode))
482                         rc = expandFifo(&fd, &ch);
483                     else if (S_ISCHR(ch.mode) || S_ISBLK(ch.mode))
484                         rc = expandDevice(&fd, &ch);
485                     else if (S_ISSOCK(ch.mode)) {
486                         /* this mimicks cpio but probably isnt' right */
487                         rc = expandFifo(&fd, &ch);
488                     } else {
489                         rc = CPIO_INTERNAL;
490                     }
491                 }
492
493                 if (!rc)
494                     rc = setInfo(&ch);
495
496                 if (ch.nlink > 1) {
497                     li->createdPath = linkNum;
498                     li->linksLeft--;
499                     rc = createLinks(li, failedFile);
500                 }
501             }
502
503             if (rc && !*failedFile) {
504                 *failedFile = strdup(ch.path);
505
506                 olderr = errno;
507                 unlink(ch.path);
508                 errno = olderr;
509             }
510         }
511
512         padfd(&fd, 4);
513
514         if (!rc && cb) {
515             cbInfo.file = ch.path;
516             cbInfo.fileSize = ch.size;
517             cbInfo.fileComplete = ch.size;
518             cbInfo.bytesProcessed = fd.pos;
519             cb(&cbInfo, cbData);
520         }
521
522         free(ch.path);
523     } while (1 && !rc);
524
525     li = links;
526     while (li && !rc) {
527         if (li->linksLeft) {
528             if (li->createdPath == -1)
529                 rc = CPIO_INTERNAL;
530             else 
531                 rc = createLinks(li, failedFile);
532         }
533
534         freeLink(li);
535
536         links = li;
537         li = li->next;
538         free(links);
539         links = li;
540     }
541
542     li = links;
543     /* if an error got us here links will still be eating some memory */
544     while (li) {
545         freeLink(li);
546         links = li;
547         li = li->next;
548         free(links);
549     }
550
551     return rc;
552 }