1 /** \ingroup payload
2  * \file lib/cpio.c
3  *  Handle cpio payloads within rpm packages.
4  *
5  * \warning FIXME: We don't translate between cpio and system mode bits! These
6  * should both be the same, but really odd things are going to happen if
7  * that's not true!
8  */
9 
10 #include "system.h"
11 
12 #if MAJOR_IN_MKDEV
13 #include <sys/mkdev.h>
14 #elif MAJOR_IN_SYSMACROS
15 #include <sys/sysmacros.h>
16 #else
17 #include <sys/types.h> /* already included from system.h */
18 #endif
19 #include <string.h>
20 #include <fcntl.h>
21 
22 #include <rpm/rpmio.h>
23 #include <rpm/rpmlog.h>
24 #include <rpm/rpmstring.h>
25 
26 #include "lib/cpio.h"
27 #include "lib/rpmarchive.h"
28 
29 #include "debug.h"
30 
31 
32 struct rpmcpio_s {
33     FD_t fd;
34     char mode;
35     off_t offset;
36     off_t fileend;
37 };
38 
39 /*
40  * Size limit for individual files in "new ascii format" cpio archives.
41  * The max size of the entire archive is unlimited from cpio POV,
42  * but subject to filesystem limitations.
43  */
44 #define CPIO_FILESIZE_MAX UINT32_MAX
45 
46 #define CPIO_NEWC_MAGIC	"070701"
47 #define CPIO_CRC_MAGIC	"070702"
48 #define CPIO_STRIPPED_MAGIC "07070X"
49 #define CPIO_TRAILER	"TRAILER!!!"
50 
51 /** \ingroup payload
52  * Cpio archive header information.
53  */
54 struct cpioCrcPhysicalHeader {
55     /* char magic[6]; handled separately */
56     char inode[8];
57     char mode[8];
58     char uid[8];
59     char gid[8];
60     char nlink[8];
61     char mtime[8];
62     char filesize[8];
63     char devMajor[8];
64     char devMinor[8];
65     char rdevMajor[8];
66     char rdevMinor[8];
67     char namesize[8];
68     char checksum[8];			/* ignored !! */
69 };
70 
71 #define	PHYS_HDR_SIZE	104		/* Don't depend on sizeof(struct) */
72 
73 struct cpioStrippedPhysicalHeader {
74     /* char magic[6]; handled separately */
75     char fx[8];
76 };
77 
78 #define STRIPPED_PHYS_HDR_SIZE 8       /* Don't depend on sizeof(struct) */
79 
rpmcpioOpen(FD_t fd,char mode)80 rpmcpio_t rpmcpioOpen(FD_t fd, char mode)
81 {
82     if ((mode & O_ACCMODE) != O_RDONLY &&
83         (mode & O_ACCMODE) != O_WRONLY)
84         return NULL;
85 
86     rpmcpio_t cpio = xcalloc(1, sizeof(*cpio));
87     cpio->fd = fdLink(fd);
88     cpio->mode = mode;
89     cpio->offset = 0;
90     return cpio;
91 }
92 
rpmcpioTell(rpmcpio_t cpio)93 off_t rpmcpioTell(rpmcpio_t cpio)
94 {
95     return cpio->offset;
96 }
97 
98 
99 /**
100  * Convert string to unsigned integer (with buffer size check).
101  * @param str		input string
102  * @retval endptr	address of 1st character not processed
103  * @param base		numerical conversion base
104  * @param num		max no. of bytes to read
105  * @return		converted integer
106  */
strntoul(const char * str,char ** endptr,int base,size_t num)107 static unsigned long strntoul(const char *str,char **endptr, int base, size_t num)
108 {
109     char buf[num+1], * end;
110     unsigned long ret;
111 
112     strncpy(buf, str, num);
113     buf[num] = '\0';
114 
115     ret = strtoul(buf, &end, base);
116     if (*end != '\0')
117 	*endptr = ((char *)str) + (end - buf);	/* XXX discards const */
118     else
119 	*endptr = ((char *)str) + strlen(buf);
120 
121     return ret;
122 }
123 
124 
rpmcpioWritePad(rpmcpio_t cpio,ssize_t modulo)125 static int rpmcpioWritePad(rpmcpio_t cpio, ssize_t modulo)
126 {
127     char buf[modulo];
128     ssize_t left, written;
129     memset(buf, 0, modulo);
130     left = (modulo - ((cpio->offset) % modulo)) % modulo;
131     if (left <= 0)
132         return 0;
133     written = Fwrite(&buf, left, 1, cpio->fd);
134     if (written != left) {
135         return RPMERR_WRITE_FAILED;
136     }
137     cpio->offset += written;
138     return 0;
139 }
140 
rpmcpioReadPad(rpmcpio_t cpio)141 static int rpmcpioReadPad(rpmcpio_t cpio)
142 {
143     ssize_t modulo = 4;
144     char buf[4];
145     ssize_t left, read;
146     left = (modulo - (cpio->offset % modulo)) % modulo;
147     if (left <= 0)
148         return 0;
149     read = Fread(&buf, left, 1, cpio->fd);
150     cpio->offset += read;
151     if (read != left) {
152         return RPMERR_READ_FAILED;
153     }
154     return 0;
155 }
156 
157 #define GET_NUM_FIELD(phys, log) \
158 	\
159 	log = strntoul(phys, &end, 16, sizeof(phys)); \
160 	\
161 	if ( (end - phys) != sizeof(phys) ) return RPMERR_BAD_HEADER;
162 #define SET_NUM_FIELD(phys, val, space) \
163 	sprintf(space, "%8.8lx", (unsigned long) (val)); \
164 	\
165 	memcpy(phys, space, 8) \
166 
rpmcpioTrailerWrite(rpmcpio_t cpio)167 static int rpmcpioTrailerWrite(rpmcpio_t cpio)
168 {
169     struct cpioCrcPhysicalHeader hdr;
170     int rc;
171     size_t written;
172 
173     if (cpio->fileend != cpio->offset) {
174         return RPMERR_WRITE_FAILED;
175     }
176 
177     rc = rpmcpioWritePad(cpio, 4);
178     if (rc)
179         return rc;
180 
181     memset(&hdr, '0', PHYS_HDR_SIZE);
182     memcpy(&hdr.nlink, "00000001", 8);
183     memcpy(&hdr.namesize, "0000000b", 8);
184 
185     written = Fwrite(CPIO_NEWC_MAGIC, 6, 1, cpio->fd);
186     cpio->offset += written;
187     if (written != 6) {
188         return RPMERR_WRITE_FAILED;
189     }
190 
191     written = Fwrite(&hdr, PHYS_HDR_SIZE, 1, cpio->fd);
192     cpio->offset += written;
193     if (written != PHYS_HDR_SIZE) {
194         return RPMERR_WRITE_FAILED;
195     }
196     written = Fwrite(&CPIO_TRAILER, sizeof(CPIO_TRAILER), 1, cpio->fd);
197     cpio->offset += written;
198     if (written != sizeof(CPIO_TRAILER)) {
199         return RPMERR_WRITE_FAILED;
200     }
201 
202     /*
203      * XXX GNU cpio pads to 512 bytes. This may matter for
204      * tape device(s) and/or concatenated cpio archives.
205      */
206 
207     rc = rpmcpioWritePad(cpio, 4);
208 
209     return rc;
210 }
211 
rpmcpioHeaderWrite(rpmcpio_t cpio,char * path,struct stat * st)212 int rpmcpioHeaderWrite(rpmcpio_t cpio, char * path, struct stat * st)
213 {
214     struct cpioCrcPhysicalHeader hdr_s;
215     struct cpioCrcPhysicalHeader * hdr = &hdr_s;
216     char field[64];
217     size_t len, written;
218     dev_t dev;
219     int rc = 0;
220 
221     if ((cpio->mode & O_ACCMODE) != O_WRONLY) {
222         return RPMERR_WRITE_FAILED;
223     }
224 
225     if (cpio->fileend != cpio->offset) {
226         return RPMERR_WRITE_FAILED;
227     }
228 
229     if (st->st_size >= CPIO_FILESIZE_MAX) {
230 	return RPMERR_FILE_SIZE;
231     }
232 
233     rc = rpmcpioWritePad(cpio, 4);
234     if (rc) {
235         return rc;
236     }
237 
238     SET_NUM_FIELD(hdr->inode, st->st_ino, field);
239     SET_NUM_FIELD(hdr->mode, st->st_mode, field);
240     SET_NUM_FIELD(hdr->uid, st->st_uid, field);
241     SET_NUM_FIELD(hdr->gid, st->st_gid, field);
242     SET_NUM_FIELD(hdr->nlink, st->st_nlink, field);
243     SET_NUM_FIELD(hdr->mtime, st->st_mtime, field);
244     SET_NUM_FIELD(hdr->filesize, st->st_size, field);
245 
246     dev = major(st->st_dev); SET_NUM_FIELD(hdr->devMajor, dev, field);
247     dev = minor(st->st_dev); SET_NUM_FIELD(hdr->devMinor, dev, field);
248     dev = major(st->st_rdev); SET_NUM_FIELD(hdr->rdevMajor, dev, field);
249     dev = minor(st->st_rdev); SET_NUM_FIELD(hdr->rdevMinor, dev, field);
250 
251     len = strlen(path) + 1;
252     SET_NUM_FIELD(hdr->namesize, len, field);
253 
254     memcpy(hdr->checksum, "00000000", 8);
255 
256     written = Fwrite(CPIO_NEWC_MAGIC, 6, 1, cpio->fd);
257     cpio->offset += written;
258     if (written != 6) {
259         return RPMERR_WRITE_FAILED;
260     }
261 
262     written = Fwrite(hdr, PHYS_HDR_SIZE, 1, cpio->fd);
263     cpio->offset += written;
264     if (written != PHYS_HDR_SIZE) {
265         return RPMERR_WRITE_FAILED;
266     }
267 
268     written = Fwrite(path, len, 1, cpio->fd);
269     cpio->offset += written;
270     if (written != len) {
271         return RPMERR_WRITE_FAILED;
272     }
273 
274     rc = rpmcpioWritePad(cpio, 4);
275 
276     cpio->fileend = cpio->offset + st->st_size;
277 
278     return rc;
279 }
280 
rpmcpioStrippedHeaderWrite(rpmcpio_t cpio,int fx,off_t fsize)281 int rpmcpioStrippedHeaderWrite(rpmcpio_t cpio, int fx, off_t fsize)
282 {
283     struct cpioStrippedPhysicalHeader hdr_s;
284     struct cpioStrippedPhysicalHeader * hdr = &hdr_s;
285     char field[64];
286     size_t written;
287     int rc = 0;
288 
289     if ((cpio->mode & O_ACCMODE) != O_WRONLY) {
290         return RPMERR_WRITE_FAILED;
291     }
292 
293     if (cpio->fileend != cpio->offset) {
294         return RPMERR_WRITE_FAILED;
295     }
296 
297     rc = rpmcpioWritePad(cpio, 4);
298     if (rc) {
299         return rc;
300     }
301 
302     SET_NUM_FIELD(hdr->fx, fx, field);
303 
304     written = Fwrite(CPIO_STRIPPED_MAGIC, 6, 1, cpio->fd);
305     cpio->offset += written;
306     if (written != 6) {
307         return RPMERR_WRITE_FAILED;
308     }
309 
310     written = Fwrite(hdr, STRIPPED_PHYS_HDR_SIZE, 1, cpio->fd);
311     cpio->offset += written;
312     if (written != STRIPPED_PHYS_HDR_SIZE) {
313         return RPMERR_WRITE_FAILED;
314     }
315 
316     rc = rpmcpioWritePad(cpio, 4);
317 
318     cpio->fileend = cpio->offset + fsize;
319 
320     return rc;
321 }
322 
rpmcpioWrite(rpmcpio_t cpio,const void * buf,size_t size)323 ssize_t rpmcpioWrite(rpmcpio_t cpio, const void * buf, size_t size)
324 {
325     size_t written, left;
326 
327     if ((cpio->mode & O_ACCMODE) != O_WRONLY) {
328         return RPMERR_WRITE_FAILED;
329     }
330 
331     // Do not write beyond file length
332     left = cpio->fileend - cpio->offset;
333     size = size > left ? left : size;
334     written = Fwrite(buf, size, 1, cpio->fd);
335     cpio->offset += written;
336     return written;
337 }
338 
339 
rpmcpioHeaderRead(rpmcpio_t cpio,char ** path,int * fx)340 int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, int * fx)
341 {
342     struct cpioCrcPhysicalHeader hdr;
343     int nameSize;
344     char * end;
345     int rc = 0;
346     ssize_t read;
347     char magic[6];
348     rpm_loff_t fsize;
349 
350     if ((cpio->mode & O_ACCMODE) != O_RDONLY) {
351         return RPMERR_READ_FAILED;
352     }
353 
354     /* Move to next file */
355     if (cpio->fileend != cpio->offset) {
356         /* XXX try using Fseek() - which is currently broken */
357         char buf[8*BUFSIZ];
358         while (cpio->fileend != cpio->offset) {
359             read = cpio->fileend - cpio->offset > 8*BUFSIZ ? 8*BUFSIZ : cpio->fileend - cpio->offset;
360             if (rpmcpioRead(cpio, &buf, read) != read) {
361                 return RPMERR_READ_FAILED;
362             }
363         }
364     }
365 
366     rc = rpmcpioReadPad(cpio);
367     if (rc) return rc;
368 
369     read = Fread(&magic, 6, 1, cpio->fd);
370     cpio->offset += read;
371     if (read != 6)
372 	return RPMERR_BAD_MAGIC;
373 
374     /* read stripped header */
375     if (!strncmp(CPIO_STRIPPED_MAGIC, magic,
376                  sizeof(CPIO_STRIPPED_MAGIC)-1)) {
377         struct cpioStrippedPhysicalHeader shdr;
378         read = Fread(&shdr, STRIPPED_PHYS_HDR_SIZE, 1, cpio->fd);
379         cpio->offset += read;
380         if (read != STRIPPED_PHYS_HDR_SIZE)
381 	    return RPMERR_BAD_HEADER;
382 
383         GET_NUM_FIELD(shdr.fx, *fx);
384         rc = rpmcpioReadPad(cpio);
385 
386         if (!rc && *fx == -1)
387             rc = RPMERR_ITER_END;
388         return rc;
389     }
390 
391     if (strncmp(CPIO_CRC_MAGIC, magic, sizeof(CPIO_CRC_MAGIC)-1) &&
392 	strncmp(CPIO_NEWC_MAGIC, magic, sizeof(CPIO_NEWC_MAGIC)-1)) {
393 	return RPMERR_BAD_MAGIC;
394     }
395 
396     read = Fread(&hdr, PHYS_HDR_SIZE, 1, cpio->fd);
397     cpio->offset += read;
398     if (read != PHYS_HDR_SIZE)
399         return RPMERR_BAD_HEADER;
400 
401     GET_NUM_FIELD(hdr.filesize, fsize);
402     GET_NUM_FIELD(hdr.namesize, nameSize);
403     if (nameSize <= 0 || nameSize > 4096) {
404         return RPMERR_BAD_HEADER;
405     }
406 
407     char name[nameSize + 1];
408     read = Fread(name, nameSize, 1, cpio->fd);
409     name[nameSize] = '\0';
410     cpio->offset += read;
411     if (read != nameSize ) {
412         return RPMERR_BAD_HEADER;
413     }
414 
415     rc = rpmcpioReadPad(cpio);
416     cpio->fileend = cpio->offset + fsize;
417 
418     if (!rc && rstreq(name, CPIO_TRAILER))
419 	rc = RPMERR_ITER_END;
420 
421     if (!rc && path)
422 	*path = xstrdup(name);
423 
424     return rc;
425 }
426 
rpmcpioSetExpectedFileSize(rpmcpio_t cpio,off_t fsize)427 void rpmcpioSetExpectedFileSize(rpmcpio_t cpio, off_t fsize)
428 {
429     cpio->fileend = cpio->offset + fsize;
430 }
431 
rpmcpioRead(rpmcpio_t cpio,void * buf,size_t size)432 ssize_t rpmcpioRead(rpmcpio_t cpio, void * buf, size_t size)
433 {
434     size_t read, left;
435 
436     if ((cpio->mode & O_ACCMODE) != O_RDONLY) {
437         return RPMERR_READ_FAILED;
438     }
439 
440     left = cpio->fileend - cpio->offset;
441     size = size > left ? left : size;
442     read = Fread(buf, size, 1, cpio->fd);
443     cpio->offset += read;
444     return read;
445 }
446 
rpmcpioClose(rpmcpio_t cpio)447 int rpmcpioClose(rpmcpio_t cpio)
448 {
449     int rc = 0;
450     if ((cpio->mode & O_ACCMODE) == O_WRONLY) {
451         rc = rpmcpioTrailerWrite(cpio);
452     }
453     fdFree(cpio->fd);
454     cpio->fd = NULL;
455     return rc;
456 }
457 
rpmcpioFree(rpmcpio_t cpio)458 rpmcpio_t rpmcpioFree(rpmcpio_t cpio)
459 {
460     if (cpio) {
461 	if (cpio->fd)
462 	    (void) rpmcpioClose(cpio);
463 	free(cpio);
464     }
465     return NULL;
466 }
467