[PATCH] clean up ip2 glue (not yet ported tho)
[linux-2.6/history.git] / init / initramfs.c
blobcbd1a538d85e3fcd31b0bd96b3f8ff20d75820a8
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, dev_t 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 dev_t 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 = 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 printk(KERN_INFO "-> %s\n", collected);
252 if (S_ISREG(mode)) {
253 if (maybe_link() >= 0) {
254 wfd = sys_open(collected, O_WRONLY|O_CREAT, mode);
255 if (wfd >= 0) {
256 sys_fchown(wfd, uid, gid);
257 sys_fchmod(wfd, mode);
258 state = CopyFile;
261 } else if (S_ISDIR(mode)) {
262 sys_mkdir(collected, mode);
263 sys_chown(collected, uid, gid);
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);
270 } else
271 panic("populate_root: bogus mode: %o\n", mode);
272 return 0;
275 static int __init do_copy(void)
277 if (count >= body_len) {
278 sys_write(wfd, victim, body_len);
279 sys_close(wfd);
280 eat(body_len);
281 state = SkipIt;
282 return 0;
283 } else {
284 sys_write(wfd, victim, count);
285 body_len -= count;
286 eat(count);
287 return 1;
291 static int __init do_symlink(void)
293 collected[N_ALIGN(name_len) + body_len] = '\0';
294 sys_symlink(collected + N_ALIGN(name_len), collected);
295 sys_lchown(collected, uid, gid);
296 state = SkipIt;
297 next_state = Start;
298 return 0;
301 static __initdata int (*actions[])(void) = {
302 [Start] = do_start,
303 [Collect] = do_collect,
304 [GotHeader] = do_header,
305 [SkipIt] = do_skip,
306 [GotName] = do_name,
307 [CopyFile] = do_copy,
308 [GotSymlink] = do_symlink,
309 [Reset] = do_reset,
312 static int __init write_buffer(char *buf, unsigned len)
314 count = len;
315 victim = buf;
317 while (!actions[state]())
319 return len - count;
322 static void __init flush_buffer(char *buf, unsigned len)
324 int written;
325 while ((written = write_buffer(buf, len)) < len) {
326 char c = buf[written];
327 if (c == '0') {
328 buf += written;
329 len -= written;
330 state = Start;
331 continue;
332 } else
333 error("junk in compressed archive");
338 * gzip declarations
341 #define OF(args) args
343 #ifndef memzero
344 #define memzero(s, n) memset ((s), 0, (n))
345 #endif
347 typedef unsigned char uch;
348 typedef unsigned short ush;
349 typedef unsigned long ulg;
351 #define WSIZE 0x8000 /* window size--must be a power of two, and */
352 /* at least 32K for zip's deflate method */
354 static uch *inbuf;
355 static uch *window;
357 static unsigned insize; /* valid bytes in inbuf */
358 static unsigned inptr; /* index of next byte to be processed in inbuf */
359 static unsigned outcnt; /* bytes in output buffer */
360 static long bytes_out;
362 #define get_byte() (inptr < insize ? inbuf[inptr++] : -1)
364 /* Diagnostic functions (stubbed out) */
365 #define Assert(cond,msg)
366 #define Trace(x)
367 #define Tracev(x)
368 #define Tracevv(x)
369 #define Tracec(c,x)
370 #define Tracecv(c,x)
372 #define STATIC static
374 static void flush_window(void);
375 static void error(char *m);
376 static void gzip_mark(void **);
377 static void gzip_release(void **);
379 #include "../lib/inflate.c"
381 static void __init gzip_mark(void **ptr)
385 static void __init gzip_release(void **ptr)
389 /* ===========================================================================
390 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
391 * (Used for the decompressed data only.)
393 static void __init flush_window(void)
395 ulg c = crc; /* temporary variable */
396 unsigned n;
397 uch *in, ch;
399 flush_buffer(window, outcnt);
400 in = window;
401 for (n = 0; n < outcnt; n++) {
402 ch = *in++;
403 c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
405 crc = c;
406 bytes_out += (ulg)outcnt;
407 outcnt = 0;
410 static void __init unpack_to_rootfs(char *buf, unsigned len)
412 int written;
413 header_buf = malloc(110);
414 symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1);
415 name_buf = malloc(N_ALIGN(PATH_MAX));
416 window = malloc(WSIZE);
417 if (!window || !header_buf || !symlink_buf || !name_buf)
418 error("can't allocate buffers");
419 state = Start;
420 this_header = 0;
421 while (len) {
422 loff_t saved_offset = this_header;
423 if (*buf == '0' && !(this_header & 3)) {
424 state = Start;
425 written = write_buffer(buf, len);
426 buf += written;
427 len -= written;
428 continue;
429 } else if (!*buf) {
430 buf++;
431 len--;
432 this_header++;
433 continue;
435 this_header = 0;
436 insize = len;
437 inbuf = buf;
438 inptr = 0;
439 outcnt = 0; /* bytes in output buffer */
440 bytes_out = 0;
441 crc = (ulg)0xffffffffL; /* shift register contents */
442 makecrc();
443 if (gunzip())
444 error("ungzip failed");
445 if (state != Reset)
446 error("junk in gzipped archive");
447 this_header = saved_offset + inptr;
448 buf += inptr;
449 len -= inptr;
451 free(window);
452 free(name_buf);
453 free(symlink_buf);
454 free(header_buf);
457 extern char __initramfs_start, __initramfs_end;
459 void __init populate_rootfs(void)
461 unpack_to_rootfs(&__initramfs_start,
462 &__initramfs_end - &__initramfs_start);