18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/init.h> 38c2ecf20Sopenharmony_ci#include <linux/fs.h> 48c2ecf20Sopenharmony_ci#include <linux/slab.h> 58c2ecf20Sopenharmony_ci#include <linux/types.h> 68c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/string.h> 98c2ecf20Sopenharmony_ci#include <linux/dirent.h> 108c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 118c2ecf20Sopenharmony_ci#include <linux/utime.h> 128c2ecf20Sopenharmony_ci#include <linux/file.h> 138c2ecf20Sopenharmony_ci#include <linux/memblock.h> 148c2ecf20Sopenharmony_ci#include <linux/namei.h> 158c2ecf20Sopenharmony_ci#include <linux/init_syscalls.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic ssize_t __init xwrite(struct file *file, const char *p, size_t count, 188c2ecf20Sopenharmony_ci loff_t *pos) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci ssize_t out = 0; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci /* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */ 238c2ecf20Sopenharmony_ci while (count) { 248c2ecf20Sopenharmony_ci ssize_t rv = kernel_write(file, p, count, pos); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (rv < 0) { 278c2ecf20Sopenharmony_ci if (rv == -EINTR || rv == -EAGAIN) 288c2ecf20Sopenharmony_ci continue; 298c2ecf20Sopenharmony_ci return out ? out : rv; 308c2ecf20Sopenharmony_ci } else if (rv == 0) 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci p += rv; 348c2ecf20Sopenharmony_ci out += rv; 358c2ecf20Sopenharmony_ci count -= rv; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return out; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic __initdata char *message; 428c2ecf20Sopenharmony_cistatic void __init error(char *x) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci if (!message) 458c2ecf20Sopenharmony_ci message = x; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* link hash */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define N_ALIGN(len) ((((len) + 1) & ~3) + 2) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic __initdata struct hash { 538c2ecf20Sopenharmony_ci int ino, minor, major; 548c2ecf20Sopenharmony_ci umode_t mode; 558c2ecf20Sopenharmony_ci struct hash *next; 568c2ecf20Sopenharmony_ci char name[N_ALIGN(PATH_MAX)]; 578c2ecf20Sopenharmony_ci} *head[32]; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline int hash(int major, int minor, int ino) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci unsigned long tmp = ino + minor + (major << 3); 628c2ecf20Sopenharmony_ci tmp += tmp >> 5; 638c2ecf20Sopenharmony_ci return tmp & 31; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic char __init *find_link(int major, int minor, int ino, 678c2ecf20Sopenharmony_ci umode_t mode, char *name) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct hash **p, *q; 708c2ecf20Sopenharmony_ci for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { 718c2ecf20Sopenharmony_ci if ((*p)->ino != ino) 728c2ecf20Sopenharmony_ci continue; 738c2ecf20Sopenharmony_ci if ((*p)->minor != minor) 748c2ecf20Sopenharmony_ci continue; 758c2ecf20Sopenharmony_ci if ((*p)->major != major) 768c2ecf20Sopenharmony_ci continue; 778c2ecf20Sopenharmony_ci if (((*p)->mode ^ mode) & S_IFMT) 788c2ecf20Sopenharmony_ci continue; 798c2ecf20Sopenharmony_ci return (*p)->name; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci q = kmalloc(sizeof(struct hash), GFP_KERNEL); 828c2ecf20Sopenharmony_ci if (!q) 838c2ecf20Sopenharmony_ci panic("can't allocate link hash entry"); 848c2ecf20Sopenharmony_ci q->major = major; 858c2ecf20Sopenharmony_ci q->minor = minor; 868c2ecf20Sopenharmony_ci q->ino = ino; 878c2ecf20Sopenharmony_ci q->mode = mode; 888c2ecf20Sopenharmony_ci strcpy(q->name, name); 898c2ecf20Sopenharmony_ci q->next = NULL; 908c2ecf20Sopenharmony_ci *p = q; 918c2ecf20Sopenharmony_ci return NULL; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void __init free_hash(void) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct hash **p, *q; 978c2ecf20Sopenharmony_ci for (p = head; p < head + 32; p++) { 988c2ecf20Sopenharmony_ci while (*p) { 998c2ecf20Sopenharmony_ci q = *p; 1008c2ecf20Sopenharmony_ci *p = q->next; 1018c2ecf20Sopenharmony_ci kfree(q); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic long __init do_utime(char *filename, time64_t mtime) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct timespec64 t[2]; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci t[0].tv_sec = mtime; 1118c2ecf20Sopenharmony_ci t[0].tv_nsec = 0; 1128c2ecf20Sopenharmony_ci t[1].tv_sec = mtime; 1138c2ecf20Sopenharmony_ci t[1].tv_nsec = 0; 1148c2ecf20Sopenharmony_ci return init_utimes(filename, t); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic __initdata LIST_HEAD(dir_list); 1188c2ecf20Sopenharmony_cistruct dir_entry { 1198c2ecf20Sopenharmony_ci struct list_head list; 1208c2ecf20Sopenharmony_ci char *name; 1218c2ecf20Sopenharmony_ci time64_t mtime; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void __init dir_add(const char *name, time64_t mtime) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL); 1278c2ecf20Sopenharmony_ci if (!de) 1288c2ecf20Sopenharmony_ci panic("can't allocate dir_entry buffer"); 1298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&de->list); 1308c2ecf20Sopenharmony_ci de->name = kstrdup(name, GFP_KERNEL); 1318c2ecf20Sopenharmony_ci de->mtime = mtime; 1328c2ecf20Sopenharmony_ci list_add(&de->list, &dir_list); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void __init dir_utime(void) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct dir_entry *de, *tmp; 1388c2ecf20Sopenharmony_ci list_for_each_entry_safe(de, tmp, &dir_list, list) { 1398c2ecf20Sopenharmony_ci list_del(&de->list); 1408c2ecf20Sopenharmony_ci do_utime(de->name, de->mtime); 1418c2ecf20Sopenharmony_ci kfree(de->name); 1428c2ecf20Sopenharmony_ci kfree(de); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic __initdata time64_t mtime; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* cpio header parsing */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic __initdata unsigned long ino, major, minor, nlink; 1518c2ecf20Sopenharmony_cistatic __initdata umode_t mode; 1528c2ecf20Sopenharmony_cistatic __initdata unsigned long body_len, name_len; 1538c2ecf20Sopenharmony_cistatic __initdata uid_t uid; 1548c2ecf20Sopenharmony_cistatic __initdata gid_t gid; 1558c2ecf20Sopenharmony_cistatic __initdata unsigned rdev; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void __init parse_header(char *s) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci unsigned long parsed[12]; 1608c2ecf20Sopenharmony_ci char buf[9]; 1618c2ecf20Sopenharmony_ci int i; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci buf[8] = '\0'; 1648c2ecf20Sopenharmony_ci for (i = 0, s += 6; i < 12; i++, s += 8) { 1658c2ecf20Sopenharmony_ci memcpy(buf, s, 8); 1668c2ecf20Sopenharmony_ci parsed[i] = simple_strtoul(buf, NULL, 16); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci ino = parsed[0]; 1698c2ecf20Sopenharmony_ci mode = parsed[1]; 1708c2ecf20Sopenharmony_ci uid = parsed[2]; 1718c2ecf20Sopenharmony_ci gid = parsed[3]; 1728c2ecf20Sopenharmony_ci nlink = parsed[4]; 1738c2ecf20Sopenharmony_ci mtime = parsed[5]; /* breaks in y2106 */ 1748c2ecf20Sopenharmony_ci body_len = parsed[6]; 1758c2ecf20Sopenharmony_ci major = parsed[7]; 1768c2ecf20Sopenharmony_ci minor = parsed[8]; 1778c2ecf20Sopenharmony_ci rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); 1788c2ecf20Sopenharmony_ci name_len = parsed[11]; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* FSM */ 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic __initdata enum state { 1848c2ecf20Sopenharmony_ci Start, 1858c2ecf20Sopenharmony_ci Collect, 1868c2ecf20Sopenharmony_ci GotHeader, 1878c2ecf20Sopenharmony_ci SkipIt, 1888c2ecf20Sopenharmony_ci GotName, 1898c2ecf20Sopenharmony_ci CopyFile, 1908c2ecf20Sopenharmony_ci GotSymlink, 1918c2ecf20Sopenharmony_ci Reset 1928c2ecf20Sopenharmony_ci} state, next_state; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic __initdata char *victim; 1958c2ecf20Sopenharmony_cistatic unsigned long byte_count __initdata; 1968c2ecf20Sopenharmony_cistatic __initdata loff_t this_header, next_header; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic inline void __init eat(unsigned n) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci victim += n; 2018c2ecf20Sopenharmony_ci this_header += n; 2028c2ecf20Sopenharmony_ci byte_count -= n; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic __initdata char *collected; 2068c2ecf20Sopenharmony_cistatic long remains __initdata; 2078c2ecf20Sopenharmony_cistatic __initdata char *collect; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void __init read_into(char *buf, unsigned size, enum state next) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci if (byte_count >= size) { 2128c2ecf20Sopenharmony_ci collected = victim; 2138c2ecf20Sopenharmony_ci eat(size); 2148c2ecf20Sopenharmony_ci state = next; 2158c2ecf20Sopenharmony_ci } else { 2168c2ecf20Sopenharmony_ci collect = collected = buf; 2178c2ecf20Sopenharmony_ci remains = size; 2188c2ecf20Sopenharmony_ci next_state = next; 2198c2ecf20Sopenharmony_ci state = Collect; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic __initdata char *header_buf, *symlink_buf, *name_buf; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int __init do_start(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci read_into(header_buf, 110, GotHeader); 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int __init do_collect(void) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci unsigned long n = remains; 2348c2ecf20Sopenharmony_ci if (byte_count < n) 2358c2ecf20Sopenharmony_ci n = byte_count; 2368c2ecf20Sopenharmony_ci memcpy(collect, victim, n); 2378c2ecf20Sopenharmony_ci eat(n); 2388c2ecf20Sopenharmony_ci collect += n; 2398c2ecf20Sopenharmony_ci if ((remains -= n) != 0) 2408c2ecf20Sopenharmony_ci return 1; 2418c2ecf20Sopenharmony_ci state = next_state; 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int __init do_header(void) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (memcmp(collected, "070707", 6)==0) { 2488c2ecf20Sopenharmony_ci error("incorrect cpio method used: use -H newc option"); 2498c2ecf20Sopenharmony_ci return 1; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci if (memcmp(collected, "070701", 6)) { 2528c2ecf20Sopenharmony_ci error("no cpio magic"); 2538c2ecf20Sopenharmony_ci return 1; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci parse_header(collected); 2568c2ecf20Sopenharmony_ci next_header = this_header + N_ALIGN(name_len) + body_len; 2578c2ecf20Sopenharmony_ci next_header = (next_header + 3) & ~3; 2588c2ecf20Sopenharmony_ci state = SkipIt; 2598c2ecf20Sopenharmony_ci if (name_len <= 0 || name_len > PATH_MAX) 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci if (S_ISLNK(mode)) { 2628c2ecf20Sopenharmony_ci if (body_len > PATH_MAX) 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci collect = collected = symlink_buf; 2658c2ecf20Sopenharmony_ci remains = N_ALIGN(name_len) + body_len; 2668c2ecf20Sopenharmony_ci next_state = GotSymlink; 2678c2ecf20Sopenharmony_ci state = Collect; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci if (S_ISREG(mode) || !body_len) 2718c2ecf20Sopenharmony_ci read_into(name_buf, N_ALIGN(name_len), GotName); 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int __init do_skip(void) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci if (this_header + byte_count < next_header) { 2788c2ecf20Sopenharmony_ci eat(byte_count); 2798c2ecf20Sopenharmony_ci return 1; 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci eat(next_header - this_header); 2828c2ecf20Sopenharmony_ci state = next_state; 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int __init do_reset(void) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci while (byte_count && *victim == '\0') 2908c2ecf20Sopenharmony_ci eat(1); 2918c2ecf20Sopenharmony_ci if (byte_count && (this_header & 3)) 2928c2ecf20Sopenharmony_ci error("broken padding"); 2938c2ecf20Sopenharmony_ci return 1; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void __init clean_path(char *path, umode_t fmode) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct kstat st; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!init_stat(path, &st, AT_SYMLINK_NOFOLLOW) && 3018c2ecf20Sopenharmony_ci (st.mode ^ fmode) & S_IFMT) { 3028c2ecf20Sopenharmony_ci if (S_ISDIR(st.mode)) 3038c2ecf20Sopenharmony_ci init_rmdir(path); 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci init_unlink(path); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int __init maybe_link(void) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci if (nlink >= 2) { 3128c2ecf20Sopenharmony_ci char *old = find_link(major, minor, ino, mode, collected); 3138c2ecf20Sopenharmony_ci if (old) { 3148c2ecf20Sopenharmony_ci clean_path(collected, 0); 3158c2ecf20Sopenharmony_ci return (init_link(old, collected) < 0) ? -1 : 1; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic __initdata struct file *wfile; 3228c2ecf20Sopenharmony_cistatic __initdata loff_t wfile_pos; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int __init do_name(void) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci state = SkipIt; 3278c2ecf20Sopenharmony_ci next_state = Reset; 3288c2ecf20Sopenharmony_ci if (strcmp(collected, "TRAILER!!!") == 0) { 3298c2ecf20Sopenharmony_ci free_hash(); 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci clean_path(collected, mode); 3338c2ecf20Sopenharmony_ci if (S_ISREG(mode)) { 3348c2ecf20Sopenharmony_ci int ml = maybe_link(); 3358c2ecf20Sopenharmony_ci if (ml >= 0) { 3368c2ecf20Sopenharmony_ci int openflags = O_WRONLY|O_CREAT; 3378c2ecf20Sopenharmony_ci if (ml != 1) 3388c2ecf20Sopenharmony_ci openflags |= O_TRUNC; 3398c2ecf20Sopenharmony_ci wfile = filp_open(collected, openflags, mode); 3408c2ecf20Sopenharmony_ci if (IS_ERR(wfile)) 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci wfile_pos = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci vfs_fchown(wfile, uid, gid); 3458c2ecf20Sopenharmony_ci vfs_fchmod(wfile, mode); 3468c2ecf20Sopenharmony_ci if (body_len) 3478c2ecf20Sopenharmony_ci vfs_truncate(&wfile->f_path, body_len); 3488c2ecf20Sopenharmony_ci state = CopyFile; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci } else if (S_ISDIR(mode)) { 3518c2ecf20Sopenharmony_ci init_mkdir(collected, mode); 3528c2ecf20Sopenharmony_ci init_chown(collected, uid, gid, 0); 3538c2ecf20Sopenharmony_ci init_chmod(collected, mode); 3548c2ecf20Sopenharmony_ci dir_add(collected, mtime); 3558c2ecf20Sopenharmony_ci } else if (S_ISBLK(mode) || S_ISCHR(mode) || 3568c2ecf20Sopenharmony_ci S_ISFIFO(mode) || S_ISSOCK(mode)) { 3578c2ecf20Sopenharmony_ci if (maybe_link() == 0) { 3588c2ecf20Sopenharmony_ci init_mknod(collected, mode, rdev); 3598c2ecf20Sopenharmony_ci init_chown(collected, uid, gid, 0); 3608c2ecf20Sopenharmony_ci init_chmod(collected, mode); 3618c2ecf20Sopenharmony_ci do_utime(collected, mtime); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int __init do_copy(void) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci if (byte_count >= body_len) { 3708c2ecf20Sopenharmony_ci struct timespec64 t[2] = { }; 3718c2ecf20Sopenharmony_ci if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len) 3728c2ecf20Sopenharmony_ci error("write error"); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci t[0].tv_sec = mtime; 3758c2ecf20Sopenharmony_ci t[1].tv_sec = mtime; 3768c2ecf20Sopenharmony_ci vfs_utimes(&wfile->f_path, t); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci fput(wfile); 3798c2ecf20Sopenharmony_ci eat(body_len); 3808c2ecf20Sopenharmony_ci state = SkipIt; 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci } else { 3838c2ecf20Sopenharmony_ci if (xwrite(wfile, victim, byte_count, &wfile_pos) != byte_count) 3848c2ecf20Sopenharmony_ci error("write error"); 3858c2ecf20Sopenharmony_ci body_len -= byte_count; 3868c2ecf20Sopenharmony_ci eat(byte_count); 3878c2ecf20Sopenharmony_ci return 1; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int __init do_symlink(void) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci collected[N_ALIGN(name_len) + body_len] = '\0'; 3948c2ecf20Sopenharmony_ci clean_path(collected, 0); 3958c2ecf20Sopenharmony_ci init_symlink(collected + N_ALIGN(name_len), collected); 3968c2ecf20Sopenharmony_ci init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW); 3978c2ecf20Sopenharmony_ci do_utime(collected, mtime); 3988c2ecf20Sopenharmony_ci state = SkipIt; 3998c2ecf20Sopenharmony_ci next_state = Reset; 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic __initdata int (*actions[])(void) = { 4048c2ecf20Sopenharmony_ci [Start] = do_start, 4058c2ecf20Sopenharmony_ci [Collect] = do_collect, 4068c2ecf20Sopenharmony_ci [GotHeader] = do_header, 4078c2ecf20Sopenharmony_ci [SkipIt] = do_skip, 4088c2ecf20Sopenharmony_ci [GotName] = do_name, 4098c2ecf20Sopenharmony_ci [CopyFile] = do_copy, 4108c2ecf20Sopenharmony_ci [GotSymlink] = do_symlink, 4118c2ecf20Sopenharmony_ci [Reset] = do_reset, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic long __init write_buffer(char *buf, unsigned long len) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci byte_count = len; 4178c2ecf20Sopenharmony_ci victim = buf; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci while (!actions[state]()) 4208c2ecf20Sopenharmony_ci ; 4218c2ecf20Sopenharmony_ci return len - byte_count; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic long __init flush_buffer(void *bufv, unsigned long len) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci char *buf = (char *) bufv; 4278c2ecf20Sopenharmony_ci long written; 4288c2ecf20Sopenharmony_ci long origLen = len; 4298c2ecf20Sopenharmony_ci if (message) 4308c2ecf20Sopenharmony_ci return -1; 4318c2ecf20Sopenharmony_ci while ((written = write_buffer(buf, len)) < len && !message) { 4328c2ecf20Sopenharmony_ci char c = buf[written]; 4338c2ecf20Sopenharmony_ci if (c == '0') { 4348c2ecf20Sopenharmony_ci buf += written; 4358c2ecf20Sopenharmony_ci len -= written; 4368c2ecf20Sopenharmony_ci state = Start; 4378c2ecf20Sopenharmony_ci } else if (c == 0) { 4388c2ecf20Sopenharmony_ci buf += written; 4398c2ecf20Sopenharmony_ci len -= written; 4408c2ecf20Sopenharmony_ci state = Reset; 4418c2ecf20Sopenharmony_ci } else 4428c2ecf20Sopenharmony_ci error("junk within compressed archive"); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci return origLen; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic unsigned long my_inptr; /* index of next byte to be processed in inbuf */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci#include <linux/decompress/generic.h> 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic char * __init unpack_to_rootfs(char *buf, unsigned long len) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci long written; 4548c2ecf20Sopenharmony_ci decompress_fn decompress; 4558c2ecf20Sopenharmony_ci const char *compress_name; 4568c2ecf20Sopenharmony_ci static __initdata char msg_buf[64]; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci header_buf = kmalloc(110, GFP_KERNEL); 4598c2ecf20Sopenharmony_ci symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL); 4608c2ecf20Sopenharmony_ci name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (!header_buf || !symlink_buf || !name_buf) 4638c2ecf20Sopenharmony_ci panic("can't allocate buffers"); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci state = Start; 4668c2ecf20Sopenharmony_ci this_header = 0; 4678c2ecf20Sopenharmony_ci message = NULL; 4688c2ecf20Sopenharmony_ci while (!message && len) { 4698c2ecf20Sopenharmony_ci loff_t saved_offset = this_header; 4708c2ecf20Sopenharmony_ci if (*buf == '0' && !(this_header & 3)) { 4718c2ecf20Sopenharmony_ci state = Start; 4728c2ecf20Sopenharmony_ci written = write_buffer(buf, len); 4738c2ecf20Sopenharmony_ci buf += written; 4748c2ecf20Sopenharmony_ci len -= written; 4758c2ecf20Sopenharmony_ci continue; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci if (!*buf) { 4788c2ecf20Sopenharmony_ci buf++; 4798c2ecf20Sopenharmony_ci len--; 4808c2ecf20Sopenharmony_ci this_header++; 4818c2ecf20Sopenharmony_ci continue; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci this_header = 0; 4848c2ecf20Sopenharmony_ci decompress = decompress_method(buf, len, &compress_name); 4858c2ecf20Sopenharmony_ci pr_debug("Detected %s compressed data\n", compress_name); 4868c2ecf20Sopenharmony_ci if (decompress) { 4878c2ecf20Sopenharmony_ci int res = decompress(buf, len, NULL, flush_buffer, NULL, 4888c2ecf20Sopenharmony_ci &my_inptr, error); 4898c2ecf20Sopenharmony_ci if (res) 4908c2ecf20Sopenharmony_ci error("decompressor failed"); 4918c2ecf20Sopenharmony_ci } else if (compress_name) { 4928c2ecf20Sopenharmony_ci if (!message) { 4938c2ecf20Sopenharmony_ci snprintf(msg_buf, sizeof msg_buf, 4948c2ecf20Sopenharmony_ci "compression method %s not configured", 4958c2ecf20Sopenharmony_ci compress_name); 4968c2ecf20Sopenharmony_ci message = msg_buf; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci } else 4998c2ecf20Sopenharmony_ci error("invalid magic at start of compressed archive"); 5008c2ecf20Sopenharmony_ci if (state != Reset) 5018c2ecf20Sopenharmony_ci error("junk at the end of compressed archive"); 5028c2ecf20Sopenharmony_ci this_header = saved_offset + my_inptr; 5038c2ecf20Sopenharmony_ci buf += my_inptr; 5048c2ecf20Sopenharmony_ci len -= my_inptr; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci dir_utime(); 5078c2ecf20Sopenharmony_ci kfree(name_buf); 5088c2ecf20Sopenharmony_ci kfree(symlink_buf); 5098c2ecf20Sopenharmony_ci kfree(header_buf); 5108c2ecf20Sopenharmony_ci return message; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int __initdata do_retain_initrd; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int __init retain_initrd_param(char *str) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci if (*str) 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci do_retain_initrd = 1; 5208c2ecf20Sopenharmony_ci return 1; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci__setup("retain_initrd", retain_initrd_param); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_KEEPINITRD 5258c2ecf20Sopenharmony_cistatic int __init keepinitrd_setup(char *__unused) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci do_retain_initrd = 1; 5288c2ecf20Sopenharmony_ci return 1; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci__setup("keepinitrd", keepinitrd_setup); 5318c2ecf20Sopenharmony_ci#endif 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciextern char __initramfs_start[]; 5348c2ecf20Sopenharmony_ciextern unsigned long __initramfs_size; 5358c2ecf20Sopenharmony_ci#include <linux/initrd.h> 5368c2ecf20Sopenharmony_ci#include <linux/kexec.h> 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_civoid __init reserve_initrd_mem(void) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci phys_addr_t start; 5418c2ecf20Sopenharmony_ci unsigned long size; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Ignore the virtul address computed during device tree parsing */ 5448c2ecf20Sopenharmony_ci initrd_start = initrd_end = 0; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (!phys_initrd_size) 5478c2ecf20Sopenharmony_ci return; 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * Round the memory region to page boundaries as per free_initrd_mem() 5508c2ecf20Sopenharmony_ci * This allows us to detect whether the pages overlapping the initrd 5518c2ecf20Sopenharmony_ci * are in use, but more importantly, reserves the entire set of pages 5528c2ecf20Sopenharmony_ci * as we don't want these pages allocated for other purposes. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci start = round_down(phys_initrd_start, PAGE_SIZE); 5558c2ecf20Sopenharmony_ci size = phys_initrd_size + (phys_initrd_start - start); 5568c2ecf20Sopenharmony_ci size = round_up(size, PAGE_SIZE); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!memblock_is_region_memory(start, size)) { 5598c2ecf20Sopenharmony_ci pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region", 5608c2ecf20Sopenharmony_ci (u64)start, size); 5618c2ecf20Sopenharmony_ci goto disable; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (memblock_is_region_reserved(start, size)) { 5658c2ecf20Sopenharmony_ci pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region\n", 5668c2ecf20Sopenharmony_ci (u64)start, size); 5678c2ecf20Sopenharmony_ci goto disable; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci memblock_reserve(start, size); 5718c2ecf20Sopenharmony_ci /* Now convert initrd to virtual addresses */ 5728c2ecf20Sopenharmony_ci initrd_start = (unsigned long)__va(phys_initrd_start); 5738c2ecf20Sopenharmony_ci initrd_end = initrd_start + phys_initrd_size; 5748c2ecf20Sopenharmony_ci initrd_below_start_ok = 1; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return; 5778c2ecf20Sopenharmony_cidisable: 5788c2ecf20Sopenharmony_ci pr_cont(" - disabling initrd\n"); 5798c2ecf20Sopenharmony_ci initrd_start = 0; 5808c2ecf20Sopenharmony_ci initrd_end = 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_civoid __weak __init free_initrd_mem(unsigned long start, unsigned long end) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_KEEP_MEMBLOCK 5868c2ecf20Sopenharmony_ci unsigned long aligned_start = ALIGN_DOWN(start, PAGE_SIZE); 5878c2ecf20Sopenharmony_ci unsigned long aligned_end = ALIGN(end, PAGE_SIZE); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci memblock_free(__pa(aligned_start), aligned_end - aligned_start); 5908c2ecf20Sopenharmony_ci#endif 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, 5938c2ecf20Sopenharmony_ci "initrd"); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 5978c2ecf20Sopenharmony_cistatic bool __init kexec_free_initrd(void) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci unsigned long crashk_start = (unsigned long)__va(crashk_res.start); 6008c2ecf20Sopenharmony_ci unsigned long crashk_end = (unsigned long)__va(crashk_res.end); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * If the initrd region is overlapped with crashkernel reserved region, 6048c2ecf20Sopenharmony_ci * free only memory that is not part of crashkernel region. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci if (initrd_start >= crashk_end || initrd_end <= crashk_start) 6078c2ecf20Sopenharmony_ci return false; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* 6108c2ecf20Sopenharmony_ci * Initialize initrd memory region since the kexec boot does not do. 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci memset((void *)initrd_start, 0, initrd_end - initrd_start); 6138c2ecf20Sopenharmony_ci if (initrd_start < crashk_start) 6148c2ecf20Sopenharmony_ci free_initrd_mem(initrd_start, crashk_start); 6158c2ecf20Sopenharmony_ci if (initrd_end > crashk_end) 6168c2ecf20Sopenharmony_ci free_initrd_mem(crashk_end, initrd_end); 6178c2ecf20Sopenharmony_ci return true; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci#else 6208c2ecf20Sopenharmony_cistatic inline bool kexec_free_initrd(void) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci return false; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci#endif /* CONFIG_KEXEC_CORE */ 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_RAM 6278c2ecf20Sopenharmony_cistatic void __init populate_initrd_image(char *err) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci ssize_t written; 6308c2ecf20Sopenharmony_ci struct file *file; 6318c2ecf20Sopenharmony_ci loff_t pos = 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci unpack_to_rootfs(__initramfs_start, __initramfs_size); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd\n", 6368c2ecf20Sopenharmony_ci err); 6378c2ecf20Sopenharmony_ci file = filp_open("/initrd.image", O_WRONLY | O_CREAT, 0700); 6388c2ecf20Sopenharmony_ci if (IS_ERR(file)) 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci written = xwrite(file, (char *)initrd_start, initrd_end - initrd_start, 6428c2ecf20Sopenharmony_ci &pos); 6438c2ecf20Sopenharmony_ci if (written != initrd_end - initrd_start) 6448c2ecf20Sopenharmony_ci pr_err("/initrd.image: incomplete write (%zd != %ld)\n", 6458c2ecf20Sopenharmony_ci written, initrd_end - initrd_start); 6468c2ecf20Sopenharmony_ci fput(file); 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci#endif /* CONFIG_BLK_DEV_RAM */ 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int __init populate_rootfs(void) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci /* Load the built in initramfs */ 6538c2ecf20Sopenharmony_ci char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); 6548c2ecf20Sopenharmony_ci if (err) 6558c2ecf20Sopenharmony_ci panic("%s", err); /* Failed to decompress INTERNAL initramfs */ 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE)) 6588c2ecf20Sopenharmony_ci goto done; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_BLK_DEV_RAM)) 6618c2ecf20Sopenharmony_ci printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n"); 6628c2ecf20Sopenharmony_ci else 6638c2ecf20Sopenharmony_ci printk(KERN_INFO "Unpacking initramfs...\n"); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); 6668c2ecf20Sopenharmony_ci if (err) { 6678c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_RAM 6688c2ecf20Sopenharmony_ci populate_initrd_image(err); 6698c2ecf20Sopenharmony_ci#else 6708c2ecf20Sopenharmony_ci printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err); 6718c2ecf20Sopenharmony_ci#endif 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cidone: 6758c2ecf20Sopenharmony_ci /* 6768c2ecf20Sopenharmony_ci * If the initrd region is overlapped with crashkernel reserved region, 6778c2ecf20Sopenharmony_ci * free only memory that is not part of crashkernel region. 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci if (!do_retain_initrd && initrd_start && !kexec_free_initrd()) 6808c2ecf20Sopenharmony_ci free_initrd_mem(initrd_start, initrd_end); 6818c2ecf20Sopenharmony_ci initrd_start = 0; 6828c2ecf20Sopenharmony_ci initrd_end = 0; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci flush_delayed_fput(); 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_cirootfs_initcall(populate_rootfs); 688