Blame view

init/initramfs.c 16.3 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  #include <linux/init.h>
e7cb072eb   Rasmus Villemoes   init/initramfs.c:...
3
  #include <linux/async.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
  #include <linux/fs.h>
  #include <linux/slab.h>
  #include <linux/types.h>
  #include <linux/fcntl.h>
  #include <linux/delay.h>
  #include <linux/string.h>
df52092f3   Li, Shaohua   fastboot: remove ...
10
  #include <linux/dirent.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <linux/syscalls.h>
889d51a10   Nye Liu   initramfs: add op...
12
  #include <linux/utime.h>
088655148   Lokesh Vutla   initramfs: finish...
13
  #include <linux/file.h>
899ee4afe   Mike Rapoport   arm64: use generi...
14
  #include <linux/memblock.h>
dd23e8098   Florian Fainelli   initramfs: panic ...
15
  #include <linux/mm.h>
b2a74d5f9   Christoph Hellwig   initramfs: remove...
16
  #include <linux/namei.h>
8fb9f73e5   Christoph Hellwig   init: add an init...
17
  #include <linux/init_syscalls.h>
b234ed6d6   Rasmus Villemoes   init: move usermo...
18
  #include <linux/umh.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19

bf6419e4d   Christoph Hellwig   initramfs: switch...
20
21
  static ssize_t __init xwrite(struct file *file, const char *p, size_t count,
  		loff_t *pos)
