From a06650df599e79a1330dc21eca929b4b87f803ec Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Fri, 1 Nov 2002 02:02:04 -0500 Subject: [PATCH] Minimal initramfs support (based on Al Viro's work). --- Makefile | 2 +- arch/i386/Makefile | 1 + arch/i386/vmlinux.lds.S | 4 + init/Makefile | 2 +- init/do_mounts.c | 4 +- init/initramfs.c | 462 +++++++++++++++++++++++++++++++++++++++++++++++ init/main.c | 2 + usr/Makefile | 18 ++ usr/gen_init_cpio.c | 137 ++++++++++++++ 9 files changed, 627 insertions(+), 5 deletions(-) create mode 100644 init/initramfs.c create mode 100644 usr/Makefile create mode 100644 usr/gen_init_cpio.c diff --git a/Makefile b/Makefile index bd10588..705b3b3 100644 --- a/Makefile +++ b/Makefile @@ -209,7 +209,7 @@ init-y := init/ drivers-y := drivers/ sound/ net-y := net/ libs-y := lib/ -core-y := +core-y := usr/ SUBDIRS := ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),) diff --git a/arch/i386/Makefile b/arch/i386/Makefile index eeaf539..b8dab22 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -18,6 +18,7 @@ LDFLAGS := -m elf_i386 OBJCOPYFLAGS := -O binary -R .note -R .comment -S +ARCHBLOBLFLAGS := -I binary -O elf32-i386 -B i386 LDFLAGS_vmlinux := -e stext CFLAGS += -pipe diff --git a/arch/i386/vmlinux.lds.S b/arch/i386/vmlinux.lds.S index 99301e9..5d6f29d 100644 --- a/arch/i386/vmlinux.lds.S +++ b/arch/i386/vmlinux.lds.S @@ -77,6 +77,10 @@ SECTIONS *(.initcall7.init) } __initcall_end = .; + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.initramfs) } + __initramfs_end = .; . = ALIGN(32); __per_cpu_start = .; .data.percpu : { *(.data.percpu) } diff --git a/init/Makefile b/init/Makefile index e19137c..c8ec0e6 100644 --- a/init/Makefile +++ b/init/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel. # -obj-y := main.o version.o do_mounts.o +obj-y := main.o version.o do_mounts.o initramfs.o # files to be removed upon make clean clean-files := ../include/linux/compile.h diff --git a/init/do_mounts.c b/init/do_mounts.c index 040e867..b00b776 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -748,9 +748,7 @@ void prepare_namespace(void) mount_initrd = 0; real_root_dev = ROOT_DEV; #endif - sys_mkdir("/dev", 0700); - sys_mkdir("/root", 0700); - sys_mknod("/dev/console", S_IFCHR|0600, MKDEV(TTYAUX_MAJOR, 1)); + #ifdef CONFIG_DEVFS_FS sys_mount("devfs", "/dev", "devfs", 0, NULL); do_devfs = 1; diff --git a/init/initramfs.c b/init/initramfs.c new file mode 100644 index 0000000..0c67f0c --- /dev/null +++ b/init/initramfs.c @@ -0,0 +1,462 @@ +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include +#include + +static void __init error(char *x) +{ + panic("populate_root: %s\n", x); +} + +static void __init *malloc(int size) +{ + return kmalloc(size, GFP_KERNEL); +} + +static void __init free(void *where) +{ + kfree(where); +} + +asmlinkage long sys_mkdir(char *name, int mode); +asmlinkage long sys_mknod(char *name, int mode, dev_t dev); +asmlinkage long sys_symlink(char *old, char *new); +asmlinkage long sys_link(char *old, char *new); +asmlinkage long sys_write(int fd, void *buf, ssize_t size); +asmlinkage long sys_chown(char *name, uid_t uid, gid_t gid); +asmlinkage long sys_lchown(char *name, uid_t uid, gid_t gid); +asmlinkage long sys_fchown(int fd, uid_t uid, gid_t gid); +asmlinkage long sys_chmod(char *name, mode_t mode); +asmlinkage long sys_fchmod(int fd, mode_t mode); + +/* link hash */ + +static struct hash { + int ino, minor, major; + struct hash *next; + char *name; +} *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; +} + +static char __init *find_link(int major, int minor, int ino, char *name) +{ + 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; + return (*p)->name; + } + q = (struct hash *)malloc(sizeof(struct hash)); + if (!q) + error("can't allocate link hash entry"); + q->ino = ino; + q->minor = minor; + q->major = major; + q->name = name; + 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; + free(q); + } + } +} + +/* cpio header parsing */ + +static __initdata unsigned long ino, major, minor, nlink; +static __initdata mode_t mode; +static __initdata unsigned long body_len, name_len; +static __initdata uid_t uid; +static __initdata gid_t gid; +static __initdata dev_t 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]; + body_len = parsed[6]; + major = parsed[7]; + minor = parsed[8]; + rdev = MKDEV(parsed[9], parsed[10]); + name_len = parsed[11]; +} + +/* FSM */ + +enum state { + Start, + Collect, + GotHeader, + SkipIt, + GotName, + CopyFile, + GotSymlink, + Reset +} state, next_state; + +char *victim; +unsigned count; +loff_t this_header, next_header; + +static inline void eat(unsigned n) +{ + victim += n; + this_header += n; + count -= n; +} + +#define N_ALIGN(len) ((((len) + 1) & ~3) + 2) + +static __initdata char *collected; +static __initdata int remains; +static __initdata char *collect; + +static void __init read_into(char *buf, unsigned size, enum state next) +{ + if (count >= size) { + 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) +{ + unsigned n = remains; + if (count < n) + n = count; + memcpy(collect, victim, n); + eat(n); + collect += n; + if (remains -= n) + return 1; + state = next_state; + return 0; +} + +static int __init do_header(void) +{ + parse_header(collected); + next_header = this_header + N_ALIGN(name_len) + body_len; + next_header = (next_header + 3) & ~3; + if (name_len <= 0 || name_len > PATH_MAX) + state = SkipIt; + else if (S_ISLNK(mode)) { + if (body_len > PATH_MAX) + state = SkipIt; + else { + collect = collected = symlink_buf; + remains = N_ALIGN(name_len) + body_len; + next_state = GotSymlink; + state = Collect; + } + } else if (body_len && !S_ISREG(mode)) + state = SkipIt; + else + read_into(name_buf, N_ALIGN(name_len), GotName); + return 0; +} + +static int __init do_skip(void) +{ + if (this_header + count <= next_header) { + eat(count); + return 1; + } else { + eat(next_header - this_header); + state = next_state; + return 0; + } +} + +static int __init do_reset(void) +{ + while(count && *victim == '\0') + eat(1); + if (count && (this_header & 3)) + error("broken padding"); + return 1; +} + +static int __init maybe_link(void) +{ + if (nlink >= 2) { + char *old = find_link(major, minor, ino, collected); + if (old) + return (sys_link(old, collected) < 0) ? -1 : 1; + } + return 0; +} + +static __initdata int wfd; + +static int __init do_name(void) +{ + state = SkipIt; + next_state = Start; + if (strcmp(collected, "TRAILER!!!") == 0) { + free_hash(); + next_state = Reset; + return 0; + } + printk(KERN_INFO "-> %s\n", collected); + if (S_ISREG(mode)) { + if (maybe_link() >= 0) { + wfd = sys_open(collected, O_WRONLY|O_CREAT, mode); + if (wfd >= 0) { + sys_fchown(wfd, uid, gid); + sys_fchmod(wfd, mode); + state = CopyFile; + } + } + } else if (S_ISDIR(mode)) { + sys_mkdir(collected, mode); + sys_chown(collected, uid, gid); + } 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); + } + } else + panic("populate_root: bogus mode: %o\n", mode); + return 0; +} + +static int __init do_copy(void) +{ + if (count >= body_len) { + sys_write(wfd, victim, body_len); + sys_close(wfd); + eat(body_len); + state = SkipIt; + return 0; + } else { + sys_write(wfd, victim, count); + body_len -= count; + eat(count); + return 1; + } +} + +static int __init do_symlink(void) +{ + collected[N_ALIGN(name_len) + body_len] = '\0'; + sys_symlink(collected + N_ALIGN(name_len), collected); + sys_lchown(collected, uid, gid); + state = SkipIt; + next_state = Start; + 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, +}; + +static int __init write_buffer(char *buf, unsigned len) +{ + count = len; + victim = buf; + + while (!actions[state]()) + ; + return len - count; +} + +static void __init flush_buffer(char *buf, unsigned len) +{ + int written; + while ((written = write_buffer(buf, len)) < len) { + char c = buf[written]; + if (c == '0') { + buf += written; + len -= written; + state = Start; + continue; + } else + error("junk in compressed archive"); + } +} + +/* + * gzip declarations + */ + +#define OF(args) args + +#ifndef memzero +#define memzero(s, n) memset ((s), 0, (n)) +#endif + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* window size--must be a power of two, and */ + /* at least 32K for zip's deflate method */ + +static uch *inbuf; +static uch *window; + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ +static long bytes_out; + +#define get_byte() (inptr < insize ? inbuf[inptr++] : -1) + +/* Diagnostic functions (stubbed out) */ +#define Assert(cond,msg) +#define Trace(x) +#define Tracev(x) +#define Tracevv(x) +#define Tracec(c,x) +#define Tracecv(c,x) + +#define STATIC static + +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +#include "../lib/inflate.c" + +static void __init gzip_mark(void **ptr) +{ +} + +static void __init gzip_release(void **ptr) +{ +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void __init flush_window(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + + flush_buffer(window, outcnt); + in = window; + for (n = 0; n < outcnt; n++) { + ch = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void __init unpack_to_rootfs(char *buf, unsigned len) +{ + int written; + header_buf = malloc(110); + symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1); + name_buf = malloc(N_ALIGN(PATH_MAX)); + window = malloc(WSIZE); + if (!window || !header_buf || !symlink_buf || !name_buf) + error("can't allocate buffers"); + state = Start; + this_header = 0; + while (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; + } else if (!*buf) { + buf++; + len--; + this_header++; + continue; + } + this_header = 0; + insize = len; + inbuf = buf; + inptr = 0; + outcnt = 0; /* bytes in output buffer */ + bytes_out = 0; + crc = (ulg)0xffffffffL; /* shift register contents */ + makecrc(); + if (gunzip()) + error("ungzip failed"); + if (state != Reset) + error("junk in gzipped archive"); + this_header = saved_offset + inptr; + buf += inptr; + len -= inptr; + } + free(window); + free(name_buf); + free(symlink_buf); + free(header_buf); +} + +extern unsigned long __initramfs_start, __initramfs_end; + +void __init populate_rootfs(void) +{ + unpack_to_rootfs((void *) &__initramfs_start, + &__initramfs_end - &__initramfs_start); +} diff --git a/init/main.c b/init/main.c index d75bf0ac..00f6afe 100644 --- a/init/main.c +++ b/init/main.c @@ -72,6 +72,7 @@ extern void pidhash_init(void); extern void pte_chain_init(void); extern void radix_tree_init(void); extern void free_initmem(void); +extern void populate_rootfs(void); #ifdef CONFIG_TC extern void tc_init(void); @@ -433,6 +434,7 @@ asmlinkage void __init start_kernel(void) vfs_caches_init(num_physpages); radix_tree_init(); signals_init(); + populate_rootfs(); #ifdef CONFIG_PROC_FS proc_root_init(); #endif diff --git a/usr/Makefile b/usr/Makefile new file mode 100644 index 0000000..d3c2c0b --- /dev/null +++ b/usr/Makefile @@ -0,0 +1,18 @@ + +include arch/$(ARCH)/Makefile + +obj-y := initramfs_data.o + +host-progs := gen_init_cpio + +clean-files := initramfs_data.cpio.gz + +$(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio.gz + $(OBJCOPY) $(ARCHBLOBLFLAGS) \ + --rename-section .data=.init.initramfs \ + $(obj)/initramfs_data.cpio.gz $(obj)/initramfs_data.o + $(STRIP) -s $(obj)/initramfs_data.o + +$(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio + ( cd $(obj) ; ./gen_init_cpio | gzip -9c > initramfs_data.cpio.gz ) + diff --git a/usr/gen_init_cpio.c b/usr/gen_init_cpio.c new file mode 100644 index 0000000..a6024fa --- /dev/null +++ b/usr/gen_init_cpio.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include + +static unsigned int offset; +static unsigned int ino = 721; + +static void push_rest(const char *name) +{ + unsigned int name_len = strlen(name) + 1; + unsigned int tmp_ofs; + + fputs(name, stdout); + putchar(0); + offset += name_len; + + tmp_ofs = name_len + 110; + while (tmp_ofs & 3) { + putchar(0); + offset++; + tmp_ofs++; + } +} + +static void push_hdr(const char *s) +{ + fputs(s, stdout); + offset += 110; +} + +static void cpio_trailer(void) +{ + char s[256]; + const char *name = "TRAILER!!!"; + + sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + "070701", /* magic */ + 0, /* ino */ + 0, /* mode */ + (long) 0, /* uid */ + (long) 0, /* gid */ + 1, /* nlink */ + (long) 0, /* mtime */ + 0, /* filesize */ + 0, /* major */ + 0, /* minor */ + 0, /* rmajor */ + 0, /* rminor */ + strlen(name) + 1, /* namesize */ + 0); /* chksum */ + push_hdr(s); + push_rest(name); + + while (offset % 512) { + putchar(0); + offset++; + } +} + +static void cpio_mkdir(const char *name, unsigned int mode, + uid_t uid, gid_t gid) +{ + char s[256]; + time_t mtime = time(NULL); + + sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + "070701", /* magic */ + ino++, /* ino */ + S_IFDIR | mode, /* mode */ + (long) uid, /* uid */ + (long) gid, /* gid */ + 2, /* nlink */ + (long) mtime, /* mtime */ + 0, /* filesize */ + 3, /* major */ + 1, /* minor */ + 0, /* rmajor */ + 0, /* rminor */ + strlen(name) + 1, /* namesize */ + 0); /* chksum */ + push_hdr(s); + push_rest(name); +} + +static void cpio_mknod(const char *name, unsigned int mode, + uid_t uid, gid_t gid, int dev_type, + unsigned int maj, unsigned int min) +{ + char s[256]; + time_t mtime = time(NULL); + + if (dev_type == 'b') + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX" + "%08X%08X%08X%08X%08X%08X%08X", + "070701", /* magic */ + ino++, /* ino */ + mode, /* mode */ + (long) uid, /* uid */ + (long) gid, /* gid */ + 1, /* nlink */ + (long) mtime, /* mtime */ + 0, /* filesize */ + 3, /* major */ + 1, /* minor */ + maj, /* rmajor */ + min, /* rminor */ + strlen(name) + 1, /* namesize */ + 0); /* chksum */ + push_hdr(s); + push_rest(name); +} + +int main (int argc, char *argv[]) +{ + cpio_mkdir("/dev", 0700, 0, 0); + cpio_mknod("/dev/console", 0600, 0, 0, 'c', 5, 1); + cpio_mkdir("/root", 0700, 0, 0); + cpio_trailer(); + + exit(0); + + /* silence compiler warnings */ + return 0; + (void) argc; + (void) argv; +} + -- 1.7.10.4