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