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