| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* ----------------------------------------------------------------------- * | 
 |  * | 
 |  *   Copyright 2012 Intel Corporation; author H. Peter Anvin | 
 |  * | 
 |  * ----------------------------------------------------------------------- */ | 
 |  | 
 | /* | 
 |  * earlycpio.c | 
 |  * | 
 |  * Find a specific cpio member; must precede any compressed content. | 
 |  * This is used to locate data items in the initramfs used by the | 
 |  * kernel itself during early boot (before the main initramfs is | 
 |  * decompressed.)  It is the responsibility of the initramfs creator | 
 |  * to ensure that these items are uncompressed at the head of the | 
 |  * blob.  Depending on the boot loader or package tool that may be a | 
 |  * separate file or part of the same file. | 
 |  */ | 
 |  | 
 | #include <linux/earlycpio.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/string.h> | 
 |  | 
 | enum cpio_fields { | 
 | 	C_MAGIC, | 
 | 	C_INO, | 
 | 	C_MODE, | 
 | 	C_UID, | 
 | 	C_GID, | 
 | 	C_NLINK, | 
 | 	C_MTIME, | 
 | 	C_FILESIZE, | 
 | 	C_MAJ, | 
 | 	C_MIN, | 
 | 	C_RMAJ, | 
 | 	C_RMIN, | 
 | 	C_NAMESIZE, | 
 | 	C_CHKSUM, | 
 | 	C_NFIELDS | 
 | }; | 
 |  | 
 | /** | 
 |  * find_cpio_data - Search for files in an uncompressed cpio | 
 |  * @path:       The directory to search for, including a slash at the end | 
 |  * @data:       Pointer to the cpio archive or a header inside | 
 |  * @len:        Remaining length of the cpio based on data pointer | 
 |  * @nextoff:    When a matching file is found, this is the offset from the | 
 |  *              beginning of the cpio to the beginning of the next file, not the | 
 |  *              matching file itself. It can be used to iterate through the cpio | 
 |  *              to find all files inside of a directory path. | 
 |  * | 
 |  * Return:      &struct cpio_data containing the address, length and | 
 |  *              filename (with the directory path cut off) of the found file. | 
 |  *              If you search for a filename and not for files in a directory, | 
 |  *              pass the absolute path of the filename in the cpio and make sure | 
 |  *              the match returned an empty filename string. | 
 |  */ | 
 |  | 
 | struct cpio_data find_cpio_data(const char *path, void *data, | 
 | 				size_t len,  long *nextoff) | 
 | { | 
 | 	const size_t cpio_header_len = 8*C_NFIELDS - 2; | 
 | 	struct cpio_data cd = { NULL, 0, "" }; | 
 | 	const char *p, *dptr, *nptr; | 
 | 	unsigned int ch[C_NFIELDS], *chp, v; | 
 | 	unsigned char c, x; | 
 | 	size_t mypathsize = strlen(path); | 
 | 	int i, j; | 
 |  | 
 | 	p = data; | 
 |  | 
 | 	while (len > cpio_header_len) { | 
 | 		if (!*p) { | 
 | 			/* All cpio headers need to be 4-byte aligned */ | 
 | 			p += 4; | 
 | 			len -= 4; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		j = 6;		/* The magic field is only 6 characters */ | 
 | 		chp = ch; | 
 | 		for (i = C_NFIELDS; i; i--) { | 
 | 			v = 0; | 
 | 			while (j--) { | 
 | 				v <<= 4; | 
 | 				c = *p++; | 
 |  | 
 | 				x = c - '0'; | 
 | 				if (x < 10) { | 
 | 					v += x; | 
 | 					continue; | 
 | 				} | 
 |  | 
 | 				x = (c | 0x20) - 'a'; | 
 | 				if (x < 6) { | 
 | 					v += x + 10; | 
 | 					continue; | 
 | 				} | 
 |  | 
 | 				goto quit; /* Invalid hexadecimal */ | 
 | 			} | 
 | 			*chp++ = v; | 
 | 			j = 8;	/* All other fields are 8 characters */ | 
 | 		} | 
 |  | 
 | 		if ((ch[C_MAGIC] - 0x070701) > 1) | 
 | 			goto quit; /* Invalid magic */ | 
 |  | 
 | 		len -= cpio_header_len; | 
 |  | 
 | 		dptr = PTR_ALIGN(p + ch[C_NAMESIZE], 4); | 
 | 		nptr = PTR_ALIGN(dptr + ch[C_FILESIZE], 4); | 
 |  | 
 | 		if (nptr > p + len || dptr < p || nptr < dptr) | 
 | 			goto quit; /* Buffer overrun */ | 
 |  | 
 | 		if ((ch[C_MODE] & 0170000) == 0100000 && | 
 | 		    ch[C_NAMESIZE] >= mypathsize && | 
 | 		    !memcmp(p, path, mypathsize)) { | 
 |  | 
 | 			if (nextoff) | 
 | 				*nextoff = (long)nptr - (long)data; | 
 |  | 
 | 			if (ch[C_NAMESIZE] - mypathsize >= MAX_CPIO_FILE_NAME) { | 
 | 				pr_warn( | 
 | 				"File %s exceeding MAX_CPIO_FILE_NAME [%d]\n", | 
 | 				p, MAX_CPIO_FILE_NAME); | 
 | 			} | 
 | 			strscpy(cd.name, p + mypathsize, MAX_CPIO_FILE_NAME); | 
 |  | 
 | 			cd.data = (void *)dptr; | 
 | 			cd.size = ch[C_FILESIZE]; | 
 | 			return cd; /* Found it! */ | 
 | 		} | 
 | 		len -= (nptr - p); | 
 | 		p = nptr; | 
 | 	} | 
 |  | 
 | quit: | 
 | 	return cd; | 
 | } |