Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/usr/gen_init_cpio.c
29264 views
1
// SPDX-License-Identifier: GPL-2.0
2
#define _GNU_SOURCE
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <stdint.h>
6
#include <stdbool.h>
7
#include <sys/types.h>
8
#include <sys/stat.h>
9
#include <string.h>
10
#include <unistd.h>
11
#include <time.h>
12
#include <fcntl.h>
13
#include <errno.h>
14
#include <ctype.h>
15
#include <limits.h>
16
17
/*
18
* Original work by Jeff Garzik
19
*
20
* External file lists, symlink, pipe and fifo support by Thayne Harbaugh
21
* Hard link support by Luciano Rocha
22
*/
23
24
#define xstr(s) #s
25
#define str(s) xstr(s)
26
#define MIN(a, b) ((a) < (b) ? (a) : (b))
27
#define CPIO_HDR_LEN 110
28
#define CPIO_TRAILER "TRAILER!!!"
29
#define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align))
30
31
/* zero-padding the filename field for data alignment is limited by PATH_MAX */
32
static char padding[PATH_MAX];
33
static unsigned int offset;
34
static unsigned int ino = 721;
35
static time_t default_mtime;
36
static bool do_file_mtime;
37
static bool do_csum = false;
38
static int outfd = STDOUT_FILENO;
39
static unsigned int dalign;
40
41
struct file_handler {
42
const char *type;
43
int (*handler)(const char *line);
44
};
45
46
static int push_buf(const char *name, size_t name_len)
47
{
48
ssize_t len;
49
50
len = write(outfd, name, name_len);
51
if (len != name_len)
52
return -1;
53
54
offset += name_len;
55
return 0;
56
}
57
58
static int push_pad(size_t padlen)
59
{
60
ssize_t len = 0;
61
62
if (!padlen)
63
return 0;
64
65
if (padlen < sizeof(padding))
66
len = write(outfd, padding, padlen);
67
if (len != padlen)
68
return -1;
69
70
offset += padlen;
71
return 0;
72
}
73
74
static int push_rest(const char *name, size_t name_len)
75
{
76
ssize_t len;
77
78
len = write(outfd, name, name_len);
79
if (len != name_len)
80
return -1;
81
82
offset += name_len;
83
84
return push_pad(padlen(name_len + CPIO_HDR_LEN, 4));
85
}
86
87
static int cpio_trailer(void)
88
{
89
int len;
90
unsigned int namesize = sizeof(CPIO_TRAILER);
91
92
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
93
"%08X%08X%08X%08X%08X%08X%08X",
94
do_csum ? "070702" : "070701", /* magic */
95
0, /* ino */
96
0, /* mode */
97
(long) 0, /* uid */
98
(long) 0, /* gid */
99
1, /* nlink */
100
(long) 0, /* mtime */
101
0, /* filesize */
102
0, /* major */
103
0, /* minor */
104
0, /* rmajor */
105
0, /* rminor */
106
namesize, /* namesize */
107
0); /* chksum */
108
offset += len;
109
110
if (len != CPIO_HDR_LEN ||
111
push_rest(CPIO_TRAILER, namesize) < 0 ||
112
push_pad(padlen(offset, 512)) < 0)
113
return -1;
114
115
return fsync(outfd);
116
}
117
118
static int cpio_mkslink(const char *name, const char *target,
119
unsigned int mode, uid_t uid, gid_t gid)
120
{
121
int len;
122
unsigned int namesize, targetsize = strlen(target) + 1;
123
124
if (name[0] == '/')
125
name++;
126
namesize = strlen(name) + 1;
127
128
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
129
"%08X%08X%08X%08X%08X%08X%08X",
130
do_csum ? "070702" : "070701", /* magic */
131
ino++, /* ino */
132
S_IFLNK | mode, /* mode */
133
(long) uid, /* uid */
134
(long) gid, /* gid */
135
1, /* nlink */
136
(long) default_mtime, /* mtime */
137
targetsize, /* filesize */
138
3, /* major */
139
1, /* minor */
140
0, /* rmajor */
141
0, /* rminor */
142
namesize, /* namesize */
143
0); /* chksum */
144
offset += len;
145
146
if (len != CPIO_HDR_LEN ||
147
push_buf(name, namesize) < 0 ||
148
push_pad(padlen(offset, 4)) < 0 ||
149
push_buf(target, targetsize) < 0 ||
150
push_pad(padlen(offset, 4)) < 0)
151
return -1;
152
153
return 0;
154
155
}
156
157
static int cpio_mkslink_line(const char *line)
158
{
159
char name[PATH_MAX + 1];
160
char target[PATH_MAX + 1];
161
unsigned int mode;
162
int uid;
163
int gid;
164
int rc = -1;
165
166
if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
167
fprintf(stderr, "Unrecognized dir format '%s'", line);
168
goto fail;
169
}
170
rc = cpio_mkslink(name, target, mode, uid, gid);
171
fail:
172
return rc;
173
}
174
175
static int cpio_mkgeneric(const char *name, unsigned int mode,
176
uid_t uid, gid_t gid)
177
{
178
int len;
179
unsigned int namesize;
180
181
if (name[0] == '/')
182
name++;
183
namesize = strlen(name) + 1;
184
185
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
186
"%08X%08X%08X%08X%08X%08X%08X",
187
do_csum ? "070702" : "070701", /* magic */
188
ino++, /* ino */
189
mode, /* mode */
190
(long) uid, /* uid */
191
(long) gid, /* gid */
192
2, /* nlink */
193
(long) default_mtime, /* mtime */
194
0, /* filesize */
195
3, /* major */
196
1, /* minor */
197
0, /* rmajor */
198
0, /* rminor */
199
namesize, /* namesize */
200
0); /* chksum */
201
offset += len;
202
203
if (len != CPIO_HDR_LEN ||
204
push_rest(name, namesize) < 0)
205
return -1;
206
207
return 0;
208
}
209
210
enum generic_types {
211
GT_DIR,
212
GT_PIPE,
213
GT_SOCK
214
};
215
216
struct generic_type {
217
const char *type;
218
mode_t mode;
219
};
220
221
static const struct generic_type generic_type_table[] = {
222
[GT_DIR] = {
223
.type = "dir",
224
.mode = S_IFDIR
225
},
226
[GT_PIPE] = {
227
.type = "pipe",
228
.mode = S_IFIFO
229
},
230
[GT_SOCK] = {
231
.type = "sock",
232
.mode = S_IFSOCK
233
}
234
};
235
236
static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
237
{
238
char name[PATH_MAX + 1];
239
unsigned int mode;
240
int uid;
241
int gid;
242
int rc = -1;
243
244
if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
245
fprintf(stderr, "Unrecognized %s format '%s'",
246
line, generic_type_table[gt].type);
247
goto fail;
248
}
249
mode |= generic_type_table[gt].mode;
250
rc = cpio_mkgeneric(name, mode, uid, gid);
251
fail:
252
return rc;
253
}
254
255
static int cpio_mkdir_line(const char *line)
256
{
257
return cpio_mkgeneric_line(line, GT_DIR);
258
}
259
260
static int cpio_mkpipe_line(const char *line)
261
{
262
return cpio_mkgeneric_line(line, GT_PIPE);
263
}
264
265
static int cpio_mksock_line(const char *line)
266
{
267
return cpio_mkgeneric_line(line, GT_SOCK);
268
}
269
270
static int cpio_mknod(const char *name, unsigned int mode,
271
uid_t uid, gid_t gid, char dev_type,
272
unsigned int maj, unsigned int min)
273
{
274
int len;
275
unsigned int namesize;
276
277
if (dev_type == 'b')
278
mode |= S_IFBLK;
279
else
280
mode |= S_IFCHR;
281
282
if (name[0] == '/')
283
name++;
284
namesize = strlen(name) + 1;
285
286
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
287
"%08X%08X%08X%08X%08X%08X%08X",
288
do_csum ? "070702" : "070701", /* magic */
289
ino++, /* ino */
290
mode, /* mode */
291
(long) uid, /* uid */
292
(long) gid, /* gid */
293
1, /* nlink */
294
(long) default_mtime, /* mtime */
295
0, /* filesize */
296
3, /* major */
297
1, /* minor */
298
maj, /* rmajor */
299
min, /* rminor */
300
namesize, /* namesize */
301
0); /* chksum */
302
offset += len;
303
304
if (len != CPIO_HDR_LEN ||
305
push_rest(name, namesize) < 0)
306
return -1;
307
308
return 0;
309
}
310
311
static int cpio_mknod_line(const char *line)
312
{
313
char name[PATH_MAX + 1];
314
unsigned int mode;
315
int uid;
316
int gid;
317
char dev_type;
318
unsigned int maj;
319
unsigned int min;
320
int rc = -1;
321
322
if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
323
name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
324
fprintf(stderr, "Unrecognized nod format '%s'", line);
325
goto fail;
326
}
327
rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
328
fail:
329
return rc;
330
}
331
332
static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum)
333
{
334
while (size) {
335
unsigned char filebuf[65536];
336
ssize_t this_read;
337
size_t i, this_size = MIN(size, sizeof(filebuf));
338
339
this_read = read(fd, filebuf, this_size);
340
if (this_read <= 0 || this_read > this_size)
341
return -1;
342
343
for (i = 0; i < this_read; i++)
344
*csum += filebuf[i];
345
346
size -= this_read;
347
}
348
/* seek back to the start for data segment I/O */
349
if (lseek(fd, 0, SEEK_SET) < 0)
350
return -1;
351
352
return 0;
353
}
354
355
static int cpio_mkfile(const char *name, const char *location,
356
unsigned int mode, uid_t uid, gid_t gid,
357
unsigned int nlinks)
358
{
359
struct stat buf;
360
unsigned long size;
361
int file, retval, len;
362
int rc = -1;
363
time_t mtime;
364
int namesize, namepadlen;
365
unsigned int i;
366
uint32_t csum = 0;
367
ssize_t this_read;
368
369
mode |= S_IFREG;
370
371
file = open (location, O_RDONLY);
372
if (file < 0) {
373
fprintf (stderr, "File %s could not be opened for reading\n", location);
374
goto error;
375
}
376
377
retval = fstat(file, &buf);
378
if (retval) {
379
fprintf(stderr, "File %s could not be stat()'ed\n", location);
380
goto error;
381
}
382
383
if (do_file_mtime) {
384
mtime = default_mtime;
385
} else {
386
mtime = buf.st_mtime;
387
if (mtime > 0xffffffff) {
388
fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
389
location);
390
mtime = 0xffffffff;
391
}
392
393
if (mtime < 0) {
394
fprintf(stderr, "%s: Timestamp negative, clipping.\n",
395
location);
396
mtime = 0;
397
}
398
}
399
400
if (buf.st_size > 0xffffffff) {
401
fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
402
location);
403
goto error;
404
}
405
406
if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
407
fprintf(stderr, "Failed to checksum file %s\n", location);
408
goto error;
409
}
410
411
size = 0;
412
namepadlen = 0;
413
for (i = 1; i <= nlinks; i++) {
414
if (name[0] == '/')
415
name++;
416
namesize = strlen(name) + 1;
417
418
/* data goes on last link, after any alignment padding */
419
if (i == nlinks)
420
size = buf.st_size;
421
422
if (dalign && size > dalign) {
423
namepadlen = padlen(offset + CPIO_HDR_LEN + namesize,
424
dalign);
425
if (namesize + namepadlen > PATH_MAX) {
426
fprintf(stderr,
427
"%s: best-effort alignment %u missed\n",
428
name, dalign);
429
namepadlen = 0;
430
}
431
}
432
433
len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
434
"%08lX%08X%08X%08X%08X%08X%08X",
435
do_csum ? "070702" : "070701", /* magic */
436
ino, /* ino */
437
mode, /* mode */
438
(long) uid, /* uid */
439
(long) gid, /* gid */
440
nlinks, /* nlink */
441
(long) mtime, /* mtime */
442
size, /* filesize */
443
3, /* major */
444
1, /* minor */
445
0, /* rmajor */
446
0, /* rminor */
447
namesize + namepadlen, /* namesize */
448
size ? csum : 0); /* chksum */
449
offset += len;
450
451
if (len != CPIO_HDR_LEN ||
452
push_buf(name, namesize) < 0 ||
453
push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0)
454
goto error;
455
456
if (size) {
457
this_read = copy_file_range(file, NULL, outfd, NULL, size, 0);
458
if (this_read > 0) {
459
if (this_read > size)
460
goto error;
461
offset += this_read;
462
size -= this_read;
463
}
464
/* short or failed copy falls back to read/write... */
465
}
466
467
while (size) {
468
unsigned char filebuf[65536];
469
size_t this_size = MIN(size, sizeof(filebuf));
470
471
this_read = read(file, filebuf, this_size);
472
if (this_read <= 0 || this_read > this_size) {
473
fprintf(stderr, "Can not read %s file\n", location);
474
goto error;
475
}
476
477
if (write(outfd, filebuf, this_read) != this_read) {
478
fprintf(stderr, "writing filebuf failed\n");
479
goto error;
480
}
481
offset += this_read;
482
size -= this_read;
483
}
484
if (push_pad(padlen(offset, 4)) < 0)
485
goto error;
486
487
name += namesize;
488
}
489
ino++;
490
rc = 0;
491
492
error:
493
if (file >= 0)
494
close(file);
495
return rc;
496
}
497
498
static char *cpio_replace_env(char *new_location)
499
{
500
char expanded[PATH_MAX + 1];
501
char *start, *end, *var;
502
503
while ((start = strstr(new_location, "${")) &&
504
(end = strchr(start + 2, '}'))) {
505
*start = *end = 0;
506
var = getenv(start + 2);
507
snprintf(expanded, sizeof expanded, "%s%s%s",
508
new_location, var ? var : "", end + 1);
509
strcpy(new_location, expanded);
510
}
511
512
return new_location;
513
}
514
515
static int cpio_mkfile_line(const char *line)
516
{
517
char name[PATH_MAX + 1];
518
char *dname = NULL; /* malloc'ed buffer for hard links */
519
char location[PATH_MAX + 1];
520
unsigned int mode;
521
int uid;
522
int gid;
523
int nlinks = 1;
524
int end = 0, dname_len = 0;
525
int rc = -1;
526
527
if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
528
"s %o %d %d %n",
529
name, location, &mode, &uid, &gid, &end)) {
530
fprintf(stderr, "Unrecognized file format '%s'", line);
531
goto fail;
532
}
533
if (end && isgraph(line[end])) {
534
int len;
535
int nend;
536
537
dname = malloc(strlen(line));
538
if (!dname) {
539
fprintf (stderr, "out of memory (%d)\n", dname_len);
540
goto fail;
541
}
542
543
dname_len = strlen(name) + 1;
544
memcpy(dname, name, dname_len);
545
546
do {
547
nend = 0;
548
if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
549
name, &nend) < 1)
550
break;
551
len = strlen(name) + 1;
552
memcpy(dname + dname_len, name, len);
553
dname_len += len;
554
nlinks++;
555
end += nend;
556
} while (isgraph(line[end]));
557
} else {
558
dname = name;
559
}
560
rc = cpio_mkfile(dname, cpio_replace_env(location),
561
mode, uid, gid, nlinks);
562
fail:
563
if (dname_len) free(dname);
564
return rc;
565
}
566
567
static void usage(const char *prog)
568
{
569
fprintf(stderr, "Usage:\n"
570
"\t%s [-t <timestamp>] [-c] [-o <output_file>] [-a <data_align>] <cpio_list>\n"
571
"\n"
572
"<cpio_list> is a file containing newline separated entries that\n"
573
"describe the files to be included in the initramfs archive:\n"
574
"\n"
575
"# a comment\n"
576
"file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
577
"dir <name> <mode> <uid> <gid>\n"
578
"nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
579
"slink <name> <target> <mode> <uid> <gid>\n"
580
"pipe <name> <mode> <uid> <gid>\n"
581
"sock <name> <mode> <uid> <gid>\n"
582
"\n"
583
"<name> name of the file/dir/nod/etc in the archive\n"
584
"<location> location of the file in the current filesystem\n"
585
" expands shell variables quoted with ${}\n"
586
"<target> link target\n"
587
"<mode> mode/permissions of the file\n"
588
"<uid> user id (0=root)\n"
589
"<gid> group id (0=root)\n"
590
"<dev_type> device type (b=block, c=character)\n"
591
"<maj> major number of nod\n"
592
"<min> minor number of nod\n"
593
"<hard links> space separated list of other links to file\n"
594
"\n"
595
"example:\n"
596
"# A simple initramfs\n"
597
"dir /dev 0755 0 0\n"
598
"nod /dev/console 0600 0 0 c 5 1\n"
599
"dir /root 0700 0 0\n"
600
"dir /sbin 0755 0 0\n"
601
"file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
602
"\n"
603
"<timestamp> is time in seconds since Epoch that will be used\n"
604
"as mtime for symlinks, directories, regular and special files.\n"
605
"The default is to use the current time for all files, but\n"
606
"preserve modification time for regular files.\n"
607
"-c: calculate and store 32-bit checksums for file data.\n"
608
"<output_file>: write cpio to this file instead of stdout\n"
609
"<data_align>: attempt to align file data by zero-padding the\n"
610
"filename field up to data_align. Must be a multiple of 4.\n"
611
"Alignment is best-effort; PATH_MAX limits filename padding.\n",
612
prog);
613
}
614
615
static const struct file_handler file_handler_table[] = {
616
{
617
.type = "file",
618
.handler = cpio_mkfile_line,
619
}, {
620
.type = "nod",
621
.handler = cpio_mknod_line,
622
}, {
623
.type = "dir",
624
.handler = cpio_mkdir_line,
625
}, {
626
.type = "slink",
627
.handler = cpio_mkslink_line,
628
}, {
629
.type = "pipe",
630
.handler = cpio_mkpipe_line,
631
}, {
632
.type = "sock",
633
.handler = cpio_mksock_line,
634
}, {
635
.type = NULL,
636
.handler = NULL,
637
}
638
};
639
640
#define LINE_SIZE (2 * PATH_MAX + 50)
641
642
int main (int argc, char *argv[])
643
{
644
FILE *cpio_list;
645
char line[LINE_SIZE];
646
char *args, *type;
647
int ec = 0;
648
int line_nr = 0;
649
const char *filename;
650
651
default_mtime = time(NULL);
652
while (1) {
653
int opt = getopt(argc, argv, "t:cho:a:");
654
char *invalid;
655
656
if (opt == -1)
657
break;
658
switch (opt) {
659
case 't':
660
default_mtime = strtol(optarg, &invalid, 10);
661
if (!*optarg || *invalid) {
662
fprintf(stderr, "Invalid timestamp: %s\n",
663
optarg);
664
usage(argv[0]);
665
exit(1);
666
}
667
do_file_mtime = true;
668
break;
669
case 'c':
670
do_csum = true;
671
break;
672
case 'o':
673
outfd = open(optarg,
674
O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC,
675
0600);
676
if (outfd < 0) {
677
fprintf(stderr, "failed to open %s\n", optarg);
678
usage(argv[0]);
679
exit(1);
680
}
681
break;
682
case 'a':
683
dalign = strtoul(optarg, &invalid, 10);
684
if (!*optarg || *invalid || (dalign & 3)) {
685
fprintf(stderr, "Invalid data_align: %s\n",
686
optarg);
687
usage(argv[0]);
688
exit(1);
689
}
690
break;
691
case 'h':
692
case '?':
693
usage(argv[0]);
694
exit(opt == 'h' ? 0 : 1);
695
}
696
}
697
698
/*
699
* Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
700
* representation that exceeds 8 chars and breaks the cpio header
701
* specification. Negative timestamps similarly exceed 8 chars.
702
*/
703
if (default_mtime > 0xffffffff || default_mtime < 0) {
704
fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n");
705
exit(1);
706
}
707
708
if (argc - optind != 1) {
709
usage(argv[0]);
710
exit(1);
711
}
712
filename = argv[optind];
713
if (!strcmp(filename, "-"))
714
cpio_list = stdin;
715
else if (!(cpio_list = fopen(filename, "r"))) {
716
fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
717
filename, strerror(errno));
718
usage(argv[0]);
719
exit(1);
720
}
721
722
while (fgets(line, LINE_SIZE, cpio_list)) {
723
int type_idx;
724
size_t slen = strlen(line);
725
726
line_nr++;
727
728
if ('#' == *line) {
729
/* comment - skip to next line */
730
continue;
731
}
732
733
if (! (type = strtok(line, " \t"))) {
734
fprintf(stderr,
735
"ERROR: incorrect format, could not locate file type line %d: '%s'\n",
736
line_nr, line);
737
ec = -1;
738
break;
739
}
740
741
if ('\n' == *type) {
742
/* a blank line */
743
continue;
744
}
745
746
if (slen == strlen(type)) {
747
/* must be an empty line */
748
continue;
749
}
750
751
if (! (args = strtok(NULL, "\n"))) {
752
fprintf(stderr,
753
"ERROR: incorrect format, newline required line %d: '%s'\n",
754
line_nr, line);
755
ec = -1;
756
}
757
758
for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
759
int rc;
760
if (! strcmp(line, file_handler_table[type_idx].type)) {
761
if ((rc = file_handler_table[type_idx].handler(args))) {
762
ec = rc;
763
fprintf(stderr, " line %d\n", line_nr);
764
}
765
break;
766
}
767
}
768
769
if (NULL == file_handler_table[type_idx].type) {
770
fprintf(stderr, "unknown file type line %d: '%s'\n",
771
line_nr, line);
772
}
773
}
774
if (ec == 0)
775
ec = cpio_trailer();
776
777
exit(ec);
778
}
779
780