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