+- update patches.suse/early_userspace-instead-of-linuxrc
[linux-flexiantxendom0-3.2.10.git] / init / initramfs.c
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>
10 #include <linux/vmalloc.h>
11
12 #ifdef CONFIG_ACPI_INITRD
13                 unsigned char *dsdt_start;
14 #endif
15
16 static __initdata char *message;
17
18 static void __init error(char *x)
19 {
20         if (!message)
21                 message = x;
22 }
23
24 static void __init *malloc(int size)
25 {
26         return kmalloc(size, GFP_KERNEL);
27 }
28
29 static void __init free(void *where)
30 {
31         kfree(where);
32 }
33
34 asmlinkage long sys_mkdir(char *name, int mode);
35 asmlinkage long sys_mknod(char *name, int mode, unsigned dev);
36 asmlinkage long sys_symlink(char *old, char *new);
37 asmlinkage long sys_link(char *old, char *new);
38 asmlinkage long sys_write(int fd, const char *buf, size_t size);
39 asmlinkage long sys_chown(char *name, uid_t uid, gid_t gid);
40 asmlinkage long sys_lchown(char *name, uid_t uid, gid_t gid);
41 asmlinkage long sys_fchown(int fd, uid_t uid, gid_t gid);
42 asmlinkage long sys_chmod(char *name, mode_t mode);
43 asmlinkage long sys_fchmod(int fd, mode_t mode);
44
45 /* link hash */
46
47 static struct hash {
48         int ino, minor, major;
49         struct hash *next;
50         char *name;
51 } *head[32];
52
53 static inline int hash(int major, int minor, int ino)
54 {
55         unsigned long tmp = ino + minor + (major << 3);
56         tmp += tmp >> 5;
57         return tmp & 31;
58 }
59
60 static char __init *find_link(int major, int minor, int ino, char *name)
61 {
62         struct hash **p, *q;
63         for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
64                 if ((*p)->ino != ino)
65                         continue;
66                 if ((*p)->minor != minor)
67                         continue;
68                 if ((*p)->major != major)
69                         continue;
70                 return (*p)->name;
71         }
72         q = (struct hash *)malloc(sizeof(struct hash));
73         if (!q)
74                 panic("can't allocate link hash entry");
75         q->ino = ino;
76         q->minor = minor;
77         q->major = major;
78         q->name = name;
79         q->next = NULL;
80         *p = q;
81         return NULL;
82 }
83
84 static void __init free_hash(void)
85 {
86         struct hash **p, *q;
87         for (p = head; p < head + 32; p++) {
88                 while (*p) {
89                         q = *p;
90                         *p = q->next;
91                         free(q);
92                 }
93         }
94 }
95
96 /* cpio header parsing */
97
98 static __initdata unsigned long ino, major, minor, nlink;
99 static __initdata mode_t mode;
100 static __initdata unsigned long body_len, name_len;
101 static __initdata uid_t uid;
102 static __initdata gid_t gid;
103 static __initdata unsigned rdev;
104
105 static void __init parse_header(char *s)
106 {
107         unsigned long parsed[12];
108         char buf[9];
109         int i;
110
111         buf[8] = '\0';
112         for (i = 0, s += 6; i < 12; i++, s += 8) {
113                 memcpy(buf, s, 8);
114                 parsed[i] = simple_strtoul(buf, NULL, 16);
115         }
116         ino = parsed[0];
117         mode = parsed[1];
118         uid = parsed[2];
119         gid = parsed[3];
120         nlink = parsed[4];
121         body_len = parsed[6];
122         major = parsed[7];
123         minor = parsed[8];
124         rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
125         name_len = parsed[11];
126 }
127
128 /* FSM */
129
130 static __initdata enum state {
131         Start,
132         Collect,
133         GotHeader,
134         SkipIt,
135         GotName,
136         CopyFile,
137         GotSymlink,
138         Reset
139 } state, next_state;
140
141 static __initdata char *victim;
142 static __initdata unsigned count;
143 static __initdata loff_t this_header, next_header;
144
145 static __initdata int dry_run;
146
147 static inline void eat(unsigned n)
148 {
149         victim += n;
150         this_header += n;
151         count -= n;
152 }
153
154 #define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
155
156 static __initdata char *collected;
157 static __initdata int remains;
158 static __initdata char *collect;
159
160 static void __init read_into(char *buf, unsigned size, enum state next)
161 {
162         if (count >= size) {
163                 collected = victim;
164                 eat(size);
165                 state = next;
166         } else {
167                 collect = collected = buf;
168                 remains = size;
169                 next_state = next;
170                 state = Collect;
171         }
172 }
173
174 static __initdata char *header_buf, *symlink_buf, *name_buf;
175
176 static int __init do_start(void)
177 {
178         read_into(header_buf, 110, GotHeader);
179         return 0;
180 }
181
182 static int __init do_collect(void)
183 {
184         unsigned n = remains;
185         if (count < n)
186                 n = count;
187         memcpy(collect, victim, n);
188         eat(n);
189         collect += n;
190         if (remains -= n)
191                 return 1;
192         state = next_state;
193         return 0;
194 }
195
196 static int __init do_header(void)
197 {
198         if (memcmp(collected, "070701", 6)) {
199                 error("no cpio magic");
200                 return 1;
201         }
202         parse_header(collected);
203         next_header = this_header + N_ALIGN(name_len) + body_len;
204         next_header = (next_header + 3) & ~3;
205         if (dry_run) {
206                 read_into(name_buf, N_ALIGN(name_len), GotName);
207                 return 0;
208         }
209         state = SkipIt;
210         if (name_len <= 0 || name_len > PATH_MAX)
211                 return 0;
212         if (S_ISLNK(mode)) {
213                 if (body_len > PATH_MAX)
214                         return 0;
215                 collect = collected = symlink_buf;
216                 remains = N_ALIGN(name_len) + body_len;
217                 next_state = GotSymlink;
218                 state = Collect;
219                 return 0;
220         }
221         if (S_ISREG(mode) || !body_len)
222                 read_into(name_buf, N_ALIGN(name_len), GotName);
223         return 0;
224 }
225
226 static int __init do_skip(void)
227 {
228         if (this_header + count <= next_header) {
229                 eat(count);
230                 return 1;
231         } else {
232                 eat(next_header - this_header);
233                 state = next_state;
234                 return 0;
235         }
236 }
237
238 static int __init do_reset(void)
239 {
240         while(count && *victim == '\0')
241                 eat(1);
242         if (count && (this_header & 3))
243                 error("broken padding");
244         return 1;
245 }
246
247 static int __init maybe_link(void)
248 {
249         if (nlink >= 2) {
250                 char *old = find_link(major, minor, ino, collected);
251                 if (old)
252                         return (sys_link(old, collected) < 0) ? -1 : 1;
253         }
254         return 0;
255 }
256
257 static __initdata int wfd;
258
259 static int __init do_name(void)
260 {
261         state = SkipIt;
262         next_state = Start;
263         if (strcmp(collected, "TRAILER!!!") == 0) {
264                 free_hash();
265                 next_state = Reset;
266                 return 0;
267         }
268         if (dry_run)
269                 return 0;
270         if (S_ISREG(mode)) {
271                 if (maybe_link() >= 0) {
272                         wfd = sys_open(collected, O_WRONLY|O_CREAT, mode);
273                         if (wfd >= 0) {
274                                 sys_fchown(wfd, uid, gid);
275                                 sys_fchmod(wfd, mode);
276                                 state = CopyFile;
277                         }
278                 }
279         } else if (S_ISDIR(mode)) {
280                 sys_mkdir(collected, mode);
281                 sys_chown(collected, uid, gid);
282                 sys_chmod(collected, mode);
283         } else if (S_ISBLK(mode) || S_ISCHR(mode) ||
284                    S_ISFIFO(mode) || S_ISSOCK(mode)) {
285                 if (maybe_link() == 0) {
286                         sys_mknod(collected, mode, rdev);
287                         sys_chown(collected, uid, gid);
288                         sys_chmod(collected, mode);
289                 }
290         }
291         return 0;
292 }
293
294 static int __init do_copy(void)
295 {
296         if (count >= body_len) {
297                 sys_write(wfd, victim, body_len);
298                 sys_close(wfd);
299                 eat(body_len);
300                 state = SkipIt;
301                 return 0;
302         } else {
303                 sys_write(wfd, victim, count);
304                 body_len -= count;
305                 eat(count);
306                 return 1;
307         }
308 }
309
310 static int __init do_symlink(void)
311 {
312         collected[N_ALIGN(name_len) + body_len] = '\0';
313         sys_symlink(collected + N_ALIGN(name_len), collected);
314         sys_lchown(collected, uid, gid);
315         state = SkipIt;
316         next_state = Start;
317         return 0;
318 }
319
320 static __initdata int (*actions[])(void) = {
321         [Start]         = do_start,
322         [Collect]       = do_collect,
323         [GotHeader]     = do_header,
324         [SkipIt]        = do_skip,
325         [GotName]       = do_name,
326         [CopyFile]      = do_copy,
327         [GotSymlink]    = do_symlink,
328         [Reset]         = do_reset,
329 };
330
331 static int __init write_buffer(char *buf, unsigned len)
332 {
333         count = len;
334         victim = buf;
335
336         while (!actions[state]())
337                 ;
338         return len - count;
339 }
340
341 static void __init flush_buffer(char *buf, unsigned len)
342 {
343         int written;
344         if (message)
345                 return;
346         while ((written = write_buffer(buf, len)) < len && !message) {
347                 char c = buf[written];
348                 if (c == '0') {
349                         buf += written;
350                         len -= written;
351                         state = Start;
352                 } else
353                         error("junk in compressed archive");
354         }
355 }
356
357 /*
358  * gzip declarations
359  */
360
361 #define OF(args)  args
362
363 #ifndef memzero
364 #define memzero(s, n)     memset ((s), 0, (n))
365 #endif
366
367 typedef unsigned char  uch;
368 typedef unsigned short ush;
369 typedef unsigned long  ulg;
370
371 #define WSIZE 0x8000    /* window size--must be a power of two, and */
372                         /*  at least 32K for zip's deflate method */
373
374 static uch *inbuf;
375 static uch *window;
376
377 static unsigned insize;  /* valid bytes in inbuf */
378 static unsigned inptr;   /* index of next byte to be processed in inbuf */
379 static unsigned outcnt;  /* bytes in output buffer */
380 static long bytes_out;
381
382 #define get_byte()  (inptr < insize ? inbuf[inptr++] : -1)
383                 
384 /* Diagnostic functions (stubbed out) */
385 #define Assert(cond,msg)
386 #define Trace(x)
387 #define Tracev(x)
388 #define Tracevv(x)
389 #define Tracec(c,x)
390 #define Tracecv(c,x)
391
392 #define STATIC static
393
394 static void flush_window(void);
395 static void error(char *m);
396 static void gzip_mark(void **);
397 static void gzip_release(void **);
398
399 #include "../lib/inflate.c"
400
401 static void __init gzip_mark(void **ptr)
402 {
403 }
404
405 static void __init gzip_release(void **ptr)
406 {
407 }
408
409 /* ===========================================================================
410  * Write the output window window[0..outcnt-1] and update crc and bytes_out.
411  * (Used for the decompressed data only.)
412  */
413 static void __init flush_window(void)
414 {
415         ulg c = crc;         /* temporary variable */
416         unsigned n;
417         uch *in, ch;
418
419         flush_buffer(window, outcnt);
420         in = window;
421         for (n = 0; n < outcnt; n++) {
422                 ch = *in++;
423                 c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
424         }
425         crc = c;
426         bytes_out += (ulg)outcnt;
427         outcnt = 0;
428 }
429
430 char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
431 {
432         int written;
433         dry_run = check_only;
434         header_buf = malloc(110);
435         symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1);
436         name_buf = malloc(N_ALIGN(PATH_MAX));
437         window = malloc(WSIZE);
438         if (!window || !header_buf || !symlink_buf || !name_buf)
439                 panic("can't allocate buffers");
440         state = Start;
441         this_header = 0;
442         message = NULL;
443         while (!message && len) {
444                 loff_t saved_offset = this_header;
445                 if (*buf == '0' && !(this_header & 3)) {
446                         state = Start;
447                         written = write_buffer(buf, len);
448                         buf += written;
449                         len -= written;
450                         continue;
451                 }
452                 if (!*buf) {
453                         buf++;
454                         len--;
455                         this_header++;
456                         continue;
457                 }
458                 this_header = 0;
459                 insize = len;
460                 inbuf = buf;
461                 inptr = 0;
462                 outcnt = 0;             /* bytes in output buffer */
463                 bytes_out = 0;
464                 crc = (ulg)0xffffffffL; /* shift register contents */
465                 makecrc();
466                 if (gunzip())
467                         message = "ungzip failed";
468                 if (state != Reset)
469                         error("junk in gzipped archive");
470                 this_header = saved_offset + inptr;
471                 buf += inptr;
472                 len -= inptr;
473         }
474         free(window);
475         free(name_buf);
476         free(symlink_buf);
477         free(header_buf);
478         return message;
479 }
480
481 extern char __initramfs_start, __initramfs_end;
482 #ifdef CONFIG_BLK_DEV_INITRD
483 #include <linux/initrd.h>
484 #endif
485
486 void __init populate_rootfs(void)
487 {
488         char *err = unpack_to_rootfs(&__initramfs_start,
489                          &__initramfs_end - &__initramfs_start, 0);
490         if (err)
491                 panic(err);
492 #ifdef CONFIG_BLK_DEV_INITRD
493         if (initrd_start) {
494                 int fd;
495                 
496                 
497                 printk(KERN_INFO "checking if image is initramfs...");
498                 err = unpack_to_rootfs((char *)initrd_start,
499                                initrd_end - initrd_start, 1);
500                 if (!err) {
501                         printk(" it is\n");
502                         unpack_to_rootfs((char *)initrd_start,
503                                  initrd_end - initrd_start, 0);
504                         free_initrd_mem(initrd_start, initrd_end);
505                         return;
506                 }
507                 printk("it isn't (%s); looks like an initrd\n", err);
508                 
509 #ifdef CONFIG_ACPI_INITRD
510                 unsigned char start_signature[] = "INITRDDSDT123DSDT123";
511                 unsigned char end_signature[] =   "INITRDDSDT321DSDT321";
512                 unsigned char *data;
513                 unsigned char *dsdt_start_tmp = NULL;
514                 unsigned char *initrd_end_tmp = NULL;
515                 dsdt_start = NULL;
516                 
517                 printk(KERN_INFO "Looking for DSDT in initrd ...");
518                  //* don't scan above end, do not modify initrd borders *//
519                 initrd_end_tmp=(unsigned char*)initrd_end-sizeof(end_signature);
520
521                 // searching for start signature in initrd
522                 for (data=(unsigned char*)initrd_start; 
523                         data < (unsigned char*)initrd_end_tmp ; ++data) {
524                         
525                         if (!memcmp(data, start_signature, 
526                                 sizeof(start_signature)-1)) {
527                                 printk(KERN_INFO " found (at offset %u in initrd)!\n", 
528                                         data+sizeof(start_signature)-
529                                         (unsigned char*)initrd_start);
530                                 dsdt_start_tmp = data+sizeof(start_signature);
531                                 break;
532                         }
533                 }
534                 // check if head of dsdt is valid
535                 if (dsdt_start_tmp != NULL && !memcmp(dsdt_start_tmp, "DSDT", 4)) {
536                         // searching for end signature in initrd
537                         for (data += sizeof(end_signature); 
538                                 data <= (unsigned char*)initrd_end; data++){  
539                                 if (!memcmp(data, end_signature, 
540                                         sizeof(end_signature)-1)){
541                                         break;
542                                 }
543                         }
544                         printk (KERN_INFO "size of dsdt: %u!\n", data-dsdt_start_tmp);
545                         // DSDT could be about 10-200kb, maybe more? 
546                         // could kmalloc be used ?
547                         // am I allowed to use vmalloc ?
548                         dsdt_start = vmalloc(data-dsdt_start_tmp+1);
549                         memcpy(dsdt_start, dsdt_start_tmp, data-dsdt_start_tmp);
550                         printk(KERN_INFO "%d bytes allocated and copied for DSDT", data-dsdt_start_tmp);
551                 }
552                 else{
553                         printk(KERN_INFO "No customized DSDT found in initrd!\n");
554                 }
555 #endif 
556                 
557                 fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);
558                 if (fd >= 0) {
559                         sys_write(fd, (char *)initrd_start,
560                                         initrd_end - initrd_start);
561                         sys_close(fd);
562                         free_initrd_mem(initrd_start, initrd_end);
563                 }
564         }
565 #endif
566 }