cregit-Linux how code gets into the kernel

Release 4.14 init/initramfs.c

Directory: init
// SPDX-License-Identifier: GPL-2.0
/*
 * Many of the syscalls used in this file expect some of the arguments
 * to be __user pointers not __kernel pointers.  To limit the sparse
 * noise, turn off sparse checking for this file.
 */
#ifdef __CHECKER__

#undef __CHECKER__
#warning "Sparse checking disabled for this file"
#endif

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/dirent.h>
#include <linux/syscalls.h>
#include <linux/utime.h>
#include <linux/file.h>


static ssize_t __init xwrite(int fd, const char *p, size_t count) { ssize_t out = 0; /* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */ while (count) { ssize_t rv = sys_write(fd, p, count); if (rv < 0) { if (rv == -EINTR || rv == -EAGAIN) continue; return out ? out : rv; } else if (rv == 0) break; p += rv; out += rv; count -= rv; } return out; }

Contributors

PersonTokensPropCommitsCommitProp
Yinghai Lu93100.00%1100.00%
Total93100.00%1100.00%

static __initdata char *message;
static void __init error(char *x) { if (!message) message = x; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik1365.00%150.00%
Andrew Morton735.00%150.00%
Total20100.00%2100.00%

/* link hash */ #define N_ALIGN(len) ((((len) + 1) & ~3) + 2) static __initdata struct hash { int ino, minor, major; umode_t mode; struct hash *next; char name[N_ALIGN(PATH_MAX)]; } *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; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik41100.00%1100.00%
Total41100.00%1100.00%


static char __init *find_link(int major, int minor, int ino, umode_t mode, 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; if (((*p)->mode ^ mode) & S_IFMT) continue; return (*p)->name; } q = kmalloc(sizeof(struct hash), GFP_KERNEL); if (!q) panic("can't allocate link hash entry"); q->major = major; q->minor = minor; q->ino = ino; q->mode = mode; strcpy(q->name, name); q->next = NULL; *p = q; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik15680.83%116.67%
H. Peter Anvin2814.51%116.67%
Mark Huang42.07%116.67%
Thomas Petazzoni31.55%116.67%
Andrew Morton10.52%116.67%
Al Viro10.52%116.67%
Total193100.00%6100.00%


static void __init free_hash(void) { struct hash **p, *q; for (p = head; p < head + 32; p++) { while (*p) { q = *p; *p = q->next; kfree(q); } } }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik5898.31%150.00%
Thomas Petazzoni11.69%150.00%
Total59100.00%2100.00%


static long __init do_utime(char *filename, time_t mtime) { struct timespec64 t[2]; t[0].tv_sec = mtime; t[0].tv_nsec = 0; t[1].tv_sec = mtime; t[1].tv_nsec = 0; return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW); }

Contributors

PersonTokensPropCommitsCommitProp
Nye Liu6898.55%150.00%
Deepa Dinamani11.45%150.00%
Total69100.00%2100.00%

static __initdata LIST_HEAD(dir_list); struct dir_entry { struct list_head list; char *name; time_t mtime; };
static void __init dir_add(const char *name, time_t mtime) { struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL); if (!de) panic("can't allocate dir_entry buffer"); INIT_LIST_HEAD(&de->list); de->name = kstrdup(name, GFP_KERNEL); de->mtime = mtime; list_add(&de->list, &dir_list); }

Contributors

PersonTokensPropCommitsCommitProp
Nye Liu77100.00%1100.00%
Total77100.00%1100.00%


static void __init dir_utime(void) { struct dir_entry *de, *tmp; list_for_each_entry_safe(de, tmp, &dir_list, list) { list_del(&de->list); do_utime(de->name, de->mtime); kfree(de->name); kfree(de); } }

Contributors

PersonTokensPropCommitsCommitProp
Nye Liu60100.00%1100.00%
Total60100.00%1100.00%

static __initdata time_t mtime; /* cpio header parsing */ static __initdata unsigned long ino, major, minor, nlink; static __initdata umode_t mode; static __initdata unsigned long body_len, name_len; static __initdata uid_t uid; static __initdata gid_t gid; static __initdata unsigned 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]; mtime = parsed[5]; body_len = parsed[6]; major = parsed[7]; minor = parsed[8]; rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); name_len = parsed[11]; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik15894.05%125.00%
Nye Liu74.17%125.00%
Al Viro31.79%250.00%
Total168100.00%4100.00%

