| 
									
										
										
										
											2012-10-01 00:23:52 +02:00
										 |  |  | /* ----------------------------------------------------------------------- *
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   Copyright 2012 Intel Corporation; author H. Peter Anvin | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This file is part of the Linux kernel, and is made available | 
					
						
							|  |  |  |  *   under the terms of the GNU General Public License version 2, as | 
					
						
							|  |  |  |  *   published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This program is distributed in the hope it will be useful, but | 
					
						
							|  |  |  |  *   WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  *   General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * ----------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cpio_data find_cpio_data - Search for files in an uncompressed cpio | 
					
						
							| 
									
										
										
										
											2013-08-14 17:37:07 +08:00
										 |  |  |  * @path:       The directory to search for, including a slash at the end | 
					
						
							|  |  |  |  * @data:       Pointer to the 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. | 
					
						
							| 
									
										
										
										
											2012-10-01 00:23:52 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-08-14 17:37:07 +08:00
										 |  |  |  * @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. | 
					
						
							| 
									
										
										
										
											2012-10-01 00:23:52 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-19 14:53:51 -04:00
										 |  |  | struct cpio_data find_cpio_data(const char *path, void *data, | 
					
						
							| 
									
										
										
										
											2013-08-14 17:37:07 +08:00
										 |  |  | 				size_t len,  long *nextoff) | 
					
						
							| 
									
										
										
										
											2012-10-01 00:23:52 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	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)) { | 
					
						
							| 
									
										
										
										
											2013-08-14 17:37:07 +08:00
										 |  |  | 			*nextoff = (long)nptr - (long)data; | 
					
						
							| 
									
										
										
										
											2012-10-01 00:23:52 +02:00
										 |  |  | 			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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			strlcpy(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; | 
					
						
							|  |  |  | } |