fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / init / initramfs.c
index d0ce388..4fa0f79 100644 (file)
@@ -26,10 +26,13 @@ static void __init free(void *where)
 
 /* link hash */
 
-static struct hash {
+#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
+
+static __initdata struct hash {
        int ino, minor, major;
+       mode_t mode;
        struct hash *next;
-       char *name;
+       char name[N_ALIGN(PATH_MAX)];
 } *head[32];
 
 static inline int hash(int major, int minor, int ino)
@@ -39,7 +42,8 @@ static inline int hash(int major, int minor, int ino)
        return tmp & 31;
 }
 
-static char __init *find_link(int major, int minor, int ino, char *name)
+static char __init *find_link(int major, int minor, int ino,
+                             mode_t mode, char *name)
 {
        struct hash **p, *q;
        for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) {
@@ -49,15 +53,18 @@ static char __init *find_link(int major, int minor, int ino, char *name)
                        continue;
                if ((*p)->major != major)
                        continue;
+               if (((*p)->mode ^ mode) & S_IFMT)
+                       continue;
                return (*p)->name;
        }
        q = (struct hash *)malloc(sizeof(struct hash));
        if (!q)
                panic("can't allocate link hash entry");
-       q->ino = ino;
-       q->minor = minor;
        q->major = major;
-       q->name = name;
+       q->minor = minor;
+       q->ino = ino;
+       q->mode = mode;
+       strcpy(q->name, name);
        q->next = NULL;
        *p = q;
        return NULL;
@@ -133,8 +140,6 @@ static inline void eat(unsigned n)
        count -= n;
 }
 
-#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
-
 static __initdata char *collected;
 static __initdata int remains;
 static __initdata char *collect;
@@ -169,7 +174,7 @@ static int __init do_collect(void)
        memcpy(collect, victim, n);
        eat(n);
        collect += n;
-       if (remains -= n)
+       if ((remains -= n) != 0)
                return 1;
        state = next_state;
        return 0;
