Fix PCMCIA cut-and-paste cs.c bug introduced by the recent
[linux-2.6/history.git] / init / initramfs.c
blob5a9f5b3b05f7ae3d79f08a9fe1eda98f1d2e9c64
1 #define __KERNEL_SYSCALLS__
2 #include <linux/init.h>
3 #include <linux/fs.h>
4 #include <linux/slab.h>
5 #include <linux/types.h>
6 #include <linux/fcntl.h>
7 #include <linux/unistd.h>
8 #include <linux/delay.h>
9 #include <linux/string.h>
11 static void __init error(char *x)
13 panic("populate_root: %s\n", x);
16 static void __init *malloc(int size)
18 return kmalloc(size, GFP_KERNEL);
21 static void __init free(void *where)
23 kfree(where);
26 asmlinkage long sys_mkdir(char *name, int mode);
27 asmlinkage long sys_mknod(char *name, int mode, unsigned dev);
28 asmlinkage long sys_symlink(char *old, char *new);
29 asmlinkage long sys_link(char *old, char *new);
30 asmlinkage long sys_write(int fd, const char *buf, size_t size);
31 asmlinkage long sys_chown(char *name, uid_t uid, gid_t gid);
32 asmlinkage long sys_lchown(char *name, uid_t uid, gid_t gid);
33 asmlinkage long sys_fchown(int fd, uid_t uid, gid_t gid);
34 asmlinkage long sys_chmod(char *name, mode_t mode);
35 asmlinkage long sys_fchmod(int fd, mode_t mode);
37 /* link hash */
39 static struct hash {
40 int ino, minor, major;
41 struct hash *next;
42 char *name;
43 } *head[32];
45 static inline int hash(int major, int minor, int ino)
47 unsigned long tmp = ino + minor + (major << 3);
48 tmp += tmp >> 5;
49 return tmp & 31;
52 static char __init *find_link(int major, int minor, int ino, char *name)
54 struct hash **p, *q;
55 for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
56 if ((*p)->ino != ino)
57 continue;
58 if ((*p)->minor != minor)
59 continue;
60 if ((*p)->major != major)
61 continue;
62 return (*p)->name;
64 q = (struct hash *)malloc(sizeof(struct hash));
65 if (!q)
66 error("can't allocate link hash entry");
67 q->ino = ino;
68 q->minor = minor;
69 q->major = major;
70 q->name = name;
71 q->next = NULL;
72 *p = q;
73 return NULL;
76 static void __init free_hash(void)
78 struct hash **p, *q;
79 for (p = head; p < head + 32; p++) {
80 while (*p) {
81 q = *p;
82 *p = q->next;
83 free(q);
88 /* cpio header parsing */
90 static __initdata unsigned long ino, major, minor, nlink;
91 static __initdata mode_t mode;
92 static __initdata unsigned long body_len, name_len;
93 static __initdata uid_t uid;
94 static __initdata gid_t gid;
95 static __initdata unsigned rdev;
97 static void __init parse_header(char *s)
99 unsigned long parsed[12];
100 char buf[9];
101 int i;
103 buf[8] = '\0';
104 for (i = 0, s += 6; i < 12; i++, s += 8) {
105 memcpy(buf, s, 8);
106 parsed[i] = simple_strtoul(buf, NULL, 16);
108 ino = parsed[0];
109 mode = parsed[1];
110 uid = parsed[2];
111 gid = parsed[3];
112 nlink = parsed[4];
113 body_len = parsed[6];
114 major = parsed[7];
115 minor = parsed[8];
116 rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
117 name_len = parsed[11];
120 /* FSM */
122 enum state {
123 Start,
124 Collect,
125 GotHeader,
126 SkipIt,
127 GotName,
128 CopyFile,
129 GotSymlink,
130 Reset
131 } state, next_state;
133 char *victim;
134 unsigned count;
135 loff_t this_header, next_header;
137 static inline void eat(unsigned n)
139 victim += n;
140 this_header += n;
141 count -= n;
144 #define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
146 static __initdata char *collected;
147 static __initdata int remains;
148 static __initdata char *collect;
150 static void __init read_into(char *buf, unsigned size, enum state next)
152 if (count >= size) {
153 collected = victim;
154 eat(size);
155 state = next;
156 } else {
157 collect = collected = buf;
158 remains = size;
159 next_state = next;
160 state = Collect;
164 static __initdata char *header_buf, *symlink_buf, *name_buf;
166 static int __init do_start(void)
168 read_into(header_buf, 110, GotHeader);
169 return 0;
172 static int __init do_collect(void)
174 unsigned n = remains;
175 if (count < n)
176 n = count;
177 memcpy(collect, victim, n);
178 eat(n);
179 collect += n;
180 if (remains -= n)
181 return 1;
182 state = next_state;
183 return 0;
186 static int __init do_header(void)
188 parse_header(collected);
189 next_header = this_header + N_ALIGN(name_len) + body_len;
190 next_header = (next_header + 3) & ~3;
191 if (name_len <= 0 || name_len > PATH_MAX)
192 state = SkipIt;
193 else if (S_ISLNK(mode)) {
194 if (body_len > PATH_MAX)
195 state = SkipIt;
196 else {
197 collect = collected = symlink_buf;
198 remains = N_ALIGN(name_len) + body_len;
199 next_state = GotSymlink;
200 state = Collect;
202 } else if (body_len && !S_ISREG(mode))
203 state = SkipIt;
204 else
205 read_into(name_buf, N_ALIGN(name_len), GotName);
206 return 0;
209 static int __init do_skip(void)
211 if (this_header + count <= next_header) {
212 eat(count);
213 return 1;
214 } else {
215 eat(next_header - this_header);
216 state = next_state;
217 return 0;
221 static int __init do_reset(void)
223 while(count && *victim == '\0')
224 eat(1);
225 if (count && (this_header & 3))
226 error("broken padding");
227 return 1;
230 static int __init maybe_link(void)
232 if (nlink >= 2) {
233 char *old = find_link(major, minor, ino, collected);
234 if (old)
235 return (sys_link(old, collected) < 0) ? -1 : 1;
237 return 0;
240 static __initdata int wfd;
242 static int __init do_name(void)
244 state = SkipIt;
245 next_state = Start;
246 if (strcmp(collected, "TRAILER!!!") == 0) {
247 free_hash();
248 next_state = Reset;
249 return 0;
251 if (S_ISREG(mode)) {
252 if (maybe_link() >= 0) {
253 wfd = sys_open(collected, O_WRONLY|O_CREAT, mode);
254 if (wfd >= 0) {
255 sys_fchown(wfd, uid, gid);
256 sys_fchmod(wfd, mode);
257 state = CopyFile;
260 } else if (S_ISDIR(mode)) {
261 sys_mkdir(collected, mode);
262 sys_chown(collected, uid, gid);
263 sys_chmod(collected, mode);
264 } else if (S_ISBLK(mode) || S_ISCHR(mode) ||
265 S_ISFIFO(mode) || S_ISSOCK(mode)) {
266 if (maybe_link() == 0) {
267 sys_mknod(collected, mode, rdev);
268 sys_chown(collected, uid, gid);
269 sys_chmod(collected, mode);
271 } else
272 panic("populate_root: bogus mode: %o\n", mode);
273 return 0;
276 static int __init do_copy(void)
278 if (count >= body_len) {
279 sys_write(wfd, victim, body_len);
280 sys_close(wfd);
281 eat(body_len);
282 state = SkipIt;
283 return 0;
284 } else {
285 sys_write(wfd, victim, count);
286 body_len -= count;
287 eat(count);
288 return 1;
292 static int __init do_symlink(void)
294 collected[N_ALIGN(name_len) + body_len] = '\0';
295 sys_symlink(collected + N_ALIGN(name_len), collected);
296 sys_lchown(collected, uid, gid);
297 state = SkipIt;
298 next_state = Start;
299 return 0;
302 static __initdata int (*actions[])(void) = {
303 [Start] = do_start,
304 [Collect] = do_collect,
305 [GotHeader] = do_header,
306 [SkipIt] = do_skip,
307 [GotName] = do_name,
308 [CopyFile] = do_copy,
309 [GotSymlink] = do_symlink,
310 [Reset] = do_reset,
313 static int __init write_buffer(char *buf, unsigned len)
315 count = len;
316 victim = buf;
318 while (!actions[state]())
320 return len - count;
323 static void __init flush_buffer(char *buf, unsigned len)
325 int written;
326 while ((written = write_buffer(buf, len)) < len) {
327 char c = buf[written];
328 if (c == '0') {
329 buf += written;
330 len -= written;
331 state = Start;
332 continue;
333 } else
334 error("junk in compressed archive");
339 * gzip declarations
342 #define OF(args) args
344 #ifndef memzero
345 #define memzero(s, n) memset ((s), 0, (n))
346 #endif
348 typedef unsigned char uch;
349 typedef unsigned short ush;
350 typedef unsigned long ulg;
352 #define WSIZE 0x8000 /* window size--must be a power of two, and */
353 /* at least 32K for zip's deflate method */
355 static uch *inbuf;
356 static uch *window;
358 static unsigned insize; /* valid bytes in inbuf */
359 static unsigned inptr; /* index of next byte to be processed in inbuf */
360 static unsigned outcnt; /* bytes in output buffer */
361 static long bytes_out;
363 #define get_byte() (inptr < insize ? inbuf[inptr++] : -1)
365 /* Diagnostic functions (stubbed out) */
366 #define Assert(cond,msg)
367 #define Trace(x)
368 #define Tracev(x)
369 #define Tracevv(x)
370 #define Tracec(c,x)
371 #define Tracecv(c,x)
373 #define STATIC static
375 static void flush_window(void);
376 static void error(char *m);
377 static void gzip_mark(void **);
378 static void gzip_release(void **);
380 #include "../lib/inflate.c"
382 static void __init gzip_mark(void **ptr)
386 static void __init gzip_release(void **ptr)
390 /* ===========================================================================
391 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
392 * (Used for the decompressed data only.)
394 static void __init flush_window(void)
396 ulg c = crc; /* temporary variable */
397 unsigned n;
398 uch *in, ch;
400 flush_buffer(window, outcnt);
401 in = window;
402 for (n = 0; n < outcnt; n++) {
403 ch = *in++;
404 c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
406 crc = c;
407 bytes_out += (ulg)outcnt;
408 outcnt = 0;
411 static void __init unpack_to_rootfs(char *buf, unsigned len)
413 int written;
414 header_buf = malloc(110);
415 symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1);
416 name_buf = malloc(N_ALIGN(PATH_MAX));
417 window = malloc(WSIZE);
418 if (!window || !header_buf || !symlink_buf || !name_buf)
419 error("can't allocate buffers");
420 state = Start;
421 this_header = 0;
422 while (len) {
423 loff_t saved_offset = this_header;
424 if (*buf == '0' && !(this_header & 3)) {
425 state = Start;
426 written = write_buffer(buf, len);
427 buf += written;
428 len -= written;
429 continue;
430 } else if (!*buf) {
431 buf++;
432 len--;
433 this_header++;
434 continue;
436 this_header = 0;
437 insize = len;
438 inbuf = buf;
439 inptr = 0;
440 outcnt = 0; /* bytes in output buffer */
441 bytes_out = 0;
442 crc = (ulg)0xffffffffL; /* shift register contents */
443 makecrc();
444 if (gunzip())
445 error("ungzip failed");
446 if (state != Reset)
447 error("junk in gzipped archive");
448 this_header = saved_offset + inptr;
449 buf += inptr;
450 len -= inptr;
452 free(window);
453 free(name_buf);
454 free(symlink_buf);
455 free(header_buf);
458 extern char __initramfs_start, __initramfs_end;
460 void __init populate_rootfs(void)
462 unpack_to_rootfs(&__initramfs_start,
463 &__initramfs_end - &__initramfs_start);