Blame view

init/initramfs.c 13.8 KB
c67e5382f   H Hartley Sweeten   init: disable spa...
1
2
3
4
5
6
7
8
9
  /*
   * 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
10
11
12
13
14
15
16
  #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 ...
17
  #include <linux/dirent.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/syscalls.h>
889d51a10   Nye Liu   initramfs: add op...
19
  #include <linux/utime.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20

387474399   Yinghai Lu   initramfs: suppor...
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  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
43
44
45
46
47
48
  static __initdata char *message;
  static void __init error(char *x)
  {
  	if (!message)
  		message = x;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  /* link hash */
6a050da45   Mark Huang   [PATCH] initramfs...
50
  #define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
  static __initdata struct hash {
  	int ino, minor, major;
685dd2d5b   Al Viro   init/initramfs.c:...
53
  	umode_t mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  	struct hash *next;
6a050da45   Mark Huang   [PATCH] initramfs...
55
  	char name[N_ALIGN(PATH_MAX)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
  } *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...
64
  static char __init *find_link(int major, int minor, int ino,
685dd2d5b   Al Viro   init/initramfs.c:...
65
  			      umode_t mode, char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
70
71
72
73
74
  {
  	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...
75
76
  		if (((*p)->mode ^ mode) & S_IFMT)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  		return (*p)->name;
  	}
3265e66b1   Thomas Petazzoni   directly use kmal...
79
  	q = kmalloc(sizeof(struct hash), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  	if (!q)
  		panic("can't allocate link hash entry");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  	q->major = major;
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
83
84
85
  	q->minor = minor;
  	q->ino = ino;
  	q->mode = mode;
6a050da45   Mark Huang   [PATCH] initramfs...
86
  	strcpy(q->name, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
90
91
92
93
94
95
96
97
98
  	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...
99
  			kfree(q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
  		}
  	}
  }
c67e5382f   H Hartley Sweeten   init: disable spa...
103
  static long __init do_utime(char *filename, time_t mtime)
889d51a10   Nye Liu   initramfs: add op...
104
105
106
107
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
  {
  	struct timespec t[2];
  
  	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
145
146
147
  /* cpio header parsing */
  
  static __initdata unsigned long ino, major, minor, nlink;
685dd2d5b   Al Viro   init/initramfs.c:...
148
  static __initdata umode_t mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  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...
170
  	mtime = parsed[5];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  	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:...
192
  static unsigned long byte_count __initdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  static __initdata loff_t this_header, next_header;
b0a5ab931   Al Viro   initramfs: missin...
194
  static inline void __init eat(unsigned n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
  {
  	victim += n;
  	this_header += n;
c34d85aca   Mark Rustad   init/initramfs.c:...
198
  	byte_count -= n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  }
889d51a10   Nye Liu   initramfs: add op...
200
  static __initdata char *vcollected;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  static __initdata char *collected;
d97b07c54   Yinghai Lu   initramfs: suppor...
202
  static long remains __initdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
  static __initdata char *collect;
  
  static void __init read_into(char *buf, unsigned size, enum state next)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
207
  	if (byte_count >= size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  		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...
229
  	unsigned long n = remains;
c34d85aca   Mark Rustad   init/initramfs.c:...
230
231
  	if (byte_count < n)
  		n = byte_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
237
238
239
240
241
242
  	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...
243
244
245
246
  	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
247
248
249
250
251
252
253
  	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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  	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:...
273
274
  	if (this_header + byte_count < next_header) {
  		eat(byte_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
283
284
  		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:...
285
  	while (byte_count && *victim == '\0')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  		eat(1);
c34d85aca   Mark Rustad   init/initramfs.c:...
287
  	if (byte_count && (this_header & 3))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
292
293
294
  		error("broken padding");
  	return 1;
  }
  
  static int __init maybe_link(void)
  {
  	if (nlink >= 2) {
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
295
  		char *old = find_link(major, minor, ino, mode, collected);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
300
  		if (old)
  			return (sys_link(old, collected) < 0) ? -1 : 1;
  	}
  	return 0;
  }
c34d85aca   Mark Rustad   init/initramfs.c:...
301
  static void __init clean_path(char *path, umode_t fmode)
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
302
303
  {
  	struct stat st;
c34d85aca   Mark Rustad   init/initramfs.c:...
304
  	if (!sys_newlstat(path, &st) && (st.st_mode ^ fmode) & S_IFMT) {
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
305
306
307
308
309
310
  		if (S_ISDIR(st.st_mode))
  			sys_rmdir(path);
  		else
  			sys_unlink(path);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
  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...
321
  	clean_path(collected, mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
  	if (S_ISREG(mode)) {
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
323
324
325
326
327
328
  		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
329
330
331
  			if (wfd >= 0) {
  				sys_fchown(wfd, uid, gid);
  				sys_fchmod(wfd, mode);
d20d5a745   Randy Robertson   initramfs: fix in...
332
333
  				if (body_len)
  					sys_ftruncate(wfd, body_len);
889d51a10   Nye Liu   initramfs: add op...
334
  				vcollected = kstrdup(collected, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
  				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...
342
  		dir_add(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
  	} 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...
349
  			do_utime(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
354
355
356
  		}
  	}
  	return 0;
  }
  
  static int __init do_copy(void)
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
357
  	if (byte_count >= body_len) {
9687fd910   David Engraf   initramfs: add wr...
358
359
  		if (xwrite(wfd, victim, body_len) != body_len)
  			error("write error");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  		sys_close(wfd);
889d51a10   Nye Liu   initramfs: add op...
361
362
  		do_utime(vcollected, mtime);
  		kfree(vcollected);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
  		eat(body_len);
  		state = SkipIt;
  		return 0;
  	} else {
c34d85aca   Mark Rustad   init/initramfs.c:...
367
  		if (xwrite(wfd, victim, byte_count) != byte_count)
9687fd910   David Engraf   initramfs: add wr...
368
  			error("write error");
c34d85aca   Mark Rustad   init/initramfs.c:...
369
370
  		body_len -= byte_count;
  		eat(byte_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
374
375
376
377
  		return 1;
  	}
  }
  
  static int __init do_symlink(void)
  {
  	collected[N_ALIGN(name_len) + body_len] = '\0';
2139a7fbf   H. Peter Anvin   [PATCH] initramfs...
378
  	clean_path(collected, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
  	sys_symlink(collected + N_ALIGN(name_len), collected);
  	sys_lchown(collected, uid, gid);
889d51a10   Nye Liu   initramfs: add op...
381
  	do_utime(collected, mtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  	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...
397
  static long __init write_buffer(char *buf, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
  {
c34d85aca   Mark Rustad   init/initramfs.c:...
399
  	byte_count = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
  	victim = buf;
  
  	while (!actions[state]())
  		;
c34d85aca   Mark Rustad   init/initramfs.c:...
404
  	return len - byte_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
406
  static long __init flush_buffer(void *bufv, unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
  {
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
408
  	char *buf = (char *) bufv;
d97b07c54   Yinghai Lu   initramfs: suppor...
409
410
  	long written;
  	long origLen = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  	if (message)
30d65dbfe   Alain Knaff   bzip2/lzma: confi...
412
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
418
419
420
421
422
423
424
425
  	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...
426
  	return origLen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
428
  static unsigned long my_inptr; /* index of next byte to be processed in inbuf */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429

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

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

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

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

2965faa5e   Dave Young   kexec: split kexe...
518
  #ifdef CONFIG_KEXEC_CORE
9c15e852a   Haren Myneni   [PATCH] kexec: fi...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
  	/*
  	 * 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...
536
  skip:
0f3d2bd54   Jan Beulich   [PATCH] free init...
537
538
539
  	initrd_start = 0;
  	initrd_end = 0;
  }
b52bb3712   Nikanth Karthikesan   init/initramfs: f...
540
  #ifdef CONFIG_BLK_DEV_RAM
df52092f3   Li, Shaohua   fastboot: remove ...
541
542
543
544
545
546
  #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:...
547
  	int num;
df52092f3   Li, Shaohua   fastboot: remove ...
548

c67e5382f   H Hartley Sweeten   init: disable spa...
549
  	fd = sys_open("/", O_RDONLY, 0);
df52092f3   Li, Shaohua   fastboot: remove ...
550
551
552
553
554
555
556
557
558
559
560
  	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:...
561
562
563
  	num = sys_getdents64(fd, dirp, BUF_SIZE);
  	while (num > 0) {
  		while (num > 0) {
df52092f3   Li, Shaohua   fastboot: remove ...
564
565
566
567
568
569
570
571
572
573
574
  			struct stat st;
  			int ret;
  
  			ret = sys_newlstat(dirp->d_name, &st);
  			WARN_ON_ONCE(ret);
  			if (!ret) {
  				if (S_ISDIR(st.st_mode))
  					sys_rmdir(dirp->d_name);
  				else
  					sys_unlink(dirp->d_name);
  			}
8aaed5bec   H Hartley Sweeten   init/initramfs.c:...
575
  			num -= dirp->d_reclen;
df52092f3   Li, Shaohua   fastboot: remove ...
576
577
578
579
  			dirp = (void *)dirp + dirp->d_reclen;
  		}
  		dirp = buf;
  		memset(buf, 0, BUF_SIZE);
8aaed5bec   H Hartley Sweeten   init/initramfs.c:...
580
  		num = sys_getdents64(fd, dirp, BUF_SIZE);
df52092f3   Li, Shaohua   fastboot: remove ...
581
582
583
584
585
  	}
  
  	sys_close(fd);
  	kfree(buf);
  }
b52bb3712   Nikanth Karthikesan   init/initramfs: f...
586
  #endif
df52092f3   Li, Shaohua   fastboot: remove ...
587

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