@@ -177,6 +182,10 @@ static int __init do_collect(void)
 
 static int __init do_header(void)
 {
+       if (memcmp(collected, "070707", 6)==0) {
+               error("incorrect cpio method used: use -H newc option");
+               return 1;
+       }
        if (memcmp(collected, "070701", 6)) {
                error("no cpio magic");
                return 1;
@@ -207,7 +216,7 @@ static int __init do_header(void)
 
 static int __init do_skip(void)
 {
-       if (this_header + count <= next_header) {
+       if (this_header + count < next_header) {
                eat(count);
                return 1;
        } else {
@@ -229,29 +238,46 @@ static int __init do_reset(void)
 static int __init maybe_link(void)
 {
        if (nlink >= 2) {
-               char *old = find_link(major, minor, ino, collected);
+               char *old = find_link(major, minor, ino, mode, collected);
                if (old)
                        return (sys_link(old, collected) < 0) ? -1 : 1;
        }
        return 0;
 }
 
+static void __init clean_path(char *path, mode_t mode)
+{
+       struct stat st;
+
+       if (!sys_newlstat(path, &st) && (st.st_mode^mode) & S_IFMT) {
+               if (S_ISDIR(st.st_mode))
+                       sys_rmdir(path);
+               else
+                       sys_unlink(path);
+       }
+}
+
 static __initdata int wfd;
 
 static int __init do_name(void)
 {
        state = SkipIt;
-       next_state = Start;
+       next_state = Reset;
        if (strcmp(collected, "TRAILER!!!") == 0) {
                free_hash();
-               next_state = Reset;
                return 0;
        }
        if (dry_run)
                return 0;
+       clean_path(collected, mode);
        if (S_ISREG(mode)) {
-               if (maybe_link() >= 0) {
-                       wfd = sys_open(collected, O_WRONLY|O_CREAT, mode);
+               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);
+
                        if (wfd >= 0) {
                                sys_fchown(wfd, uid, gid);
                                sys_fchmod(wfd, mode);
@@ -292,10 +318,11 @@ static int __init do_copy(void)
 static int __init do_symlink(void)
 {
        collected[N_ALIGN(name_len) + body_len] = '\0';
+       clean_path(collected, 0);
        sys_symlink(collected + N_ALIGN(name_len), collected);
        sys_lchown(collected, uid, gid);
        state = SkipIt;
-       next_state = Start;
+       next_state = Reset;
        return 0;
 }
 
@@ -331,6 +358,10 @@ static void __init flush_buffer(char *buf, unsigned len)
                        buf += written;
                        len -= written;
                        state = Start;
+               } else if (c == 0) {
+                       buf += written;
+                       len -= written;
+                       state = Reset;
                } else
                        error("junk in compressed archive");
        }
@@ -372,11 +403,12 @@ static long bytes_out;
 #define Tracecv(c,x)
 
 #define STATIC static
+#define INIT __init
 
-static void flush_window(void);
-static void error(char *m);
-static void gzip_mark(void **);
-static void gzip_release(void **);
+static void __init flush_window(void);
+static void __init error(char *m);
+static void __init gzip_mark(void **);
+static void __init gzip_release(void **);
 
 #include "../lib/inflate.c"
 
@@ -409,7 +441,7 @@ static void __init flush_window(void)
        outcnt = 0;
 }
 
-char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
+static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
 {
        int written;
        dry_run = check_only;
@@ -445,8 +477,7 @@ char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
                bytes_out = 0;
                crc = (ulg)0xffffffffL; /* shift register contents */
                makecrc();
-               if (gunzip())
-                       message = "ungzip failed";
+               gunzip();
                if (state != Reset)
                        error("junk in gzipped archive");
                this_header = saved_offset + inptr;
@@ -460,19 +491,50 @@ char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
        return message;
 }
 
-extern char __initramfs_start, __initramfs_end;
+extern char __initramfs_start[], __initramfs_end[];
 #ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/initrd.h>
+#include <linux/kexec.h>
+
+static void __init free_initrd(void)
+{
+#ifdef CONFIG_KEXEC
+       unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
+       unsigned long crashk_end   = (unsigned long)__va(crashk_res.end);
+
+       /*
+        * 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);
+
+       initrd_start = 0;
+       initrd_end = 0;
+}
+
 #endif
 
-void __init populate_rootfs(void)
+static int __init populate_rootfs(void)
 {
-       char *err = unpack_to_rootfs(&__initramfs_start,
-                        &__initramfs_end - &__initramfs_start, 0);
+       char *err = unpack_to_rootfs(__initramfs_start,
+                        __initramfs_end - __initramfs_start, 0);
        if (err)
                panic(err);
 #ifdef CONFIG_BLK_DEV_INITRD
        if (initrd_start) {
+#ifdef CONFIG_BLK_DEV_RAM
                int fd;
                printk(KERN_INFO "checking if image is initramfs...");
                err = unpack_to_rootfs((char *)initrd_start,
@@ -481,17 +543,28 @@ void __init populate_rootfs(void)
                        printk(" it is\n");
                        unpack_to_rootfs((char *)initrd_start,
                                initrd_end - initrd_start, 0);
-                       free_initrd_mem(initrd_start, initrd_end);
-                       return;
+                       free_initrd();
+                       return 0;
                }
                printk("it isn't (%s); looks like an initrd\n", err);
-               fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);
+               fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
                if (fd >= 0) {
                        sys_write(fd, (char *)initrd_start,
                                        initrd_end - initrd_start);
                        sys_close(fd);
-                       free_initrd_mem(initrd_start, initrd_end);
+                       free_initrd();
                }
+#else
+               printk(KERN_INFO "Unpacking initramfs...");
+               err = unpack_to_rootfs((char *)initrd_start,
+                       initrd_end - initrd_start, 0);
+               if (err)
+                       panic(err);
+               printk(" done\n");
+               free_initrd();
+#endif
        }
 #endif
+       return 0;
 }
+rootfs_initcall(populate_rootfs);