387474399   Yinghai Lu   initramfs: suppor...
22
23
24
25
26
  {
  	ssize_t out = 0;
  
  	/* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */
  	while (count) {
bf6419e4d   Christoph Hellwig   initramfs: switch...
27
  		ssize_t rv = kernel_write(file, p, count, pos);
387474399   Yinghai Lu   initramfs: suppor...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  
  		if (rv < 0) {
  			if (rv == -EINTR || rv == -EAGAIN)
  				continue;
  			return out ? out : rv;
  		} else if (rv == 0)
  			break;
  
  		p += rv;
  		out += rv;
  		count -= rv;
  	}
  
  	return out;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
  static __initdata char *message;
  static void __init error(char *x)
  {
  	if (!message)
  		message = x;
  }
dd23e8098   Florian Fainelli   initramfs: panic ...
49
50
51
52
53
54
55
56
57
  static void panic_show_mem(const char *fmt, ...)
  {
  	va_list args;
  
  	show_mem(0, NULL);
  	va_start(args, fmt);
  	panic(fmt, args);
  	va_end(args);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  /* link hash */
6a050da45   Mark Huang   [PATCH] initramfs...
59
  #define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
  static __initdata struct hash {
  	int ino, minor, major;
685dd2d5b   Al Viro   init/initramfs.c:...
62
  	umode_t mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  	struct hash *next;
6a050da45   Mark Huang   [PATCH] initramfs...
64
  	char name[N_ALIGN(PATH_MAX)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
69
70
71
72
  } *head[32];
  
  static inline int hash(int major, int minor, int ino)
  {
  	unsigned long tmp = ino + minor + (major << 3);
  	tmp += tmp >> 5;
  	return tmp & 31;
  }
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
73
  static char __init *find_link(int major, int minor, int ino,
685dd2d5b   Al Viro   init/initramfs.c:...
74
  			      umode_t mode, char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
82
83
  {
  	struct hash **p, *q;
  	for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
  		if ((*p)->ino != ino)
  			continue;
  		if ((*p)->minor != minor)
  			continue;
  		if ((*p)->major != major)
  			continue;
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
84
85
  		if (((*p)->mode ^ mode) & S_IFMT)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
  		return (*p)->name;
  	}
3265e66b1   Thomas Petazzoni   directly use kmal...
88
  	q = kmalloc(sizeof(struct hash), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  	if (!q)
dd23e8098   Florian Fainelli   initramfs: panic ...
90
  		panic_show_mem("can't allocate link hash entry");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	q->major = major;
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
92
93
94
  	q->minor = minor;
  	q->ino = ino;
  	q->mode = mode;
6a050da45   Mark Huang   [PATCH] initramfs...
95
  	strcpy(q->name, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
103
104
105
106
107
  	q->next = NULL;
  	*p = q;
  	return NULL;
  }
  
  static void __init free_hash(void)
  {
  	struct hash **p, *q;
  	for (p = head; p < head + 32; p++) {
  		while (*p) {
  			q = *p;
  			*p = q->next;
3265e66b1   Thomas Petazzoni   directly use kmal...
108
  			kfree(q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
  		}
  	}
  }
e35c4c64f   Arnd Bergmann   initramfs: use ti...
112
  static long __init do_utime(char *filename, time64_t mtime)
889d51a10   Nye Liu   initramfs: add op...
113
  {
aaed2dd8a   Deepa Dinamani   utimes: Make utim...
114
  	struct timespec64 t[2];
889d51a10   Nye Liu   initramfs: add op...
115
116
117
118
119
  
  	t[0].tv_sec = mtime;
  	t[0].tv_nsec = 0;
  	t[1].tv_sec = mtime;
  	t[1].tv_nsec = 0;
235e57935   Christoph Hellwig   init: add an init...
120
  	return init_utimes(filename, t);
889d51a10   Nye Liu   initramfs: add op...
121
122
123
124
125
126
  }
  
  static __initdata LIST_HEAD(dir_list);
  struct dir_entry {
  	struct list_head list;
  	char *name;
e35c4c64f   Arnd Bergmann   initramfs: use ti...
127
  	time64_t mtime;
889d51a10   Nye Liu   initramfs: add op...
128
  };
e35c4c64f   Arnd Bergmann   initramfs: use ti...
129
  static void __init dir_add(const char *name, time64_t mtime)
889d51a10   Nye Liu   initramfs: add op...
130
131
132
  {
  	struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
  	if (!de)
dd23e8098   Florian Fainelli   initramfs: panic ...
133
  		panic_show_mem("can't allocate dir_entry buffer");
889d51a10   Nye Liu   initramfs: add op...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  	INIT_LIST_HEAD(&de->list);
  	de->name = kstrdup(name, GFP_KERNEL);
  	de->mtime = mtime;
  	list_add(&de->list, &dir_list);
  }
  
  static void __init dir_utime(void)
  {
  	struct dir_entry *de, *tmp;
  	list_for_each_entry_safe(de, tmp, &dir_list, list) {
  		list_del(&de->list);
  		do_utime(de->name, de->mtime);
  		kfree(de->name);
  		kfree(de);
  	}
  }
e35c4c64f   Arnd Bergmann   initramfs: use ti...
150
  static __initdata time64_t mtime;
889d51a10   Nye Liu   initramfs: add op...
151

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
  /* cpio header parsing */
  
  static __initdata unsigned long ino, major, minor, nlink;
685dd2d5b   Al Viro   init/initramfs.c:...
155
  static __initdata umode_t mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  static __initdata unsigned long body_len, name_len;
  static __initdata uid_t uid;
  static __initdata gid_t gid;
  static __initdata unsigned rdev;
  
  static void __init parse_header(char *s)
  {
  	unsigned long parsed[12];
  	char buf[9];
  	int i;
  
  	buf[8] = '\0';
  	for (i = 0, s += 6; i < 12; i++, s += 8) {
  		memcpy(buf, s, 8);
  		parsed[i] = simple_strtoul(buf, NULL, 16);
  	}
  	ino = parsed[0];
  	mode = parsed[1];
  	uid = parsed[2];
  	gid = parsed[3];
  	nlink = parsed[4];
e35c4c64f   Arnd Bergmann   initramfs: use ti...
177
  	mtime = parsed[5]; /* breaks in y2106 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  	body_len = parsed[6];
  	major = parsed[7];
  	minor = parsed[8];
  	rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
  	name_len = parsed[11];
  }
  
  /* FSM */
  
  static __initdata enum state {
  	Start,
  	Collect,
  	GotHeader,
  	SkipIt,
  	GotName,
  	CopyFile,
  	GotSymlink,
  	Reset
  } state, next_state;
  
  static __initdata char *victim;
c34d85aca   Mark Rustad   init/initramfs.c:...
199
  static unsigned long byte_count __initdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  static __initdata loff_t this_header, next_header;
b0a5ab931   Al Viro   initramfs: missin...
201
  static inline void __init eat(unsigned n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
  {
  	victim += n;
  	this_header += n;
c34d85aca   Mark Rustad   init/initramfs.c:...
205
  	byte_count -= n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  static __initdata char *collected;
d97b07c54   Yinghai Lu   initramfs: suppor...
208
  static long remains __initdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  static __initdata char *collect;
  
  static void __init read_into(char *buf, unsigned size, enum state next)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
213
  	if (byte_count >= size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  		collected = victim;
  		eat(size);
  		state = next;
  	} else {
  		collect = collected = buf;
  		remains = size;
  		next_state = next;
  		state = Collect;
  	}
  }
  
  static __initdata char *header_buf, *symlink_buf, *name_buf;
  
  static int __init do_start(void)
  {
  	read_into(header_buf, 110, GotHeader);
  	return 0;
  }
  
  static int __init do_collect(void)
  {
d97b07c54   Yinghai Lu   initramfs: suppor...
235
  	unsigned long n = remains;
c34d85aca   Mark Rustad   init/initramfs.c:...
236
237
  	if (byte_count < n)
  		n = byte_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
242
243
244
245
246
247
248
  	memcpy(collect, victim, n);
  	eat(n);
  	collect += n;
  	if ((remains -= n) != 0)
  		return 1;
  	state = next_state;
  	return 0;
  }
  
  static int __init do_header(void)
  {
2e591bbc0   Arjan van de Ven   [PATCH] Make init...
249
250
251
252
  	if (memcmp(collected, "070707", 6)==0) {
  		error("incorrect cpio method used: use -H newc option");
  		return 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
259
  	if (memcmp(collected, "070701", 6)) {
  		error("no cpio magic");
  		return 1;
  	}
  	parse_header(collected);
  	next_header = this_header + N_ALIGN(name_len) + body_len;
  	next_header = (next_header + 3) & ~3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  	state = SkipIt;
  	if (name_len <= 0 || name_len > PATH_MAX)
  		return 0;
  	if (S_ISLNK(mode)) {
  		if (body_len > PATH_MAX)
  			return 0;
  		collect = collected = symlink_buf;
  		remains = N_ALIGN(name_len) + body_len;
  		next_state = GotSymlink;
  		state = Collect;
  		return 0;
  	}
  	if (S_ISREG(mode) || !body_len)
  		read_into(name_buf, N_ALIGN(name_len), GotName);
  	return 0;
  }
  
  static int __init do_skip(void)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
279
280
  	if (this_header + byte_count < next_header) {
  		eat(byte_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
285
286
287
288
289
290
  		return 1;
  	} else {
  		eat(next_header - this_header);
  		state = next_state;
  		return 0;
  	}
  }
  
  static int __init do_reset(void)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
291
  	while (byte_count && *victim == '\0')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  		eat(1);
c34d85aca   Mark Rustad   init/initramfs.c:...
293
  	if (byte_count && (this_header & 3))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
  		error("broken padding");
  	return 1;
  }
c34d85aca   Mark Rustad   init/initramfs.c:...
297
  static void __init clean_path(char *path, umode_t fmode)
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
298
  {
046aa1265   Arnd Bergmann   initramfs: use vf...
299
  	struct kstat st;
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
300

7b81ce7cd   Barret Rhoden   init: fix error c...
301
  	if (!init_stat(path, &st, AT_SYMLINK_NOFOLLOW) &&
716308a53   Christoph Hellwig   init: add an init...
302
  	    (st.mode ^ fmode) & S_IFMT) {
046aa1265   Arnd Bergmann   initramfs: use vf...
303
  		if (S_ISDIR(st.mode))
20cce026c   Christoph Hellwig   init: add an init...
304
  			init_rmdir(path);
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
305
  		else
8fb9f73e5   Christoph Hellwig   init: add an init...
306
  			init_unlink(path);
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
307
308
  	}
  }
7c0950d45   Li Zhijian   initramfs: clean ...
309
310
311
312
313
314
  static int __init maybe_link(void)
  {
  	if (nlink >= 2) {
  		char *old = find_link(major, minor, ino, mode, collected);
  		if (old) {
  			clean_path(collected, 0);
812931d69   Christoph Hellwig   init: add an init...
315
  			return (init_link(old, collected) < 0) ? -1 : 1;
7c0950d45   Li Zhijian   initramfs: clean ...
316
317
318
319
  		}
  	}
  	return 0;
  }
bf6419e4d   Christoph Hellwig   initramfs: switch...
320
321
  static __initdata struct file *wfile;
  static __initdata loff_t wfile_pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
328
329
330
  
  static int __init do_name(void)
  {
  	state = SkipIt;
  	next_state = Reset;
  	if (strcmp(collected, "TRAILER!!!") == 0) {
  		free_hash();
  		return 0;
  	}
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
331
  	clean_path(collected, mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  	if (S_ISREG(mode)) {
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
333
334
335
336
337
  		int ml = maybe_link();
  		if (ml >= 0) {
  			int openflags = O_WRONLY|O_CREAT;
  			if (ml != 1)
  				openflags |= O_TRUNC;
bf6419e4d   Christoph Hellwig   initramfs: switch...
338
339
340
341
342
343
344
345
346
  			wfile = filp_open(collected, openflags, mode);
  			if (IS_ERR(wfile))
  				return 0;
  			wfile_pos = 0;
  
  			vfs_fchown(wfile, uid, gid);
  			vfs_fchmod(wfile, mode);
  			if (body_len)
  				vfs_truncate(&wfile->f_path, body_len);
bf6419e4d   Christoph Hellwig   initramfs: switch...
347
  			state = CopyFile;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
  		}
  	} else if (S_ISDIR(mode)) {
83ff98c3e   Christoph Hellwig   init: add an init...
350
  		init_mkdir(collected, mode);
b873498f9   Christoph Hellwig   init: add an init...
351
  		init_chown(collected, uid, gid, 0);
1097742ef   Christoph Hellwig   init: add an init...
352
  		init_chmod(collected, mode);
889d51a10   Nye Liu   initramfs: add op...
353
  		dir_add(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
  	} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
  		   S_ISFIFO(mode) || S_ISSOCK(mode)) {
  		if (maybe_link() == 0) {
5fee64fcd   Christoph Hellwig   init: add an init...
357
  			init_mknod(collected, mode, rdev);
b873498f9   Christoph Hellwig   init: add an init...
358
  			init_chown(collected, uid, gid, 0);
1097742ef   Christoph Hellwig   init: add an init...
359
  			init_chmod(collected, mode);
889d51a10   Nye Liu   initramfs: add op...
360
  			do_utime(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
  		}
  	}
  	return 0;
  }
  
  static int __init do_copy(void)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
368
  	if (byte_count >= body_len) {
38b082236   Christoph Hellwig   initramfs: use vf...
369
  		struct timespec64 t[2] = { };
bf6419e4d   Christoph Hellwig   initramfs: switch...
370
  		if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len)
9687fd910   David Engraf   initramfs: add wr...
371
  			error("write error");
38b082236   Christoph Hellwig   initramfs: use vf...
372
373
374
375
  
  		t[0].tv_sec = mtime;
  		t[1].tv_sec = mtime;
  		vfs_utimes(&wfile->f_path, t);
bf6419e4d   Christoph Hellwig   initramfs: switch...
376
  		fput(wfile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
  		eat(body_len);
  		state = SkipIt;
  		return 0;
  	} else {
bf6419e4d   Christoph Hellwig   initramfs: switch...
381
  		if (xwrite(wfile, victim, byte_count, &wfile_pos) != byte_count)
9687fd910   David Engraf   initramfs: add wr...
382
  			error("write error");
c34d85aca   Mark Rustad   init/initramfs.c:...
383
384
  		body_len -= byte_count;
  		eat(byte_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
390
391
  		return 1;
  	}
  }
  
  static int __init do_symlink(void)
  {
  	collected[N_ALIGN(name_len) + body_len] = '\0';
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
392
  	clean_path(collected, 0);
cd3acb6a7   Christoph Hellwig   init: add an init...
393
  	init_symlink(collected + N_ALIGN(name_len), collected);
b873498f9   Christoph Hellwig   init: add an init...
394
  	init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW);
889d51a10   Nye Liu   initramfs: add op...
395
  	do_utime(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  	state = SkipIt;
  	next_state = Reset;
  	return 0;
  }
  
  static __initdata int (*actions[])(void) = {
  	[Start]		= do_start,
  	[Collect]	= do_collect,
  	[GotHeader]	= do_header,
  	[SkipIt]	= do_skip,
  	[GotName]	= do_name,
  	[CopyFile]	= do_copy,
  	[GotSymlink]	= do_symlink,
  	[Reset]		= do_reset,
  };
d97b07c54   Yinghai Lu   initramfs: suppor...
411
  static long __init write_buffer(char *buf, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
413
  	byte_count = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
  	victim = buf;
  
  	while (!actions[state]())
  		;
c34d85aca   Mark Rustad   init/initramfs.c:...
418
  	return len - byte_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
420
  static long __init flush_buffer(void *bufv, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
  {
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
422
  	char *buf = (char *) bufv;
d97b07c54   Yinghai Lu   initramfs: suppor...
423
424
  	long written;
  	long origLen = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  	if (message)
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
426
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
431
432
433
434
435
436
437
  	while ((written = write_buffer(buf, len)) < len && !message) {
  		char c = buf[written];
  		if (c == '0') {
  			buf += written;
  			len -= written;
  			state = Start;
  		} else if (c == 0) {
  			buf += written;
  			len -= written;
  			state = Reset;
  		} else
e5eed351f   David Engraf   init/initramfs.c:...
438
  			error("junk within compressed archive");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  	}
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
440
  	return origLen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
442
  static unsigned long my_inptr; /* index of next byte to be processed in inbuf */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443

889c92d21   H. Peter Anvin   bzip2/lzma: centr...
444
  #include <linux/decompress/generic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445

d97b07c54   Yinghai Lu   initramfs: suppor...
446
  static char * __init unpack_to_rootfs(char *buf, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  {
d97b07c54   Yinghai Lu   initramfs: suppor...
448
  	long written;
889c92d21   H. Peter Anvin   bzip2/lzma: centr...
449
  	decompress_fn decompress;
23a22d57a   H. Peter Anvin   bzip2/lzma: compr...
450
451
  	const char *compress_name;
  	static __initdata char msg_buf[64];
889c92d21   H. Peter Anvin   bzip2/lzma: centr...
452

3265e66b1   Thomas Petazzoni   directly use kmal...
453
454
455
  	header_buf = kmalloc(110, GFP_KERNEL);
  	symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
  	name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
456
457
  
  	if (!header_buf || !symlink_buf || !name_buf)
dd23e8098   Florian Fainelli   initramfs: panic ...
458
  		panic_show_mem("can't allocate buffers");
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
459

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
  	state = Start;
  	this_header = 0;
  	message = NULL;
  	while (!message && len) {
  		loff_t saved_offset = this_header;
  		if (*buf == '0' && !(this_header & 3)) {
  			state = Start;
  			written = write_buffer(buf, len);
  			buf += written;
  			len -= written;
  			continue;
  		}
  		if (!*buf) {
  			buf++;
  			len--;
  			this_header++;
  			continue;
  		}
  		this_header = 0;
23a22d57a   H. Peter Anvin   bzip2/lzma: compr...
479
  		decompress = decompress_method(buf, len, &compress_name);
6aa7a29aa   Daniel M. Weeks   initramfs: debug ...
480
481
  		pr_debug("Detected %s compressed data
  ", compress_name);
54291362d   Phillip Lougher   initramfs: add mi...
482
  		if (decompress) {
d97b07c54   Yinghai Lu   initramfs: suppor...
483
  			int res = decompress(buf, len, NULL, flush_buffer, NULL,
889c92d21   H. Peter Anvin   bzip2/lzma: centr...
484
  				   &my_inptr, error);
54291362d   Phillip Lougher   initramfs: add mi...
485
486
487
  			if (res)
  				error("decompressor failed");
  		} else if (compress_name) {
23a22d57a   H. Peter Anvin   bzip2/lzma: compr...
488
489
490
491
492
493
  			if (!message) {
  				snprintf(msg_buf, sizeof msg_buf,
  					 "compression method %s not configured",
  					 compress_name);
  				message = msg_buf;
  			}
df37bd156   Phillip Lougher   initramfs: handle...
494
  		} else
e5eed351f   David Engraf   init/initramfs.c:...
495
  			error("invalid magic at start of compressed archive");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
  		if (state != Reset)
e5eed351f   David Engraf   init/initramfs.c:...
497
  			error("junk at the end of compressed archive");
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
498
499
500
  		this_header = saved_offset + my_inptr;
  		buf += my_inptr;
  		len -= my_inptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  	}
889d51a10   Nye Liu   initramfs: add op...
502
  	dir_utime();
3265e66b1   Thomas Petazzoni   directly use kmal...
503
504
505
  	kfree(name_buf);
  	kfree(symlink_buf);
  	kfree(header_buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
  	return message;
  }
0a7b35cb1   Michael Neuling   [PATCH] Add retai...
508
509
510
511
512
513
514
515
516
517
  static int __initdata do_retain_initrd;
  
  static int __init retain_initrd_param(char *str)
  {
  	if (*str)
  		return 0;
  	do_retain_initrd = 1;
  	return 1;
  }
  __setup("retain_initrd", retain_initrd_param);
d8ae8a376   Christoph Hellwig   initramfs: move t...
518
519
520
521
522
523
524
525
  #ifdef CONFIG_ARCH_HAS_KEEPINITRD
  static int __init keepinitrd_setup(char *__unused)
  {
  	do_retain_initrd = 1;
  	return 1;
  }
  __setup("keepinitrd", keepinitrd_setup);
  #endif
e7cb072eb   Rasmus Villemoes   init/initramfs.c:...
526
527
528
529
530
531
532
  static bool __initdata initramfs_async = true;
  static int __init initramfs_async_setup(char *str)
  {
  	strtobool(str, &initramfs_async);
  	return 1;
  }
  __setup("initramfs_async=", initramfs_async_setup);
ffe8018c3   Hendrik Brueckner   initramfs: fix in...
533
534
  extern char __initramfs_start[];
  extern unsigned long __initramfs_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  #include <linux/initrd.h>
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
536
  #include <linux/kexec.h>
0f3d2bd54   Jan Beulich   [PATCH] free init...
537

c72160fe0   Kefeng Wang   initramfs: Provid...
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  void __init reserve_initrd_mem(void)
  {
  	phys_addr_t start;
  	unsigned long size;
  
  	/* Ignore the virtul address computed during device tree parsing */
  	initrd_start = initrd_end = 0;
  
  	if (!phys_initrd_size)
  		return;
  	/*
  	 * Round the memory region to page boundaries as per free_initrd_mem()
  	 * This allows us to detect whether the pages overlapping the initrd
  	 * are in use, but more importantly, reserves the entire set of pages
  	 * as we don't want these pages allocated for other purposes.
  	 */
  	start = round_down(phys_initrd_start, PAGE_SIZE);
  	size = phys_initrd_size + (phys_initrd_start - start);
  	size = round_up(size, PAGE_SIZE);
  
  	if (!memblock_is_region_memory(start, size)) {
  		pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region",
  		       (u64)start, size);
  		goto disable;
  	}
  
  	if (memblock_is_region_reserved(start, size)) {
  		pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region
  ",
  		       (u64)start, size);
  		goto disable;
  	}
  
  	memblock_reserve(start, size);
  	/* Now convert initrd to virtual addresses */
  	initrd_start = (unsigned long)__va(phys_initrd_start);
  	initrd_end = initrd_start + phys_initrd_size;
  	initrd_below_start_ok = 1;
  
  	return;
  disable:
  	pr_cont(" - disabling initrd
  ");
  	initrd_start = 0;
  	initrd_end = 0;
  }
55d5b7dd6   Arnd Bergmann   initramfs: fix cl...
584
  void __weak __init free_initrd_mem(unsigned long start, unsigned long end)
4afd58e14   Christoph Hellwig   initramfs: provid...
585
  {
899ee4afe   Mike Rapoport   arm64: use generi...
586
587
588
589
590
591
  #ifdef CONFIG_ARCH_KEEP_MEMBLOCK
  	unsigned long aligned_start = ALIGN_DOWN(start, PAGE_SIZE);
  	unsigned long aligned_end = ALIGN(end, PAGE_SIZE);
  
  	memblock_free(__pa(aligned_start), aligned_end - aligned_start);
  #endif
f94f7434c   Christoph Hellwig   initramfs: poison...
592
593
  	free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM,
  			"initrd");
4afd58e14   Christoph Hellwig   initramfs: provid...
594
  }
2965faa5e   Dave Young   kexec: split kexe...
595
  #ifdef CONFIG_KEXEC_CORE
e99332e7b   Linus Torvalds   gcc-10: mark more...
596
  static bool __init kexec_free_initrd(void)
23091e287   Christoph Hellwig   initramfs: cleanu...
597
  {
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
598
599
600
601
602
603
604
  	unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
  	unsigned long crashk_end   = (unsigned long)__va(crashk_res.end);
  
  	/*
  	 * If the initrd region is overlapped with crashkernel reserved region,
  	 * free only memory that is not part of crashkernel region.
  	 */
23091e287   Christoph Hellwig   initramfs: cleanu...
605
606
607
608
609
610
611
612
613
614
615
616
  	if (initrd_start >= crashk_end || initrd_end <= crashk_start)
  		return false;
  
  	/*
  	 * Initialize initrd memory region since the kexec boot does not do.
  	 */
  	memset((void *)initrd_start, 0, initrd_end - initrd_start);
  	if (initrd_start < crashk_start)
  		free_initrd_mem(initrd_start, crashk_start);
  	if (initrd_end > crashk_end)
  		free_initrd_mem(crashk_end, initrd_end);
  	return true;
0f3d2bd54   Jan Beulich   [PATCH] free init...
617
  }
23091e287   Christoph Hellwig   initramfs: cleanu...
618
619
620
621
622
623
  #else
  static inline bool kexec_free_initrd(void)
  {
  	return false;
  }
  #endif /* CONFIG_KEXEC_CORE */
0f3d2bd54   Jan Beulich   [PATCH] free init...
624

a841c673f   Andrew Morton   revert "initramfs...
625
  #ifdef CONFIG_BLK_DEV_RAM
4ada1e810   Geert Uytterhoeven   initramfs: fix po...
626
  static void __init populate_initrd_image(char *err)
7c184ecd2   Christoph Hellwig   initramfs: factor...
627
628
  {
  	ssize_t written;
bf6419e4d   Christoph Hellwig   initramfs: switch...
629
630
  	struct file *file;
  	loff_t pos = 0;
7c184ecd2   Christoph Hellwig   initramfs: factor...
631
632
633
634
635
636
  
  	unpack_to_rootfs(__initramfs_start, __initramfs_size);
  
  	printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd
  ",
  			err);
bf6419e4d   Christoph Hellwig   initramfs: switch...
637
638
  	file = filp_open("/initrd.image", O_WRONLY | O_CREAT, 0700);
  	if (IS_ERR(file))
7c184ecd2   Christoph Hellwig   initramfs: factor...
639
  		return;
bf6419e4d   Christoph Hellwig   initramfs: switch...
640
641
  	written = xwrite(file, (char *)initrd_start, initrd_end - initrd_start,
  			&pos);
7c184ecd2   Christoph Hellwig   initramfs: factor...
642
643
644
645
  	if (written != initrd_end - initrd_start)
  		pr_err("/initrd.image: incomplete write (%zd != %ld)
  ",
  		       written, initrd_end - initrd_start);
bf6419e4d   Christoph Hellwig   initramfs: switch...
646
  	fput(file);
7c184ecd2   Christoph Hellwig   initramfs: factor...
647
648
  }
  #endif /* CONFIG_BLK_DEV_RAM */
e7cb072eb   Rasmus Villemoes   init/initramfs.c:...
649
  static void __init do_populate_rootfs(void *unused, async_cookie_t cookie)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
  {
17a9be317   Stafford Horne   initramfs: Always...
651
  	/* Load the built in initramfs */
ffe8018c3   Hendrik Brueckner   initramfs: fix in...
652
  	char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
  	if (err)
dd23e8098   Florian Fainelli   initramfs: panic ...
654
  		panic_show_mem("%s", err); /* Failed to decompress INTERNAL initramfs */
afef7889c   Christoph Hellwig   initramfs: cleanu...
655
656
657
658
659
  
  	if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE))
  		goto done;
  
  	if (IS_ENABLED(CONFIG_BLK_DEV_RAM))
a1e6b6c1a   Eric Piel   initramfs: clean ...
660
661
  		printk(KERN_INFO "Trying to unpack rootfs image as initramfs...
  ");
afef7889c   Christoph Hellwig   initramfs: cleanu...
662
663
664
  	else
  		printk(KERN_INFO "Unpacking initramfs...
  ");
54c7a8916   Christoph Hellwig   initramfs: free i...
665

afef7889c   Christoph Hellwig   initramfs: cleanu...
666
667
  	err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);
  	if (err) {
9ab6b7184   Christoph Hellwig   initramfs: remove...
668
  #ifdef CONFIG_BLK_DEV_RAM
7c184ecd2   Christoph Hellwig   initramfs: factor...
669
  		populate_initrd_image(err);
9ab6b7184   Christoph Hellwig   initramfs: remove...
670
671
672
673
  #else
  		printk(KERN_EMERG "Initramfs unpacking failed: %s
  ", err);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
  	}
23091e287   Christoph Hellwig   initramfs: cleanu...
675

afef7889c   Christoph Hellwig   initramfs: cleanu...
676
  done:
23091e287   Christoph Hellwig   initramfs: cleanu...
677
678
679
680
  	/*
  	 * If the initrd region is overlapped with crashkernel reserved region,
  	 * free only memory that is not part of crashkernel region.
  	 */
5d59aa8f9   Steven Price   initramfs: don't ...
681
  	if (!do_retain_initrd && initrd_start && !kexec_free_initrd())
23091e287   Christoph Hellwig   initramfs: cleanu...
682
683
684
  		free_initrd_mem(initrd_start, initrd_end);
  	initrd_start = 0;
  	initrd_end = 0;
17a9be317   Stafford Horne   initramfs: Always...
685
  	flush_delayed_fput();
e7cb072eb   Rasmus Villemoes   init/initramfs.c:...
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
  }
  
  static ASYNC_DOMAIN_EXCLUSIVE(initramfs_domain);
  static async_cookie_t initramfs_cookie;
  
  void wait_for_initramfs(void)
  {
  	if (!initramfs_cookie) {
  		/*
  		 * Something before rootfs_initcall wants to access
  		 * the filesystem/initramfs. Probably a bug. Make a
  		 * note, avoid deadlocking the machine, and let the
  		 * caller's access fail as it used to.
  		 */
  		pr_warn_once("wait_for_initramfs() called before rootfs_initcalls
  ");
  		return;
  	}
  	async_synchronize_cookie_domain(initramfs_cookie + 1, &initramfs_domain);
  }
  EXPORT_SYMBOL_GPL(wait_for_initramfs);
  
  static int __init populate_rootfs(void)
  {
  	initramfs_cookie = async_schedule_domain(do_populate_rootfs, NULL,
  						 &initramfs_domain);
b234ed6d6   Rasmus Villemoes   init: move usermo...
712
  	usermodehelper_enable();
e7cb072eb   Rasmus Villemoes   init/initramfs.c:...
713
714
  	if (!initramfs_async)
  		wait_for_initramfs();
8d610dd52   Linus Torvalds   Make sure we popu...
715
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  }
8d610dd52   Linus Torvalds   Make sure we popu...
717
  rootfs_initcall(populate_rootfs);