1 // SPDX-License-Identifier: GPL-2.0-only 2 /* ----------------------------------------------------------------------- * 3 * 4 * Copyright 2012 Intel Corporation; author H. Peter Anvin 5 * 6 * ----------------------------------------------------------------------- */ 7 8 /* 9 * earlycpio.c 10 * 11 * Find a specific cpio member; must precede any compressed content. 12 * This is used to locate data items in the initramfs used by the 13 * kernel itself during early boot (before the main initramfs is 14 * decompressed.) It is the responsibility of the initramfs creator 15 * to ensure that these items are uncompressed at the head of the 16 * blob. Depending on the boot loader or package tool that may be a 17 * separate file or part of the same file. 18 */ 19 20 #include <linux/earlycpio.h> 21 #include <linux/kernel.h> 22 #include <linux/string.h> 23 24 enum cpio_fields { 25 C_MAGIC, 26 C_INO, 27 C_MODE, 28 C_UID, 29 C_GID, 30 C_NLINK, 31 C_MTIME, 32 C_FILESIZE, 33 C_MAJ, 34 C_MIN, 35 C_RMAJ, 36 C_RMIN, 37 C_NAMESIZE, 38 C_CHKSUM, 39 C_NFIELDS 40 }; 41 42 /** 43 * cpio_data find_cpio_data - Search for files in an uncompressed cpio 44 * @path: The directory to search for, including a slash at the end 45 * @data: Pointer to the the cpio archive or a header inside 46 * @len: Remaining length of the cpio based on data pointer 47 * @nextoff: When a matching file is found, this is the offset from the 48 * beginning of the cpio to the beginning of the next file, not the 49 * matching file itself. It can be used to iterate through the cpio 50 * to find all files inside of a directory path. 51 * 52 * @return: struct cpio_data containing the address, length and 53 * filename (with the directory path cut off) of the found file. 54 * If you search for a filename and not for files in a directory, 55 * pass the absolute path of the filename in the cpio and make sure 56 * the match returned an empty filename string. 57 */ 58 59 struct cpio_data find_cpio_data(const char *path, void *data, 60 size_t len, long *nextoff) 61 { 62 const size_t cpio_header_len = 8*C_NFIELDS - 2; 63 struct cpio_data cd = { NULL, 0, "" }; 64 const char *p, *dptr, *nptr; 65 unsigned int ch[C_NFIELDS], *chp, v; 66 unsigned char c, x; 67 size_t mypathsize = strlen(path); 68 int i, j; 69 70 p = data; 71 72 while (len > cpio_header_len) { 73 if (!*p) { 74 /* All cpio headers need to be 4-byte aligned */ 75 p += 4; 76 len -= 4; 77 continue; 78 } 79 80 j = 6; /* The magic field is only 6 characters */ 81 chp = ch; 82 for (i = C_NFIELDS; i; i--) { 83 v = 0; 84 while (j--) { 85 v <<= 4; 86 c = *p++; 87 88 x = c - '0'; 89 if (x < 10) { 90 v += x; 91 continue; 92 } 93 94 x = (c | 0x20) - 'a'; 95 if (x < 6) { 96 v += x + 10; 97 continue; 98 } 99 100 goto quit; /* Invalid hexadecimal */ 101 } 102 *chp++ = v; 103 j = 8; /* All other fields are 8 characters */ 104 } 105 106 if ((ch[C_MAGIC] - 0x070701) > 1) 107 goto quit; /* Invalid magic */ 108 109 len -= cpio_header_len; 110 111 dptr = PTR_ALIGN(p + ch[C_NAMESIZE], 4); 112 nptr = PTR_ALIGN(dptr + ch[C_FILESIZE], 4); 113 114 if (nptr > p + len || dptr < p || nptr < dptr) 115 goto quit; /* Buffer overrun */ 116 117 if ((ch[C_MODE] & 0170000) == 0100000 && 118 ch[C_NAMESIZE] >= mypathsize && 119 !memcmp(p, path, mypathsize)) { 120 121 if (nextoff) 122 *nextoff = (long)nptr - (long)data; 123 124 if (ch[C_NAMESIZE] - mypathsize >= MAX_CPIO_FILE_NAME) { 125 pr_warn( 126 "File %s exceeding MAX_CPIO_FILE_NAME [%d]\n", 127 p, MAX_CPIO_FILE_NAME); 128 } 129 strlcpy(cd.name, p + mypathsize, MAX_CPIO_FILE_NAME); 130 131 cd.data = (void *)dptr; 132 cd.size = ch[C_FILESIZE]; 133 return cd; /* Found it! */ 134 } 135 len -= (nptr - p); 136 p = nptr; 137 } 138 139 quit: 140 return cd; 141 } 142