/* FSM */ static __initdata enum state { Start, Collect, GotHeader, SkipIt, GotName, CopyFile, GotSymlink, Reset } state, next_state; static __initdata char *victim; static unsigned long byte_count __initdata; static __initdata loff_t this_header, next_header;
static inline void __init eat(unsigned n) { victim += n; this_header += n; byte_count -= n; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik2191.30%133.33%
Al Viro14.35%133.33%
Mark D Rustad14.35%133.33%
Total23100.00%3100.00%

static __initdata char *vcollected; static __initdata char *collected; static long remains __initdata; static __initdata char *collect;
static void __init read_into(char *buf, unsigned size, enum state next) { if (byte_count >= size) { collected = victim; eat(size); state = next; } else { collect = collected = buf; remains = size; next_state = next; state = Collect; } }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik5998.33%150.00%
Mark D Rustad11.67%150.00%
Total60100.00%2100.00%

static __initdata char *header_buf, *symlink_buf, *name_buf;
static int __init do_start(void) { read_into(header_buf, 110, GotHeader); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik21100.00%1100.00%
Total21100.00%1100.00%


static int __init do_collect(void) { unsigned long n = remains; if (byte_count < n) n = byte_count; memcpy(collect, victim, n); eat(n); collect += n; if ((remains -= n) != 0) return 1; state = next_state; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik5688.89%125.00%
Mika Kukkonen46.35%125.00%
Mark D Rustad23.17%125.00%
Yinghai Lu11.59%125.00%
Total63100.00%4100.00%


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; } parse_header(collected); next_header = this_header + N_ALIGN(name_len) + body_len; next_header = (next_header + 3) & ~3; state = SkipIt; if (name_len <= 0 || name_len > PATH_MAX) return 0; if (S_ISLNK(mode)) { if (body_len > PATH_MAX) return 0; collect = collected = symlink_buf; remains = N_ALIGN(name_len) + body_len; next_state = GotSymlink; state = Collect; return 0; } if (S_ISREG(mode) || !body_len) read_into(name_buf, N_ALIGN(name_len), GotName); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik10462.65%133.33%
Andrew Morton3923.49%133.33%
Arjan van de Ven2313.86%133.33%
Total166100.00%3100.00%


static int __init do_skip(void) { if (this_header + byte_count < next_header) { eat(byte_count); return 1; } else { eat(next_header - this_header); state = next_state; return 0; } }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik4193.18%133.33%
Mark D Rustad24.55%133.33%
Andrew Morton12.27%133.33%
Total44100.00%3100.00%


static int __init do_reset(void) { while (byte_count && *victim == '\0') eat(1); if (byte_count && (this_header & 3)) error("broken padding"); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik3995.12%150.00%
Mark D Rustad24.88%150.00%
Total41100.00%2100.00%


static int __init maybe_link(void) { if (nlink >= 2) { char *old = find_link(major, minor, ino, mode, collected); if (old) return (sys_link(old, collected) < 0) ? -1 : 1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik5696.55%150.00%
H. Peter Anvin23.45%150.00%
Total58100.00%2100.00%


static void __init clean_path(char *path, umode_t fmode) { struct kstat st; if (!vfs_lstat(path, &st) && (st.mode ^ fmode) & S_IFMT) { if (S_ISDIR(st.mode)) sys_rmdir(path); else sys_unlink(path); } }

Contributors

PersonTokensPropCommitsCommitProp
H. Peter Anvin5488.52%125.00%
Arnd Bergmann46.56%125.00%
Mark D Rustad23.28%125.00%
Al Viro11.64%125.00%
Total61100.00%4100.00%

static __initdata int wfd;
static int __init do_name(void) { state = SkipIt; next_state = Reset; if (strcmp(collected, "TRAILER!!!") == 0) { free_hash(); return 0; } clean_path(collected, mode); if (S_ISREG(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); if (body_len) sys_ftruncate(wfd, body_len); vcollected = kstrdup(collected, GFP_KERNEL); state = CopyFile; } } } else if (S_ISDIR(mode)) { sys_mkdir(collected, mode); sys_chown(collected, uid, gid); sys_chmod(collected, mode); dir_add(collected, mtime); } 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); sys_chmod(collected, mode); do_utime(collected, mtime); } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik16866.93%112.50%
H. Peter Anvin3212.75%112.50%
Nye Liu239.16%112.50%
Anton Blanchard145.58%112.50%
David Howells72.79%112.50%
Randy Robertson41.59%112.50%
Andrew Morton20.80%112.50%
Milton D. Miller II10.40%112.50%
Total251100.00%8100.00%


static int __init do_copy(void) { if (byte_count >= body_len) { if (xwrite(wfd, victim, body_len) != body_len) error("write error"); sys_close(wfd); do_utime(vcollected, mtime); kfree(vcollected); eat(body_len); state = SkipIt; return 0; } else { if (xwrite(wfd, victim, byte_count) != byte_count) error("write error"); body_len -= byte_count; eat(byte_count); return 1; } }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik6162.89%120.00%
David Engraf1717.53%120.00%
Nye Liu1212.37%120.00%
Mark D Rustad55.15%120.00%
Yinghai Lu22.06%120.00%
Total97100.00%5100.00%


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); do_utime(collected, mtime); state = SkipIt; next_state = Reset; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik5277.61%125.00%
Nye Liu710.45%125.00%
H. Peter Anvin710.45%125.00%
Milton D. Miller II11.49%125.00%
Total67100.00%4100.00%

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 long __init write_buffer(char *buf, unsigned long len) { byte_count = len; victim = buf; while (!actions[state]()) ; return len - byte_count; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik3690.00%133.33%
Mark D Rustad25.00%133.33%
Yinghai Lu25.00%133.33%
Total40100.00%3100.00%


static long __init flush_buffer(void *bufv, unsigned long len) { char *buf = (char *) bufv; long written; long origLen = len; if (message) return -1; while ((written = write_buffer(buf, len)) < len && !message) { char c = buf[written]; if (c == '0') { buf += written; len -= written; state = Start; } else if (c == 0) { buf += written; len -= written; state = Reset; } else error("junk in compressed archive"); } return origLen; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik6453.78%120.00%
Alain Knaff2319.33%120.00%
Milton D. Miller II2117.65%120.00%
Andrew Morton75.88%120.00%
Yinghai Lu43.36%120.00%
Total119100.00%5100.00%

static unsigned long my_inptr; /* index of next byte to be processed in inbuf */ #include <linux/decompress/generic.h>
static char * __init unpack_to_rootfs(char *buf, unsigned long len) { long written; decompress_fn decompress; const char *compress_name; static __initdata char msg_buf[64]; header_buf = kmalloc(110, GFP_KERNEL); symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL); name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); if (!header_buf || !symlink_buf || !name_buf) panic("can't allocate buffers"); state = Start; this_header = 0; message = NULL; while (!message && 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; } if (!*buf) { buf++; len--; this_header++; continue; } this_header = 0; decompress = decompress_method(buf, len, &compress_name); pr_debug("Detected %s compressed data\n", compress_name); if (decompress) { int res = decompress(buf, len, NULL, flush_buffer, NULL, &my_inptr, error); if (res) error("decompressor failed"); } else if (compress_name) { if (!message) { snprintf(msg_buf, sizeof msg_buf, "compression method %s not configured", compress_name); message = msg_buf; } } else error("junk in compressed archive"); if (state != Reset) error("junk in compressed archive"); this_header = saved_offset + my_inptr; buf += my_inptr; len -= my_inptr; } dir_utime(); kfree(name_buf); kfree(symlink_buf); kfree(header_buf); return message; }

Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik17154.98%18.33%
H. Peter Anvin5317.04%216.67%
Alain Knaff289.00%18.33%
Phillip Lougher185.79%216.67%
Andrew Morton134.18%18.33%
Thomas Petazzoni123.86%18.33%
Daniel M. Weeks72.25%18.33%
Yinghai Lu51.61%18.33%
Nye Liu30.96%18.33%
Adrian Bunk10.32%18.33%
Total311100.00%12100.00%

