cregit-Linux how code gets into the kernel

Release 4.7 init/initramfs.c

Directory: init
/*
 * 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>


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 luyinghai 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 garzikjeff garzik1365.00%150.00%
andrew mortonandrew 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 garzikjeff 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 garzikjeff garzik15680.83%116.67%
h. peter anvinh. peter anvin2814.51%116.67%
mark huangmark huang42.07%116.67%
thomas petazzonithomas petazzoni31.55%116.67%
andrew mortonandrew morton10.52%116.67%
al viroal 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 garzikjeff garzik5898.31%150.00%
thomas petazzonithomas petazzoni11.69%150.00%
Total59100.00%2100.00%


static long __init do_utime(char *filename, time_t mtime) { struct timespec 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 liunye liu69100.00%1100.00%
Total69100.00%1100.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 liunye 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 liunye 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 garzikjeff garzik15894.05%125.00%
nye liunye liu74.17%125.00%
al viroal 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 garzikjeff garzik2191.30%133.33%
al viroal viro14.35%133.33%
mark d. rustadmark 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 garzikjeff garzik5998.33%150.00%
mark d. rustadmark 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 garzikjeff 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 garzikjeff garzik5688.89%125.00%
mika kukkonenmika kukkonen46.35%125.00%
mark d. rustadmark d. rustad23.17%125.00%
yinghai luyinghai 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 garzikjeff garzik10462.65%133.33%
andrew mortonandrew morton3923.49%133.33%
arjan van de venarjan 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 garzikjeff garzik4193.18%133.33%
mark d. rustadmark d. rustad24.55%133.33%
andrew mortonandrew 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 garzikjeff garzik3995.12%150.00%
mark d. rustadmark 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 garzikjeff garzik5696.55%150.00%
h. peter anvinh. peter anvin23.45%150.00%
Total58100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
h. peter anvinh. peter anvin5895.08%133.33%
mark d. rustadmark d. rustad23.28%133.33%
al viroal viro11.64%133.33%
Total61100.00%3100.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 garzikjeff garzik16866.93%112.50%
h. peter anvinh. peter anvin3212.75%112.50%
nye liunye liu239.16%112.50%
anton blanchardanton blanchard145.58%112.50%
david howellsdavid howells72.79%112.50%
randy robertsonrandy robertson41.59%112.50%
andrew mortonandrew morton20.80%112.50%
milton d. millermilton d. miller10.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 garzikjeff garzik6162.89%120.00%
david engrafdavid engraf1717.53%120.00%
nye liunye liu1212.37%120.00%
mark d. rustadmark d. rustad55.15%120.00%
yinghai luyinghai 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 garzikjeff garzik5277.61%125.00%
nye liunye liu710.45%125.00%
h. peter anvinh. peter anvin710.45%125.00%
milton d. millermilton d. miller11.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 garzikjeff garzik3690.00%133.33%
mark d. rustadmark d. rustad25.00%133.33%
yinghai luyinghai 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 garzikjeff garzik6453.78%120.00%
alain knaffalain knaff2319.33%120.00%
milton d. millermilton d. miller2117.65%120.00%
andrew mortonandrew morton75.88%120.00%
yinghai luyinghai 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 garzikjeff garzik17154.98%18.33%
h. peter anvinh. peter anvin5317.04%216.67%
alain knaffalain knaff289.00%18.33%
phillip lougherphillip lougher185.79%216.67%
andrew mortonandrew morton134.18%18.33%
thomas petazzonithomas petazzoni123.86%18.33%
daniel m. weeksdaniel m. weeks72.25%18.33%
yinghai luyinghai lu51.61%18.33%
nye liunye liu30.96%18.33%
adrian bunkadrian 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 neulingmichael 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 myneniharen myneni9069.77%125.00%
jan beulichjan beulich2418.60%125.00%
michael neulingmichael neuling1310.08%125.00%
dave youngdave 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 stat st; int ret; ret = sys_newlstat(dirp->d_name, &st); WARN_ON_ONCE