18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- * 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2012 Intel Corporation; author H. Peter Anvin 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * ----------------------------------------------------------------------- */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * earlycpio.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Find a specific cpio member; must precede any compressed content. 128c2ecf20Sopenharmony_ci * This is used to locate data items in the initramfs used by the 138c2ecf20Sopenharmony_ci * kernel itself during early boot (before the main initramfs is 148c2ecf20Sopenharmony_ci * decompressed.) It is the responsibility of the initramfs creator 158c2ecf20Sopenharmony_ci * to ensure that these items are uncompressed at the head of the 168c2ecf20Sopenharmony_ci * blob. Depending on the boot loader or package tool that may be a 178c2ecf20Sopenharmony_ci * separate file or part of the same file. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/earlycpio.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/string.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum cpio_fields { 258c2ecf20Sopenharmony_ci C_MAGIC, 268c2ecf20Sopenharmony_ci C_INO, 278c2ecf20Sopenharmony_ci C_MODE, 288c2ecf20Sopenharmony_ci C_UID, 298c2ecf20Sopenharmony_ci C_GID, 308c2ecf20Sopenharmony_ci C_NLINK, 318c2ecf20Sopenharmony_ci C_MTIME, 328c2ecf20Sopenharmony_ci C_FILESIZE, 338c2ecf20Sopenharmony_ci C_MAJ, 348c2ecf20Sopenharmony_ci C_MIN, 358c2ecf20Sopenharmony_ci C_RMAJ, 368c2ecf20Sopenharmony_ci C_RMIN, 378c2ecf20Sopenharmony_ci C_NAMESIZE, 388c2ecf20Sopenharmony_ci C_CHKSUM, 398c2ecf20Sopenharmony_ci C_NFIELDS 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * cpio_data find_cpio_data - Search for files in an uncompressed cpio 448c2ecf20Sopenharmony_ci * @path: The directory to search for, including a slash at the end 458c2ecf20Sopenharmony_ci * @data: Pointer to the cpio archive or a header inside 468c2ecf20Sopenharmony_ci * @len: Remaining length of the cpio based on data pointer 478c2ecf20Sopenharmony_ci * @nextoff: When a matching file is found, this is the offset from the 488c2ecf20Sopenharmony_ci * beginning of the cpio to the beginning of the next file, not the 498c2ecf20Sopenharmony_ci * matching file itself. It can be used to iterate through the cpio 508c2ecf20Sopenharmony_ci * to find all files inside of a directory path. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * @return: struct cpio_data containing the address, length and 538c2ecf20Sopenharmony_ci * filename (with the directory path cut off) of the found file. 548c2ecf20Sopenharmony_ci * If you search for a filename and not for files in a directory, 558c2ecf20Sopenharmony_ci * pass the absolute path of the filename in the cpio and make sure 568c2ecf20Sopenharmony_ci * the match returned an empty filename string. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct cpio_data find_cpio_data(const char *path, void *data, 608c2ecf20Sopenharmony_ci size_t len, long *nextoff) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci const size_t cpio_header_len = 8*C_NFIELDS - 2; 638c2ecf20Sopenharmony_ci struct cpio_data cd = { NULL, 0, "" }; 648c2ecf20Sopenharmony_ci const char *p, *dptr, *nptr; 658c2ecf20Sopenharmony_ci unsigned int ch[C_NFIELDS], *chp, v; 668c2ecf20Sopenharmony_ci unsigned char c, x; 678c2ecf20Sopenharmony_ci size_t mypathsize = strlen(path); 688c2ecf20Sopenharmony_ci int i, j; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci p = data; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci while (len > cpio_header_len) { 738c2ecf20Sopenharmony_ci if (!*p) { 748c2ecf20Sopenharmony_ci /* All cpio headers need to be 4-byte aligned */ 758c2ecf20Sopenharmony_ci p += 4; 768c2ecf20Sopenharmony_ci len -= 4; 778c2ecf20Sopenharmony_ci continue; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci j = 6; /* The magic field is only 6 characters */ 818c2ecf20Sopenharmony_ci chp = ch; 828c2ecf20Sopenharmony_ci for (i = C_NFIELDS; i; i--) { 838c2ecf20Sopenharmony_ci v = 0; 848c2ecf20Sopenharmony_ci while (j--) { 858c2ecf20Sopenharmony_ci v <<= 4; 868c2ecf20Sopenharmony_ci c = *p++; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci x = c - '0'; 898c2ecf20Sopenharmony_ci if (x < 10) { 908c2ecf20Sopenharmony_ci v += x; 918c2ecf20Sopenharmony_ci continue; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci x = (c | 0x20) - 'a'; 958c2ecf20Sopenharmony_ci if (x < 6) { 968c2ecf20Sopenharmony_ci v += x + 10; 978c2ecf20Sopenharmony_ci continue; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci goto quit; /* Invalid hexadecimal */ 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci *chp++ = v; 1038c2ecf20Sopenharmony_ci j = 8; /* All other fields are 8 characters */ 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if ((ch[C_MAGIC] - 0x070701) > 1) 1078c2ecf20Sopenharmony_ci goto quit; /* Invalid magic */ 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci len -= cpio_header_len; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dptr = PTR_ALIGN(p + ch[C_NAMESIZE], 4); 1128c2ecf20Sopenharmony_ci nptr = PTR_ALIGN(dptr + ch[C_FILESIZE], 4); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (nptr > p + len || dptr < p || nptr < dptr) 1158c2ecf20Sopenharmony_ci goto quit; /* Buffer overrun */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if ((ch[C_MODE] & 0170000) == 0100000 && 1188c2ecf20Sopenharmony_ci ch[C_NAMESIZE] >= mypathsize && 1198c2ecf20Sopenharmony_ci !memcmp(p, path, mypathsize)) { 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (nextoff) 1228c2ecf20Sopenharmony_ci *nextoff = (long)nptr - (long)data; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (ch[C_NAMESIZE] - mypathsize >= MAX_CPIO_FILE_NAME) { 1258c2ecf20Sopenharmony_ci pr_warn( 1268c2ecf20Sopenharmony_ci "File %s exceeding MAX_CPIO_FILE_NAME [%d]\n", 1278c2ecf20Sopenharmony_ci p, MAX_CPIO_FILE_NAME); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci strlcpy(cd.name, p + mypathsize, MAX_CPIO_FILE_NAME); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci cd.data = (void *)dptr; 1328c2ecf20Sopenharmony_ci cd.size = ch[C_FILESIZE]; 1338c2ecf20Sopenharmony_ci return cd; /* Found it! */ 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci len -= (nptr - p); 1368c2ecf20Sopenharmony_ci p = nptr; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciquit: 1408c2ecf20Sopenharmony_ci return cd; 1418c2ecf20Sopenharmony_ci} 142