Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/init/initramfs.c
10811 views
1
#include <linux/init.h>
2
#include <linux/fs.h>
3
#include <linux/slab.h>
4
#include <linux/types.h>
5
#include <linux/fcntl.h>
6
#include <linux/delay.h>
7
#include <linux/string.h>
8
#include <linux/dirent.h>
9
#include <linux/syscalls.h>
10
#include <linux/utime.h>
11
12
static __initdata char *message;
13
static void __init error(char *x)
14
{
15
if (!message)
16
message = x;
17
}
18
19
/* link hash */
20
21
#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
22
23
static __initdata struct hash {
24
int ino, minor, major;
25
mode_t mode;
26
struct hash *next;
27
char name[N_ALIGN(PATH_MAX)];
28
} *head[32];
29
30
static inline int hash(int major, int minor, int ino)
31
{
32
unsigned long tmp = ino + minor + (major << 3);
33
tmp += tmp >> 5;
34
return tmp & 31;
35
}
36
37
static char __init *find_link(int major, int minor, int ino,
38
mode_t mode, char *name)
39
{
40
struct hash **p, *q;
41
for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
42
if ((*p)->ino != ino)
43
continue;
44
if ((*p)->minor != minor)
45
continue;
46
if ((*p)->major != major)
47
continue;
48
if (((*p)->mode ^ mode) & S_IFMT)
49
continue;
50
return (*p)->name;
51
}
52
q = kmalloc(sizeof(struct hash), GFP_KERNEL);
53
if (!q)
54
panic("can't allocate link hash entry");
55
q->major = major;
56
q->minor = minor;
57
q->ino = ino;
58
q->mode = mode;
59
strcpy(q->name, name);
60
q->next = NULL;
61
*p = q;
62
return NULL;
63
}
64
65
static void __init free_hash(void)
66
{
67
struct hash **p, *q;
68
for (p = head; p < head + 32; p++) {
69
while (*p) {
70
q = *p;
71
*p = q->next;
72
kfree(q);
73
}
74
}
75
}
76
77
static long __init do_utime(char __user *filename, time_t mtime)
78
{
79
struct timespec t[2];
80
81
t[0].tv_sec = mtime;
82
t[0].tv_nsec = 0;
83
t[1].tv_sec = mtime;
84
t[1].tv_nsec = 0;
85
86
return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW);
87
}
88
89
static __initdata LIST_HEAD(dir_list);
90
struct dir_entry {
91
struct list_head list;
92
char *name;
93
time_t mtime;
94
};
95
96
static void __init dir_add(const char *name, time_t mtime)
97
{
98
struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
99
if (!de)
100
panic("can't allocate dir_entry buffer");
101
INIT_LIST_HEAD(&de->list);
102
de->name = kstrdup(name, GFP_KERNEL);
103
de->mtime = mtime;
104
list_add(&de->list, &dir_list);
105
}
106
107
static void __init dir_utime(void)
108
{
109
struct dir_entry *de, *tmp;
110
list_for_each_entry_safe(de, tmp, &dir_list, list) {
111
list_del(&de->list);
112
do_utime(de->name, de->mtime);
113
kfree(de->name);
114
kfree(de);
115
}
116
}
117
118
static __initdata time_t mtime;
119
120
/* cpio header parsing */
121
122
static __initdata unsigned long ino, major, minor, nlink;
123
static __initdata mode_t mode;
124
static __initdata unsigned long body_len, name_len;
125
static __initdata uid_t uid;
126
static __initdata gid_t gid;
127
static __initdata unsigned rdev;
128
129
static void __init parse_header(char *s)
130
{
131
unsigned long parsed[12];
132
char buf[9];
133
int i;
134
135
buf[8] = '\0';
136
for (i = 0, s += 6; i < 12; i++, s += 8) {
137
memcpy(buf, s, 8);
138
parsed[i] = simple_strtoul(buf, NULL, 16);
139
}
140
ino = parsed[0];
141
mode = parsed[1];
142
uid = parsed[2];
143
gid = parsed[3];
144
nlink = parsed[4];
145
mtime = parsed[5];
146
body_len = parsed[6];
147
major = parsed[7];
148
minor = parsed[8];
149
rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
150
name_len = parsed[11];
151
}
152
153
/* FSM */
154
155
static __initdata enum state {
156
Start,
157
Collect,
158
GotHeader,
159
SkipIt,
160
GotName,
161
CopyFile,
162
GotSymlink,
163
Reset
164
} state, next_state;
165
166
static __initdata char *victim;
167
static __initdata unsigned count;
168
static __initdata loff_t this_header, next_header;
169
170
static inline void __init eat(unsigned n)
171
{
172
victim += n;
173
this_header += n;
174
count -= n;
175
}
176
177
static __initdata char *vcollected;
178
static __initdata char *collected;
179
static __initdata int remains;
180
static __initdata char *collect;
181
182
static void __init read_into(char *buf, unsigned size, enum state next)
183
{
184
if (count >= size) {
185
collected = victim;
186
eat(size);
187
state = next;
188
} else {
189
collect = collected = buf;
190
remains = size;
191
next_state = next;
192
state = Collect;
193
}
194
}
195
196
static __initdata char *header_buf, *symlink_buf, *name_buf;
197
198
static int __init do_start(void)
199
{
200
read_into(header_buf, 110, GotHeader);
201
return 0;
202
}
203
204
static int __init do_collect(void)
205
{
206
unsigned n = remains;
207
if (count < n)
208
n = count;
209
memcpy(collect, victim, n);
210
eat(n);
211
collect += n;
212
if ((remains -= n) != 0)
213
return 1;
214
state = next_state;
215
return 0;
216
}
217
218
static int __init do_header(void)
219
{
220
if (memcmp(collected, "070707", 6)==0) {
221
error("incorrect cpio method used: use -H newc option");
222
return 1;
223
}
224
if (memcmp(collected, "070701", 6)) {
225
error("no cpio magic");
226
return 1;
227
}
228
parse_header(collected);
229
next_header = this_header + N_ALIGN(name_len) + body_len;
230
next_header = (next_header + 3) & ~3;
231
state = SkipIt;
232
if (name_len <= 0 || name_len > PATH_MAX)
233
return 0;
234
if (S_ISLNK(mode)) {
235
if (body_len > PATH_MAX)
236
return 0;
237
collect = collected = symlink_buf;
238
remains = N_ALIGN(name_len) + body_len;
239
next_state = GotSymlink;
240
state = Collect;
241
return 0;
242
}
243
if (S_ISREG(mode) || !body_len)
244
read_into(name_buf, N_ALIGN(name_len), GotName);
245
return 0;
246
}
247
248
static int __init do_skip(void)
249
{
250
if (this_header + count < next_header) {
251
eat(count);
252
return 1;
253
} else {
254
eat(next_header - this_header);
255
state = next_state;
256
return 0;
257
}
258
}
259
260
static int __init do_reset(void)
261
{
262
while(count && *victim == '\0')
263
eat(1);
264
if (count && (this_header & 3))
265
error("broken padding");
266
return 1;
267
}
268
269
static int __init maybe_link(void)
270
{
271
if (nlink >= 2) {
272
char *old = find_link(major, minor, ino, mode, collected);
273
if (old)
274
return (sys_link(old, collected) < 0) ? -1 : 1;
275
}
276
return 0;
277
}
278
279
static void __init clean_path(char *path, mode_t mode)
280
{
281
struct stat st;
282
283
if (!sys_newlstat(path, &st) && (st.st_mode^mode) & S_IFMT) {
284
if (S_ISDIR(st.st_mode))
285
sys_rmdir(path);
286
else
287
sys_unlink(path);
288
}
289
}
290
291
static __initdata int wfd;
292
293
static int __init do_name(void)
294
{
295
state = SkipIt;
296
next_state = Reset;
297
if (strcmp(collected, "TRAILER!!!") == 0) {
298
free_hash();
299
return 0;
300
}
301
clean_path(collected, mode);
302
if (S_ISREG(mode)) {
303
int ml = maybe_link();
304
if (ml >= 0) {
305
int openflags = O_WRONLY|O_CREAT;
306
if (ml != 1)
307
openflags |= O_TRUNC;
308
wfd = sys_open(collected, openflags, mode);
309
310
if (wfd >= 0) {
311
sys_fchown(wfd, uid, gid);
312
sys_fchmod(wfd, mode);
313
if (body_len)
314
sys_ftruncate(wfd, body_len);
315
vcollected = kstrdup(collected, GFP_KERNEL);
316
state = CopyFile;
317
}
318
}
319
} else if (S_ISDIR(mode)) {
320
sys_mkdir(collected, mode);
321
sys_chown(collected, uid, gid);
322
sys_chmod(collected, mode);
323
dir_add(collected, mtime);
324
} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
325
S_ISFIFO(mode) || S_ISSOCK(mode)) {
326
if (maybe_link() == 0) {
327
sys_mknod(collected, mode, rdev);
328
sys_chown(collected, uid, gid);
329
sys_chmod(collected, mode);
330
do_utime(collected, mtime);
331
}
332
}
333
return 0;
334
}
335
336
static int __init do_copy(void)
337
{
338
if (count >= body_len) {
339
sys_write(wfd, victim, body_len);
340
sys_close(wfd);
341
do_utime(vcollected, mtime);
342
kfree(vcollected);
343
eat(body_len);
344
state = SkipIt;
345
return 0;
346
} else {
347
sys_write(wfd, victim, count);
348
body_len -= count;
349
eat(count);
350
return 1;
351
}
352
}
353
354
static int __init do_symlink(void)
355
{
356
collected[N_ALIGN(name_len) + body_len] = '\0';
357
clean_path(collected, 0);
358
sys_symlink(collected + N_ALIGN(name_len), collected);
359
sys_lchown(collected, uid, gid);
360
do_utime(collected, mtime);
361
state = SkipIt;
362
next_state = Reset;
363
return 0;
364
}
365
366
static __initdata int (*actions[])(void) = {
367
[Start] = do_start,
368
[Collect] = do_collect,
369
[GotHeader] = do_header,
370
[SkipIt] = do_skip,
371
[GotName] = do_name,
372
[CopyFile] = do_copy,
373
[GotSymlink] = do_symlink,
374
[Reset] = do_reset,
375
};
376
377
static int __init write_buffer(char *buf, unsigned len)
378
{
379
count = len;
380
victim = buf;
381
382
while (!actions[state]())
383
;
384
return len - count;
385
}
386
387
static int __init flush_buffer(void *bufv, unsigned len)
388
{
389
char *buf = (char *) bufv;
390
int written;
391
int origLen = len;
392
if (message)
393
return -1;
394
while ((written = write_buffer(buf, len)) < len && !message) {
395
char c = buf[written];
396
if (c == '0') {
397
buf += written;
398
len -= written;
399
state = Start;
400
} else if (c == 0) {
401
buf += written;
402
len -= written;
403
state = Reset;
404
} else
405
error("junk in compressed archive");
406
}
407
return origLen;
408
}
409
410
static unsigned my_inptr; /* index of next byte to be processed in inbuf */
411
412
#include <linux/decompress/generic.h>
413
414
static char * __init unpack_to_rootfs(char *buf, unsigned len)
415
{
416
int written, res;
417
decompress_fn decompress;
418
const char *compress_name;
419
static __initdata char msg_buf[64];
420
421
header_buf = kmalloc(110, GFP_KERNEL);
422
symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
423
name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
424
425
if (!header_buf || !symlink_buf || !name_buf)
426
panic("can't allocate buffers");
427
428
state = Start;
429
this_header = 0;
430
message = NULL;
431
while (!message && len) {
432
loff_t saved_offset = this_header;
433
if (*buf == '0' && !(this_header & 3)) {
434
state = Start;
435
written = write_buffer(buf, len);
436
buf += written;
437
len -= written;
438
continue;
439
}
440
if (!*buf) {
441
buf++;
442
len--;
443
this_header++;
444
continue;
445
}
446
this_header = 0;
447
decompress = decompress_method(buf, len, &compress_name);
448
if (decompress) {
449
res = decompress(buf, len, NULL, flush_buffer, NULL,
450
&my_inptr, error);
451
if (res)
452
error("decompressor failed");
453
} else if (compress_name) {
454
if (!message) {
455
snprintf(msg_buf, sizeof msg_buf,
456
"compression method %s not configured",
457
compress_name);
458
message = msg_buf;
459
}
460
} else
461
error("junk in compressed archive");
462
if (state != Reset)
463
error("junk in compressed archive");
464
this_header = saved_offset + my_inptr;
465
buf += my_inptr;
466
len -= my_inptr;
467
}
468
dir_utime();
469
kfree(name_buf);
470
kfree(symlink_buf);
471
kfree(header_buf);
472
return message;
473
}
474
475
static int __initdata do_retain_initrd;
476
477
static int __init retain_initrd_param(char *str)
478
{
479
if (*str)
480
return 0;
481
do_retain_initrd = 1;
482
return 1;
483
}
484
__setup("retain_initrd", retain_initrd_param);
485
486
extern char __initramfs_start[];
487
extern unsigned long __initramfs_size;
488
#include <linux/initrd.h>
489
#include <linux/kexec.h>
490
491
static void __init free_initrd(void)
492
{
493
#ifdef CONFIG_KEXEC
494
unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
495
unsigned long crashk_end = (unsigned long)__va(crashk_res.end);
496
#endif
497
if (do_retain_initrd)
498
goto skip;
499
500
#ifdef CONFIG_KEXEC
501
/*
502
* If the initrd region is overlapped with crashkernel reserved region,
503
* free only memory that is not part of crashkernel region.
504
*/
505
if (initrd_start < crashk_end && initrd_end > crashk_start) {
506
/*
507
* Initialize initrd memory region since the kexec boot does
508
* not do.
509
*/
510
memset((void *)initrd_start, 0, initrd_end - initrd_start);
511
if (initrd_start < crashk_start)
512
free_initrd_mem(initrd_start, crashk_start);
513
if (initrd_end > crashk_end)
514
free_initrd_mem(crashk_end, initrd_end);
515
} else
516
#endif
517
free_initrd_mem(initrd_start, initrd_end);
518
skip:
519
initrd_start = 0;
520
initrd_end = 0;
521
}
522
523
#ifdef CONFIG_BLK_DEV_RAM
524
#define BUF_SIZE 1024
525
static void __init clean_rootfs(void)
526
{
527
int fd;
528
void *buf;
529
struct linux_dirent64 *dirp;
530
int num;
531
532
fd = sys_open((const char __user __force *) "/", O_RDONLY, 0);
533
WARN_ON(fd < 0);
534
if (fd < 0)
535
return;
536
buf = kzalloc(BUF_SIZE, GFP_KERNEL);
537
WARN_ON(!buf);
538
if (!buf) {
539
sys_close(fd);
540
return;
541
}
542
543
dirp = buf;
544
num = sys_getdents64(fd, dirp, BUF_SIZE);
545
while (num > 0) {
546
while (num > 0) {
547
struct stat st;
548
int ret;
549
550
ret = sys_newlstat(dirp->d_name, &st);
551
WARN_ON_ONCE(ret);
552
if (!ret) {
553
if (S_ISDIR(st.st_mode))
554
sys_rmdir(dirp->d_name);
555
else
556
sys_unlink(dirp->d_name);
557
}
558
559
num -= dirp->d_reclen;
560
dirp = (void *)dirp + dirp->d_reclen;
561
}
562
dirp = buf;
563
memset(buf, 0, BUF_SIZE);
564
num = sys_getdents64(fd, dirp, BUF_SIZE);
565
}
566
567
sys_close(fd);
568
kfree(buf);
569
}
570
#endif
571
572
static int __init populate_rootfs(void)
573
{
574
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
575
if (err)
576
panic(err); /* Failed to decompress INTERNAL initramfs */
577
if (initrd_start) {
578
#ifdef CONFIG_BLK_DEV_RAM
579
int fd;
580
printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
581
err = unpack_to_rootfs((char *)initrd_start,
582
initrd_end - initrd_start);
583
if (!err) {
584
free_initrd();
585
return 0;
586
} else {
587
clean_rootfs();
588
unpack_to_rootfs(__initramfs_start, __initramfs_size);
589
}
590
printk(KERN_INFO "rootfs image is not initramfs (%s)"
591
"; looks like an initrd\n", err);
592
fd = sys_open((const char __user __force *) "/initrd.image",
593
O_WRONLY|O_CREAT, 0700);
594
if (fd >= 0) {
595
sys_write(fd, (char *)initrd_start,
596
initrd_end - initrd_start);
597
sys_close(fd);
598
free_initrd();
599
}
600
#else
601
printk(KERN_INFO "Unpacking initramfs...\n");
602
err = unpack_to_rootfs((char *)initrd_start,
603
initrd_end - initrd_start);
604
if (err)
605
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
606
free_initrd();
607
#endif
608
}
609
return 0;
610
}
611
rootfs_initcall(populate_rootfs);
612
613