static int __initdata do_retain_initrd;
static int __init retain_initrd_param(char *str) { if (*str) return 0; do_retain_initrd = 1; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Neuling26100.00%1100.00%
Total26100.00%1100.00%

__setup("retain_initrd", retain_initrd_param); extern char __initramfs_start[]; extern unsigned long __initramfs_size; #include <linux/initrd.h> #include <linux/kexec.h>
static void __init free_initrd(void) { #ifdef CONFIG_KEXEC_CORE unsigned long crashk_start = (unsigned long)__va(crashk_res.start); unsigned long crashk_end = (unsigned long)__va(crashk_res.end); #endif if (do_retain_initrd) goto skip; #ifdef CONFIG_KEXEC_CORE /* * 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); skip: initrd_start = 0; initrd_end = 0; }

Contributors

PersonTokensPropCommitsCommitProp
Haren Myneni9069.77%125.00%
Jan Beulich2418.60%125.00%
Michael Neuling1310.08%125.00%
Dave Young21.55%125.00%
Total129100.00%4100.00%

#ifdef CONFIG_BLK_DEV_RAM #define BUF_SIZE 1024
static void __init clean_rootfs(void) { int fd; void *buf; struct linux_dirent64 *dirp; int num; fd = sys_open("/", O_RDONLY, 0); WARN_ON(fd < 0); if (fd < 0) return; buf = kzalloc(BUF_SIZE, GFP_KERNEL); WARN_ON(!buf); if (!buf) { sys_close(fd); return; } dirp = buf; num = sys_getdents64(fd, dirp, BUF_SIZE); while (num > 0) { while (num > 0) { struct kstat st; int ret; ret = vfs_lstat(dirp->d_name, &st); WARN_ON_ONCE(ret); if (!ret) { if (S_ISDIR(st.mode)) sys_rmdir(dirp->d_name); else sys_unlink(dirp->d_name); } num -= dirp->d_reclen; dirp = (void *)dirp + dirp->d_reclen; } dirp = buf; memset(buf, 0, BUF_SIZE); num = sys_getdents64(fd, dirp, BUF_SIZE); } sys_close(fd); kfree(buf); }

Contributors

PersonTokensPropCommitsCommitProp
David Shaohua Li20695.81%133.33%
H Hartley Sweeten62.79%133.33%
Arnd Bergmann31.40%133.33%
Total215100.00%3100.00%

#endif
static int __init populate_rootfs(void) { /* Load the built in initramfs */ char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); if (err) panic("%s", err); /* Failed to decompress INTERNAL initramfs */ /* If available load the bootloader supplied initrd */ if (initrd_start && !IS_ENABLED(CONFIG_INITRAMFS_FORCE)) { #ifdef CONFIG_BLK_DEV_RAM int fd; printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n"); err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); if (!err) { free_initrd(); goto done; } else { clean_rootfs(); unpack_to_rootfs(__initramfs_start, __initramfs_size); } printk(KERN_INFO "rootfs image is not initramfs (%s)" "; looks like an initrd\n", err); fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700); if (fd >= 0) { ssize_t written = xwrite(fd, (char *)initrd_start, initrd_end - initrd_start); if (written != initrd_end - initrd_start) pr_err("/initrd.image: incomplete write (%zd != %ld)\n", written, initrd_end - initrd_start); sys_close(fd); free_initrd(); } done: /* empty statement */; #else printk(KERN_INFO "Unpacking initramfs...\n"); err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); if (err) printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err); free_initrd(); #endif } flush_delayed_fput(); /* * Try loading default modules from initramfs. This gives * us a chance to load before device_initcalls. */ load_default_modules(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton9441.23%15.00%
Zdenek Pavlas3816.67%15.00%
Yinghai Lu2310.09%15.00%
Jeff Garzik135.70%15.00%
David Shaohua Li125.26%15.00%
Tejun Heo83.51%15.00%
Linus Torvalds73.07%315.00%
Daniel R Thompson62.63%15.00%
H. Peter Anvin41.75%15.00%
Jan Beulich41.75%15.00%
Stafford Horne41.75%15.00%
Lokesh Vutla31.32%15.00%
Éric Piel31.32%15.00%
Simon Kitching31.32%15.00%
Tetsuo Handa20.88%15.00%
Hendrik Brueckner20.88%15.00%
Al Viro10.44%15.00%
Jason Gunthorpe10.44%15.00%
Total228100.00%20100.00%

rootfs_initcall(populate_rootfs);

Overall Contributors

PersonTokensPropCommitsCommitProp
Jeff Garzik155449.40%23.33%
Nye Liu2949.35%11.67%
David Shaohua Li2257.15%11.67%
Andrew Morton1926.10%35.00%
H. Peter Anvin1835.82%46.67%
Yinghai Lu1354.29%23.33%
Haren Myneni932.96%11.67%
Alain Knaff521.65%11.67%
Michael Neuling511.62%11.67%
Zdenek Pavlas481.53%23.33%
Jan Beulich280.89%11.67%
Milton D. Miller II230.73%11.67%
Arjan van de Ven230.73%11.67%
Art Haas220.70%11.67%
H Hartley Sweeten180.57%23.33%
Phillip Lougher180.57%23.33%
Mark D Rustad180.57%11.67%
Mark Huang170.54%11.67%
David Engraf170.54%11.67%
Thomas Petazzoni160.51%11.67%
Anton Blanchard140.45%11.67%
Linus Torvalds120.38%35.00%
Al Viro100.32%58.33%
Tejun Heo80.25%11.67%
David Howells80.25%23.33%
Daniel M. Weeks70.22%11.67%
Arnd Bergmann70.22%11.67%
Hendrik Brueckner70.22%11.67%
Daniel R Thompson60.19%11.67%
Lokesh Vutla60.19%11.67%
Nikanth Karthikesan50.16%11.67%
Mika Kukkonen40.13%11.67%
Stafford Horne40.13%11.67%
Randy Robertson40.13%11.67%
Éric Piel30.10%11.67%
Simon Kitching30.10%11.67%
Christoph Hellwig30.10%11.67%
Tetsuo Handa20.06%11.67%
Dave Young20.06%11.67%
Adrian Bunk10.03%11.67%
Greg Kroah-Hartman10.03%11.67%
Jason Gunthorpe10.03%11.67%
Deepa Dinamani10.03%11.67%
Total3146100.00%60100.00%
Directory: init
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.