Blame view

init/initramfs.c 14 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
c67e5382f   H Hartley Sweeten   init: disable spa...
2
3
4
5
6
7
8
9
10
  /*
   * Many of the syscalls used in this file expect some of the arguments
   * to be __user pointers not __kernel pointers.  To limit the sparse
   * noise, turn off sparse checking for this file.
   */
  #ifdef __CHECKER__
  #undef __CHECKER__
  #warning "Sparse checking disabled for this file"
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
16
17
  #include <linux/init.h>
  #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 ...
18
  #include <linux/dirent.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/syscalls.h>
889d51a10   Nye Liu   initramfs: add op...
20
  #include <linux/utime.h>
088655148   Lokesh Vutla   initramfs: finish...
21
  #include <linux/file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

387474399   Yinghai Lu   initramfs: suppor...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  static ssize_t __init xwrite(int fd, const char *p, size_t count)
  {
  	ssize_t out = 0;
  
  	/* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */
  	while (count) {
  		ssize_t rv = sys_write(fd, p, count);
  
  		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
45
46
47
48
49
50
  static __initdata char *message;
  static void __init error(char *x)
  {
  	if (!message)
  		message = x;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  /* link hash */
6a050da45   Mark Huang   [PATCH] initramfs...
52
  #define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
  static __initdata struct hash {
  	int ino, minor, major;
685dd2d5b   Al Viro   init/initramfs.c:...
55
  	umode_t mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  	struct hash *next;
6a050da45   Mark Huang   [PATCH] initramfs...
57
  	char name[N_ALIGN(PATH_MAX)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
64
65
  } *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...
66
  static char __init *find_link(int major, int minor, int ino,
685dd2d5b   Al Viro   init/initramfs.c:...
67
  			      umode_t mode, char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
73
74
75
76
  {
  	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...
77
78
  		if (((*p)->mode ^ mode) & S_IFMT)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
  		return (*p)->name;
  	}
3265e66b1   Thomas Petazzoni   directly use kmal...
81
  	q = kmalloc(sizeof(struct hash), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
  	if (!q)
  		panic("can't allocate link hash entry");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  	q->major = major;
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
85
86
87
  	q->minor = minor;
  	q->ino = ino;
  	q->mode = mode;
6a050da45   Mark Huang   [PATCH] initramfs...
88
  	strcpy(q->name, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
92
93
94
95
96
97
98
99
100
  	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...
101
  			kfree(q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  		}
  	}
  }
c67e5382f   H Hartley Sweeten   init: disable spa...
105
  static long __init do_utime(char *filename, time_t mtime)
889d51a10   Nye Liu   initramfs: add op...
106
  {
aaed2dd8a   Deepa Dinamani   utimes: Make utim...
107
  	struct timespec64 t[2];
889d51a10   Nye Liu   initramfs: add op...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  
  	t[0].tv_sec = mtime;
  	t[0].tv_nsec = 0;
  	t[1].tv_sec = mtime;
  	t[1].tv_nsec = 0;
  
  	return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW);
  }
  
  static __initdata LIST_HEAD(dir_list);
  struct dir_entry {
  	struct list_head list;
  	char *name;
  	time_t mtime;
  };
  
  static void __init dir_add(const char *name, time_t mtime)
  {
  	struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
  	if (!de)
  		panic("can't allocate dir_entry buffer");
  	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);
  	}
  }
  
  static __initdata time_t mtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
  /* cpio header parsing */
  
  static __initdata unsigned long ino, major, minor, nlink;
685dd2d5b   Al Viro   init/initramfs.c:...
150
  static __initdata umode_t mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  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];
889d51a10   Nye Liu   initramfs: add op...
172
  	mtime = parsed[5];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  	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:...
194
  static unsigned long byte_count __initdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  static __initdata loff_t this_header, next_header;
b0a5ab931   Al Viro   initramfs: missin...
196
  static inline void __init eat(unsigned n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
  {
  	victim += n;
  	this_header += n;
c34d85aca   Mark Rustad   init/initramfs.c:...
200
  	byte_count -= n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  }
889d51a10   Nye Liu   initramfs: add op...
202
  static __initdata char *vcollected;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  static __initdata char *collected;
d97b07c54   Yinghai Lu   initramfs: suppor...
204
  static long remains __initdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
  static __initdata char *collect;
  
  static void __init read_into(char *buf, unsigned size, enum state next)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
209
  	if (byte_count >= size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  		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...
231
  	unsigned long n = remains;
c34d85aca   Mark Rustad   init/initramfs.c:...
232
233
  	if (byte_count < n)
  		n = byte_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
238
239
240
241
242
243
244
  	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...
245
246
247
248
  	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
249
250
251
252
253
254
255
  	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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  	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:...
275
276
  	if (this_header + byte_count < next_header) {
  		eat(byte_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
281
282
283
284
285
286
  		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:...
287
  	while (byte_count && *victim == '\0')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  		eat(1);
c34d85aca   Mark Rustad   init/initramfs.c:...
289
  	if (byte_count && (this_header & 3))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
295
296
  		error("broken padding");
  	return 1;
  }
  
  static int __init maybe_link(void)
  {
  	if (nlink >= 2) {
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
297
  		char *old = find_link(major, minor, ino, mode, collected);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
  		if (old)
  			return (sys_link(old, collected) < 0) ? -1 : 1;
  	}
  	return 0;
  }
c34d85aca   Mark Rustad   init/initramfs.c:...
303
  static void __init clean_path(char *path, umode_t fmode)
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
304
  {
046aa1265   Arnd Bergmann   initramfs: use vf...
305
  	struct kstat st;
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
306

046aa1265   Arnd Bergmann   initramfs: use vf...
307
308
  	if (!vfs_lstat(path, &st) && (st.mode ^ fmode) & S_IFMT) {
  		if (S_ISDIR(st.mode))
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
309
310
311
312
313
  			sys_rmdir(path);
  		else
  			sys_unlink(path);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
320
321
322
323
  static __initdata int wfd;
  
  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...
324
  	clean_path(collected, mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
  	if (S_ISREG(mode)) {
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
326
327
328
329
330
331
  		int ml = maybe_link();
  		if (ml >= 0) {
  			int openflags = O_WRONLY|O_CREAT;
  			if (ml != 1)
  				openflags |= O_TRUNC;
  			wfd = sys_open(collected, openflags, mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
334
  			if (wfd >= 0) {
  				sys_fchown(wfd, uid, gid);
  				sys_fchmod(wfd, mode);
d20d5a745   Randy Robertson   initramfs: fix in...
335
336
  				if (body_len)
  					sys_ftruncate(wfd, body_len);
889d51a10   Nye Liu   initramfs: add op...
337
  				vcollected = kstrdup(collected, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
342
343
344
  				state = CopyFile;
  			}
  		}
  	} else if (S_ISDIR(mode)) {
  		sys_mkdir(collected, mode);
  		sys_chown(collected, uid, gid);
  		sys_chmod(collected, mode);
889d51a10   Nye Liu   initramfs: add op...
345
  		dir_add(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
350
351
  	} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
  		   S_ISFIFO(mode) || S_ISSOCK(mode)) {
  		if (maybe_link() == 0) {
  			sys_mknod(collected, mode, rdev);
  			sys_chown(collected, uid, gid);
  			sys_chmod(collected, mode);
889d51a10   Nye Liu   initramfs: add op...
352
  			do_utime(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
358
359
  		}
  	}
  	return 0;
  }
  
  static int __init do_copy(void)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
360
  	if (byte_count >= body_len) {
9687fd910   David Engraf   initramfs: add wr...
361
362
  		if (xwrite(wfd, victim, body_len) != body_len)
  			error("write error");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  		sys_close(wfd);
889d51a10   Nye Liu   initramfs: add op...
364
365
  		do_utime(vcollected, mtime);
  		kfree(vcollected);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
  		eat(body_len);
  		state = SkipIt;
  		return 0;
  	} else {
c34d85aca   Mark Rustad   init/initramfs.c:...
370
  		if (xwrite(wfd, victim, byte_count) != byte_count)
9687fd910   David Engraf   initramfs: add wr...
371
  			error("write error");
c34d85aca   Mark Rustad   init/initramfs.c:...
372
373
  		body_len -= byte_count;
  		eat(byte_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
377
378
379
380
  		return 1;
  	}
  }
  
  static int __init do_symlink(void)
  {
  	collected[N_ALIGN(name_len) + body_len] = '\0';
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
381
  	clean_path(collected, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  	sys_symlink(collected + N_ALIGN(name_len), collected);
  	sys_lchown(collected, uid, gid);
889d51a10   Nye Liu   initramfs: add op...
384
  	do_utime(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  	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...
400
  static long __init write_buffer(char *buf, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
402
  	byte_count = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
  	victim = buf;
  
  	while (!actions[state]())
  		;
c34d85aca   Mark Rustad   init/initramfs.c:...
407
  	return len - byte_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
409
  static long __init flush_buffer(void *bufv, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  {
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
411
  	char *buf = (char *) bufv;
d97b07c54   Yinghai Lu   initramfs: suppor...
412
413
  	long written;
  	long origLen = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  	if (message)
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
415
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
419
420
421
422
423
424
425
426
427
428
  	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
  			error("junk in compressed archive");
  	}
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
429
  	return origLen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
431
  static unsigned long my_inptr; /* index of next byte to be processed in inbuf */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432

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

d97b07c54   Yinghai Lu   initramfs: suppor...
435
  static char * __init unpack_to_rootfs(char *buf, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  {
d97b07c54   Yinghai Lu   initramfs: suppor...
437
  	long written;
889c92d21   H. Peter Anvin   bzip2/lzma: centr...
438
  	decompress_fn decompress;
23a22d57a   H. Peter Anvin   bzip2/lzma: compr...
439
440
  	const char *compress_name;
  	static __initdata char msg_buf[64];
889c92d21   H. Peter Anvin   bzip2/lzma: centr...
441

3265e66b1   Thomas Petazzoni   directly use kmal...
442
443
444
  	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...
445
446
  
  	if (!header_buf || !symlink_buf || !name_buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  		panic("can't allocate buffers");
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
448

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
  	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...
468
  		decompress = decompress_method(buf, len, &compress_name);
6aa7a29aa   Daniel M. Weeks   initramfs: debug ...
469
470
  		pr_debug("Detected %s compressed data
  ", compress_name);
54291362d   Phillip Lougher   initramfs: add mi...
471
  		if (decompress) {
d97b07c54   Yinghai Lu   initramfs: suppor...
472
  			int res = decompress(buf, len, NULL, flush_buffer, NULL,
889c92d21   H. Peter Anvin   bzip2/lzma: centr...
473
  				   &my_inptr, error);
54291362d   Phillip Lougher   initramfs: add mi...
474
475
476
  			if (res)
  				error("decompressor failed");
  		} else if (compress_name) {
23a22d57a   H. Peter Anvin   bzip2/lzma: compr...
477
478
479
480
481
482
  			if (!message) {
  				snprintf(msg_buf, sizeof msg_buf,
  					 "compression method %s not configured",
  					 compress_name);
  				message = msg_buf;
  			}
df37bd156   Phillip Lougher   initramfs: handle...
483
484
  		} else
  			error("junk in compressed archive");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  		if (state != Reset)
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
486
487
488
489
  			error("junk in compressed archive");
  		this_header = saved_offset + my_inptr;
  		buf += my_inptr;
  		len -= my_inptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  	}
889d51a10   Nye Liu   initramfs: add op...
491
  	dir_utime();
3265e66b1   Thomas Petazzoni   directly use kmal...
492
493
494
  	kfree(name_buf);
  	kfree(symlink_buf);
  	kfree(header_buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
  	return message;
  }
0a7b35cb1   Michael Neuling   [PATCH] Add retai...
497
498
499
500
501
502
503
504
505
506
  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);
ffe8018c3   Hendrik Brueckner   initramfs: fix in...
507
508
  extern char __initramfs_start[];
  extern unsigned long __initramfs_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  #include <linux/initrd.h>
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
510
  #include <linux/kexec.h>
0f3d2bd54   Jan Beulich   [PATCH] free init...
511
512
513
  
  static void __init free_initrd(void)
  {
2965faa5e   Dave Young   kexec: split kexe...
514
  #ifdef CONFIG_KEXEC_CORE
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
515
516
  	unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
  	unsigned long crashk_end   = (unsigned long)__va(crashk_res.end);
0a7b35cb1   Michael Neuling   [PATCH] Add retai...
517
518
519
  #endif
  	if (do_retain_initrd)
  		goto skip;
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
520

2965faa5e   Dave Young   kexec: split kexe...
521
  #ifdef CONFIG_KEXEC_CORE
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
  	/*
  	 * If the initrd region is overlapped with crashkernel reserved region,
  	 * free only memory that is not part of crashkernel region.
  	 */
  	if (initrd_start < crashk_end && initrd_end > crashk_start) {
  		/*
  		 * 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);
  	} else
  #endif
  		free_initrd_mem(initrd_start, initrd_end);
0a7b35cb1   Michael Neuling   [PATCH] Add retai...
539
  skip:
0f3d2bd54   Jan Beulich   [PATCH] free init...
540
541
542
  	initrd_start = 0;
  	initrd_end = 0;
  }
b52bb3712   Nikanth Karthikesan   init/initramfs: f...
543
  #ifdef CONFIG_BLK_DEV_RAM
df52092f3   Li, Shaohua   fastboot: remove ...
544
545
546
547
548
549
  #define BUF_SIZE 1024
  static void __init clean_rootfs(void)
  {
  	int fd;
  	void *buf;
  	struct linux_dirent64 *dirp;
8aaed5bec   H Hartley Sweeten   init/initramfs.c:...
550
  	int num;
df52092f3   Li, Shaohua   fastboot: remove ...
551

c67e5382f   H Hartley Sweeten   init: disable spa...
552
  	fd = sys_open("/", O_RDONLY, 0);
df52092f3   Li, Shaohua   fastboot: remove ...
553
554
555
556
557
558
559
560
561
562
563
  	WARN_ON(fd < 0);
  	if (fd < 0)
  		return;
  	buf = kzalloc(BUF_SIZE, GFP_KERNEL);
  	WARN_ON(!buf);
  	if (!buf) {
  		sys_close(fd);
  		return;
  	}
  
  	dirp = buf;
8aaed5bec   H Hartley Sweeten   init/initramfs.c:...
564
565
566
  	num = sys_getdents64(fd, dirp, BUF_SIZE);
  	while (num > 0) {
  		while (num > 0) {
046aa1265   Arnd Bergmann   initramfs: use vf...
567
  			struct kstat st;
df52092f3   Li, Shaohua   fastboot: remove ...
568
  			int ret;
046aa1265   Arnd Bergmann   initramfs: use vf...
569
  			ret = vfs_lstat(dirp->d_name, &st);
df52092f3   Li, Shaohua   fastboot: remove ...
570
571
  			WARN_ON_ONCE(ret);
  			if (!ret) {
046aa1265   Arnd Bergmann   initramfs: use vf...
572
  				if (S_ISDIR(st.mode))
df52092f3   Li, Shaohua   fastboot: remove ...
573
574
575
576
  					sys_rmdir(dirp->d_name);
  				else
  					sys_unlink(dirp->d_name);
  			}
8aaed5bec   H Hartley Sweeten   init/initramfs.c:...
577
  			num -= dirp->d_reclen;
df52092f3   Li, Shaohua   fastboot: remove ...
578
579
580
581
  			dirp = (void *)dirp + dirp->d_reclen;
  		}
  		dirp = buf;
  		memset(buf, 0, BUF_SIZE);
8aaed5bec   H Hartley Sweeten   init/initramfs.c:...
582
  		num = sys_getdents64(fd, dirp, BUF_SIZE);
df52092f3   Li, Shaohua   fastboot: remove ...
583
584
585
586
587
  	}
  
  	sys_close(fd);
  	kfree(buf);
  }
b52bb3712   Nikanth Karthikesan   init/initramfs: f...
588
  #endif
df52092f3   Li, Shaohua   fastboot: remove ...
589

9a9e0d685   Linus Torvalds   ACPI: Remove ACPI...
590
  static int __init populate_rootfs(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  {
17a9be317   Stafford Horne   initramfs: Always...
592
  	/* Load the built in initramfs */
ffe8018c3   Hendrik Brueckner   initramfs: fix in...
593
  	char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
  	if (err)
499a4584d   Tetsuo Handa   init: fix possibl...
595
  		panic("%s", err); /* Failed to decompress INTERNAL initramfs */
17a9be317   Stafford Horne   initramfs: Always...
596
  	/* If available load the bootloader supplied initrd */
cff75e0b6   Daniel Thompson   initramfs: provid...
597
  	if (initrd_start && !IS_ENABLED(CONFIG_INITRAMFS_FORCE)) {
340e48e66   Zdenek Pavlas   [PATCH] BLK_DEV_I...
598
  #ifdef CONFIG_BLK_DEV_RAM
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
  		int fd;
a1e6b6c1a   Eric Piel   initramfs: clean ...
600
601
  		printk(KERN_INFO "Trying to unpack rootfs image as initramfs...
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  		err = unpack_to_rootfs((char *)initrd_start,
df52092f3   Li, Shaohua   fastboot: remove ...
603
  			initrd_end - initrd_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  		if (!err) {
0f3d2bd54   Jan Beulich   [PATCH] free init...
605
  			free_initrd();
bb813f4c9   Tejun Heo   init, block: try ...
606
  			goto done;
df52092f3   Li, Shaohua   fastboot: remove ...
607
608
  		} else {
  			clean_rootfs();
ffe8018c3   Hendrik Brueckner   initramfs: fix in...
609
  			unpack_to_rootfs(__initramfs_start, __initramfs_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  		}
c1c490e01   Simon Kitching   initramfs: preven...
611
612
613
  		printk(KERN_INFO "rootfs image is not initramfs (%s)"
  				"; looks like an initrd
  ", err);
c67e5382f   H Hartley Sweeten   init: disable spa...
614
  		fd = sys_open("/initrd.image",
562f5e638   Namhyung Kim   init: mark __user...
615
  			      O_WRONLY|O_CREAT, 0700);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
  		if (fd >= 0) {
387474399   Yinghai Lu   initramfs: suppor...
617
618
619
620
621
622
623
  			ssize_t written = xwrite(fd, (char *)initrd_start,
  						initrd_end - initrd_start);
  
  			if (written != initrd_end - initrd_start)
  				pr_err("/initrd.image: incomplete write (%zd != %ld)
  ",
  				       written, initrd_end - initrd_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
  			sys_close(fd);
0f3d2bd54   Jan Beulich   [PATCH] free init...
625
  			free_initrd();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
  		}
bb813f4c9   Tejun Heo   init, block: try ...
627
  	done:
394e4f5d5   Linus Torvalds   initramfs: avoid ...
628
  		/* empty statement */;
340e48e66   Zdenek Pavlas   [PATCH] BLK_DEV_I...
629
  #else
a1e6b6c1a   Eric Piel   initramfs: clean ...
630
631
  		printk(KERN_INFO "Unpacking initramfs...
  ");
340e48e66   Zdenek Pavlas   [PATCH] BLK_DEV_I...
632
  		err = unpack_to_rootfs((char *)initrd_start,
df52092f3   Li, Shaohua   fastboot: remove ...
633
  			initrd_end - initrd_start);
a1e6b6c1a   Eric Piel   initramfs: clean ...
634
635
636
  		if (err)
  			printk(KERN_EMERG "Initramfs unpacking failed: %s
  ", err);
340e48e66   Zdenek Pavlas   [PATCH] BLK_DEV_I...
637
638
  		free_initrd();
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  	}
17a9be317   Stafford Horne   initramfs: Always...
640
641
642
643
644
645
  	flush_delayed_fput();
  	/*
  	 * Try loading default modules from initramfs.  This gives
  	 * us a chance to load before device_initcalls.
  	 */
  	load_default_modules();
8d610dd52   Linus Torvalds   Make sure we popu...
646
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  }
8d610dd52   Linus Torvalds   Make sure we popu...
648
  rootfs_initcall(populate_rootfs);