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