1*45bf502fSJosef 'Jeff' Sipek /*
2*45bf502fSJosef 'Jeff' Sipek  * Copyright 2011-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
3*45bf502fSJosef 'Jeff' Sipek  *
4*45bf502fSJosef 'Jeff' Sipek  * Permission to use, copy, modify, and/or distribute this software for any
5*45bf502fSJosef 'Jeff' Sipek  * purpose with or without fee is hereby granted, provided that the above
6*45bf502fSJosef 'Jeff' Sipek  * copyright notice and this permission notice appear in all copies.
7*45bf502fSJosef 'Jeff' Sipek  *
8*45bf502fSJosef 'Jeff' Sipek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*45bf502fSJosef 'Jeff' Sipek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*45bf502fSJosef 'Jeff' Sipek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*45bf502fSJosef 'Jeff' Sipek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*45bf502fSJosef 'Jeff' Sipek  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*45bf502fSJosef 'Jeff' Sipek  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*45bf502fSJosef 'Jeff' Sipek  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*45bf502fSJosef 'Jeff' Sipek  */
16*45bf502fSJosef 'Jeff' Sipek 
17*45bf502fSJosef 'Jeff' Sipek #include <sys/types.h>
18*45bf502fSJosef 'Jeff' Sipek #include <sys/stdbool.h>
19*45bf502fSJosef 'Jeff' Sipek #include <sys/sysmacros.h>
20*45bf502fSJosef 'Jeff' Sipek #include <sys/bootvfs.h>
21*45bf502fSJosef 'Jeff' Sipek #include <sys/filep.h>
22*45bf502fSJosef 'Jeff' Sipek #include <sys/sunddi.h>
23*45bf502fSJosef 'Jeff' Sipek #include <sys/ccompile.h>
24*45bf502fSJosef 'Jeff' Sipek #include <sys/queue.h>
25*45bf502fSJosef 'Jeff' Sipek 
26*45bf502fSJosef 'Jeff' Sipek /*
27*45bf502fSJosef 'Jeff' Sipek  * A cpio archive is just a sequence of files, each consisting of a header
28*45bf502fSJosef 'Jeff' Sipek  * (struct cpio_hdr) and the file contents.
29*45bf502fSJosef 'Jeff' Sipek  */
30*45bf502fSJosef 'Jeff' Sipek 
31*45bf502fSJosef 'Jeff' Sipek struct cpio_hdr {
32*45bf502fSJosef 'Jeff' Sipek 	uint8_t		magic[6];
33*45bf502fSJosef 'Jeff' Sipek 	uint8_t		dev[6];
34*45bf502fSJosef 'Jeff' Sipek 	uint8_t		ino[6];
35*45bf502fSJosef 'Jeff' Sipek 	uint8_t		mode[6];
36*45bf502fSJosef 'Jeff' Sipek 	uint8_t		uid[6];
37*45bf502fSJosef 'Jeff' Sipek 	uint8_t		gid[6];
38*45bf502fSJosef 'Jeff' Sipek 	uint8_t		nlink[6];
39*45bf502fSJosef 'Jeff' Sipek 	uint8_t		rdev[6];
40*45bf502fSJosef 'Jeff' Sipek 	uint8_t		mtime[11];
41*45bf502fSJosef 'Jeff' Sipek 	uint8_t		namesize[6];
42*45bf502fSJosef 'Jeff' Sipek 	uint8_t		filesize[11];
43*45bf502fSJosef 'Jeff' Sipek 	char		data[];
44*45bf502fSJosef 'Jeff' Sipek };
45*45bf502fSJosef 'Jeff' Sipek 
46*45bf502fSJosef 'Jeff' Sipek /*
47*45bf502fSJosef 'Jeff' Sipek  * This structure represents an open file.  The list of all open files is
48*45bf502fSJosef 'Jeff' Sipek  * rooted in the open_files global.
49*45bf502fSJosef 'Jeff' Sipek  */
50*45bf502fSJosef 'Jeff' Sipek struct cpio_file {
51*45bf502fSJosef 'Jeff' Sipek 	/* pointers into the archive */
52*45bf502fSJosef 'Jeff' Sipek 	const struct cpio_hdr *hdr;
53*45bf502fSJosef 'Jeff' Sipek 	const char *path;		/* pointer into the archive */
54*45bf502fSJosef 'Jeff' Sipek 	const void *data;		/* pointer into the archive */
55*45bf502fSJosef 'Jeff' Sipek 
56*45bf502fSJosef 'Jeff' Sipek 	int fd;
57*45bf502fSJosef 'Jeff' Sipek 	off_t off;
58*45bf502fSJosef 'Jeff' Sipek 	struct bootstat stat;
59*45bf502fSJosef 'Jeff' Sipek 
60*45bf502fSJosef 'Jeff' Sipek 	SLIST_ENTRY(cpio_file) next;
61*45bf502fSJosef 'Jeff' Sipek };
62*45bf502fSJosef 'Jeff' Sipek 
63*45bf502fSJosef 'Jeff' Sipek extern void *bkmem_alloc(size_t);
64*45bf502fSJosef 'Jeff' Sipek extern void bkmem_free(void *, size_t);
65*45bf502fSJosef 'Jeff' Sipek 
66*45bf502fSJosef 'Jeff' Sipek static void cpio_closeall(int flag);
67*45bf502fSJosef 'Jeff' Sipek 
68*45bf502fSJosef 'Jeff' Sipek static bool mounted;
69*45bf502fSJosef 'Jeff' Sipek static SLIST_HEAD(cpio_file_list, cpio_file)
70*45bf502fSJosef 'Jeff' Sipek     open_files = SLIST_HEAD_INITIALIZER(open_files);
71*45bf502fSJosef 'Jeff' Sipek 
72*45bf502fSJosef 'Jeff' Sipek static int
cpio_strcmp(const char * a,const char * b)73*45bf502fSJosef 'Jeff' Sipek cpio_strcmp(const char *a, const char *b)
74*45bf502fSJosef 'Jeff' Sipek {
75*45bf502fSJosef 'Jeff' Sipek 	while ((*a != '\0') && (*b != '\0') && (*a == *b)) {
76*45bf502fSJosef 'Jeff' Sipek 		a++;
77*45bf502fSJosef 'Jeff' Sipek 		b++;
78*45bf502fSJosef 'Jeff' Sipek 	}
79*45bf502fSJosef 'Jeff' Sipek 
80*45bf502fSJosef 'Jeff' Sipek 	if (*a == *b)
81*45bf502fSJosef 'Jeff' Sipek 		return (0);
82*45bf502fSJosef 'Jeff' Sipek 	if (*a < *b)
83*45bf502fSJosef 'Jeff' Sipek 		return (-1);
84*45bf502fSJosef 'Jeff' Sipek 	return (1);
85*45bf502fSJosef 'Jeff' Sipek }
86*45bf502fSJosef 'Jeff' Sipek 
87*45bf502fSJosef 'Jeff' Sipek /*
88*45bf502fSJosef 'Jeff' Sipek  * Returns the parsed number on success, or UINT64_MAX on error.  This is
89*45bf502fSJosef 'Jeff' Sipek  * ok because we will never deal with numbers that large in a cpio archive.
90*45bf502fSJosef 'Jeff' Sipek  */
91*45bf502fSJosef 'Jeff' Sipek static uint64_t
__get_uint64(const uint8_t * str,size_t len,const size_t output_size)92*45bf502fSJosef 'Jeff' Sipek __get_uint64(const uint8_t *str, size_t len, const size_t output_size)
93*45bf502fSJosef 'Jeff' Sipek {
94*45bf502fSJosef 'Jeff' Sipek 	uint64_t v;
95*45bf502fSJosef 'Jeff' Sipek 
96*45bf502fSJosef 'Jeff' Sipek 	/* check that we can represent every number */
97*45bf502fSJosef 'Jeff' Sipek 	if (len * 3 > output_size)
98*45bf502fSJosef 'Jeff' Sipek 		return (UINT64_MAX);
99*45bf502fSJosef 'Jeff' Sipek 
100*45bf502fSJosef 'Jeff' Sipek 	for (v = 0; len > 0; len--, str++) {
101*45bf502fSJosef 'Jeff' Sipek 		const uint8_t c = *str;
102*45bf502fSJosef 'Jeff' Sipek 
103*45bf502fSJosef 'Jeff' Sipek 		if ((c < '0') || (c > '7'))
104*45bf502fSJosef 'Jeff' Sipek 			return (UINT64_MAX);
105*45bf502fSJosef 'Jeff' Sipek 
106*45bf502fSJosef 'Jeff' Sipek 		v = (v * 8) + (c - '0');
107*45bf502fSJosef 'Jeff' Sipek 	}
108*45bf502fSJosef 'Jeff' Sipek 
109*45bf502fSJosef 'Jeff' Sipek 	return (v);
110*45bf502fSJosef 'Jeff' Sipek }
111*45bf502fSJosef 'Jeff' Sipek 
112*45bf502fSJosef 'Jeff' Sipek static bool
get_uint64(const uint8_t * str,size_t len,uint64_t * out)113*45bf502fSJosef 'Jeff' Sipek get_uint64(const uint8_t *str, size_t len, uint64_t *out)
114*45bf502fSJosef 'Jeff' Sipek {
115*45bf502fSJosef 'Jeff' Sipek 	*out = __get_uint64(str, len, NBBY * sizeof (*out));
116*45bf502fSJosef 'Jeff' Sipek 	return (*out != UINT64_MAX);
117*45bf502fSJosef 'Jeff' Sipek }
118*45bf502fSJosef 'Jeff' Sipek 
119*45bf502fSJosef 'Jeff' Sipek static bool
get_int64(const uint8_t * str,size_t len,int64_t * out)120*45bf502fSJosef 'Jeff' Sipek get_int64(const uint8_t *str, size_t len, int64_t *out)
121*45bf502fSJosef 'Jeff' Sipek {
122*45bf502fSJosef 'Jeff' Sipek 	uint64_t tmp;
123*45bf502fSJosef 'Jeff' Sipek 
124*45bf502fSJosef 'Jeff' Sipek 	tmp = __get_uint64(str, len, NBBY * sizeof (*out) - 1);
125*45bf502fSJosef 'Jeff' Sipek 
126*45bf502fSJosef 'Jeff' Sipek 	*out = tmp;
127*45bf502fSJosef 'Jeff' Sipek 
128*45bf502fSJosef 'Jeff' Sipek 	return (tmp != UINT64_MAX);
129*45bf502fSJosef 'Jeff' Sipek }
130*45bf502fSJosef 'Jeff' Sipek 
131*45bf502fSJosef 'Jeff' Sipek static bool
get_uint32(const uint8_t * str,size_t len,uint32_t * out)132*45bf502fSJosef 'Jeff' Sipek get_uint32(const uint8_t *str, size_t len, uint32_t *out)
133*45bf502fSJosef 'Jeff' Sipek {
134*45bf502fSJosef 'Jeff' Sipek 	uint64_t tmp;
135*45bf502fSJosef 'Jeff' Sipek 
136*45bf502fSJosef 'Jeff' Sipek 	tmp = __get_uint64(str, len, NBBY * sizeof (*out));
137*45bf502fSJosef 'Jeff' Sipek 
138*45bf502fSJosef 'Jeff' Sipek 	*out = tmp;
139*45bf502fSJosef 'Jeff' Sipek 
140*45bf502fSJosef 'Jeff' Sipek 	return (tmp != UINT64_MAX);
141*45bf502fSJosef 'Jeff' Sipek }
142*45bf502fSJosef 'Jeff' Sipek 
143*45bf502fSJosef 'Jeff' Sipek static bool
get_int32(const uint8_t * str,size_t len,int32_t * out)144*45bf502fSJosef 'Jeff' Sipek get_int32(const uint8_t *str, size_t len, int32_t *out)
145*45bf502fSJosef 'Jeff' Sipek {
146*45bf502fSJosef 'Jeff' Sipek 	uint64_t tmp;
147*45bf502fSJosef 'Jeff' Sipek 
148*45bf502fSJosef 'Jeff' Sipek 	tmp = __get_uint64(str, len, NBBY * sizeof (*out) - 1);
149*45bf502fSJosef 'Jeff' Sipek 
150*45bf502fSJosef 'Jeff' Sipek 	*out = tmp;
151*45bf502fSJosef 'Jeff' Sipek 
152*45bf502fSJosef 'Jeff' Sipek 	return (tmp != UINT64_MAX);
153*45bf502fSJosef 'Jeff' Sipek }
154*45bf502fSJosef 'Jeff' Sipek 
155*45bf502fSJosef 'Jeff' Sipek static void
add_open_file(struct cpio_file * file)156*45bf502fSJosef 'Jeff' Sipek add_open_file(struct cpio_file *file)
157*45bf502fSJosef 'Jeff' Sipek {
158*45bf502fSJosef 'Jeff' Sipek 	SLIST_INSERT_HEAD(&open_files, file, next);
159*45bf502fSJosef 'Jeff' Sipek }
160*45bf502fSJosef 'Jeff' Sipek 
161*45bf502fSJosef 'Jeff' Sipek static void
remove_open_file(struct cpio_file * file)162*45bf502fSJosef 'Jeff' Sipek remove_open_file(struct cpio_file *file)
163*45bf502fSJosef 'Jeff' Sipek {
164*45bf502fSJosef 'Jeff' Sipek 	SLIST_REMOVE(&open_files, file, cpio_file, next);
165*45bf502fSJosef 'Jeff' Sipek }
166*45bf502fSJosef 'Jeff' Sipek 
167*45bf502fSJosef 'Jeff' Sipek static struct cpio_file *
find_open_file(int fd)168*45bf502fSJosef 'Jeff' Sipek find_open_file(int fd)
169*45bf502fSJosef 'Jeff' Sipek {
170*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file *file;
171*45bf502fSJosef 'Jeff' Sipek 
172*45bf502fSJosef 'Jeff' Sipek 	if (fd < 0)
173*45bf502fSJosef 'Jeff' Sipek 		return (NULL);
174*45bf502fSJosef 'Jeff' Sipek 
175*45bf502fSJosef 'Jeff' Sipek 	SLIST_FOREACH(file, &open_files, next)
176*45bf502fSJosef 'Jeff' Sipek 		if (file->fd == fd)
177*45bf502fSJosef 'Jeff' Sipek 			return (file);
178*45bf502fSJosef 'Jeff' Sipek 
179*45bf502fSJosef 'Jeff' Sipek 	return (NULL);
180*45bf502fSJosef 'Jeff' Sipek }
181*45bf502fSJosef 'Jeff' Sipek 
182*45bf502fSJosef 'Jeff' Sipek static const void *
read_ramdisk(size_t off,size_t len)183*45bf502fSJosef 'Jeff' Sipek read_ramdisk(size_t off, size_t len)
184*45bf502fSJosef 'Jeff' Sipek {
185*45bf502fSJosef 'Jeff' Sipek 	const size_t first_block_offset = off % DEV_BSIZE;
186*45bf502fSJosef 'Jeff' Sipek 	fileid_t tmpfile;
187*45bf502fSJosef 'Jeff' Sipek 
188*45bf502fSJosef 'Jeff' Sipek 	/* return a dummy non-NULL pointer */
189*45bf502fSJosef 'Jeff' Sipek 	if (len == 0)
190*45bf502fSJosef 'Jeff' Sipek 		return ("");
191*45bf502fSJosef 'Jeff' Sipek 
192*45bf502fSJosef 'Jeff' Sipek 	/* we have to read the stuff before the desired location as well */
193*45bf502fSJosef 'Jeff' Sipek 	len += first_block_offset;
194*45bf502fSJosef 'Jeff' Sipek 
195*45bf502fSJosef 'Jeff' Sipek 	tmpfile.fi_blocknum = off / DEV_BSIZE;
196*45bf502fSJosef 'Jeff' Sipek 	tmpfile.fi_count = P2ROUNDUP_TYPED(len, DEV_BSIZE, size_t);
197*45bf502fSJosef 'Jeff' Sipek 	tmpfile.fi_memp = NULL;
198*45bf502fSJosef 'Jeff' Sipek 
199*45bf502fSJosef 'Jeff' Sipek 	if (diskread(&tmpfile) != 0)
200*45bf502fSJosef 'Jeff' Sipek 		return (NULL);
201*45bf502fSJosef 'Jeff' Sipek 
202*45bf502fSJosef 'Jeff' Sipek 	return (tmpfile.fi_memp + first_block_offset);
203*45bf502fSJosef 'Jeff' Sipek }
204*45bf502fSJosef 'Jeff' Sipek 
205*45bf502fSJosef 'Jeff' Sipek static bool
parse_stat(const struct cpio_hdr * hdr,struct bootstat * stat)206*45bf502fSJosef 'Jeff' Sipek parse_stat(const struct cpio_hdr *hdr, struct bootstat *stat)
207*45bf502fSJosef 'Jeff' Sipek {
208*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint64(hdr->dev, sizeof (hdr->dev), &stat->st_dev))
209*45bf502fSJosef 'Jeff' Sipek 		return (false);
210*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint64(hdr->ino, sizeof (hdr->ino), &stat->st_ino))
211*45bf502fSJosef 'Jeff' Sipek 		return (false);
212*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint32(hdr->mode, sizeof (hdr->mode), &stat->st_mode))
213*45bf502fSJosef 'Jeff' Sipek 		return (false);
214*45bf502fSJosef 'Jeff' Sipek 	if (!get_int32(hdr->uid, sizeof (hdr->uid), &stat->st_uid))
215*45bf502fSJosef 'Jeff' Sipek 		return (false);
216*45bf502fSJosef 'Jeff' Sipek 	if (!get_int32(hdr->gid, sizeof (hdr->gid), &stat->st_gid))
217*45bf502fSJosef 'Jeff' Sipek 		return (false);
218*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint32(hdr->nlink, sizeof (hdr->nlink), &stat->st_nlink))
219*45bf502fSJosef 'Jeff' Sipek 		return (false);
220*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint64(hdr->rdev, sizeof (hdr->rdev), &stat->st_rdev))
221*45bf502fSJosef 'Jeff' Sipek 		return (false);
222*45bf502fSJosef 'Jeff' Sipek 
223*45bf502fSJosef 'Jeff' Sipek 	stat->st_mtim.tv_nsec = 0;
224*45bf502fSJosef 'Jeff' Sipek 	if (!get_int64(hdr->mtime, sizeof (hdr->mtime), &stat->st_mtim.tv_sec))
225*45bf502fSJosef 'Jeff' Sipek 		return (false);
226*45bf502fSJosef 'Jeff' Sipek 
227*45bf502fSJosef 'Jeff' Sipek 	stat->st_atim = stat->st_mtim;
228*45bf502fSJosef 'Jeff' Sipek 	stat->st_ctim = stat->st_mtim;
229*45bf502fSJosef 'Jeff' Sipek 
230*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint64(hdr->filesize, sizeof (hdr->filesize), &stat->st_size))
231*45bf502fSJosef 'Jeff' Sipek 		return (false);
232*45bf502fSJosef 'Jeff' Sipek 
233*45bf502fSJosef 'Jeff' Sipek 	stat->st_blksize = DEV_BSIZE;
234*45bf502fSJosef 'Jeff' Sipek 	stat->st_blocks = P2ROUNDUP(stat->st_size, DEV_BSIZE);
235*45bf502fSJosef 'Jeff' Sipek 
236*45bf502fSJosef 'Jeff' Sipek 	return (true);
237*45bf502fSJosef 'Jeff' Sipek }
238*45bf502fSJosef 'Jeff' Sipek 
239*45bf502fSJosef 'Jeff' Sipek /*
240*45bf502fSJosef 'Jeff' Sipek  * Check if specified header is for a file with a specific path.  If so,
241*45bf502fSJosef 'Jeff' Sipek  * fill in the file struct and return 0.  If not, return number of bytes to
242*45bf502fSJosef 'Jeff' Sipek  * skip over to get to the next header.  If an error occurs, -1 is returned.
243*45bf502fSJosef 'Jeff' Sipek  * If end of archive is reached, return -2 instead.
244*45bf502fSJosef 'Jeff' Sipek  */
245*45bf502fSJosef 'Jeff' Sipek static ssize_t
scan_archive_hdr(const struct cpio_hdr * hdr,size_t off,struct cpio_file * file,const char * wanted_path)246*45bf502fSJosef 'Jeff' Sipek scan_archive_hdr(const struct cpio_hdr *hdr, size_t off,
247*45bf502fSJosef 'Jeff' Sipek     struct cpio_file *file, const char *wanted_path)
248*45bf502fSJosef 'Jeff' Sipek {
249*45bf502fSJosef 'Jeff' Sipek 	struct bootstat stat;
250*45bf502fSJosef 'Jeff' Sipek 	uint32_t namesize;
251*45bf502fSJosef 'Jeff' Sipek 	uint64_t filesize;
252*45bf502fSJosef 'Jeff' Sipek 	const char *path;
253*45bf502fSJosef 'Jeff' Sipek 	const void *data;
254*45bf502fSJosef 'Jeff' Sipek 
255*45bf502fSJosef 'Jeff' Sipek 	if ((hdr->magic[0] != '0') || (hdr->magic[1] != '7') ||
256*45bf502fSJosef 'Jeff' Sipek 	    (hdr->magic[2] != '0') || (hdr->magic[3] != '7') ||
257*45bf502fSJosef 'Jeff' Sipek 	    (hdr->magic[4] != '0') || (hdr->magic[5] != '7'))
258*45bf502fSJosef 'Jeff' Sipek 		return (-1);
259*45bf502fSJosef 'Jeff' Sipek 
260*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint32(hdr->namesize, sizeof (hdr->namesize), &namesize))
261*45bf502fSJosef 'Jeff' Sipek 		return (-1);
262*45bf502fSJosef 'Jeff' Sipek 	if (!get_uint64(hdr->filesize, sizeof (hdr->filesize), &filesize))
263*45bf502fSJosef 'Jeff' Sipek 		return (-1);
264*45bf502fSJosef 'Jeff' Sipek 
265*45bf502fSJosef 'Jeff' Sipek 	/*
266*45bf502fSJosef 'Jeff' Sipek 	 * We have the two sizes, let's try to read the name and file
267*45bf502fSJosef 'Jeff' Sipek 	 * contents to make sure they are part of the ramdisk.
268*45bf502fSJosef 'Jeff' Sipek 	 */
269*45bf502fSJosef 'Jeff' Sipek 
270*45bf502fSJosef 'Jeff' Sipek 	off += offsetof(struct cpio_hdr, data[0]);
271*45bf502fSJosef 'Jeff' Sipek 	path = read_ramdisk(off, namesize);
272*45bf502fSJosef 'Jeff' Sipek 	data = read_ramdisk(off + namesize, filesize);
273*45bf502fSJosef 'Jeff' Sipek 
274*45bf502fSJosef 'Jeff' Sipek 	/* either read failing is fatal */
275*45bf502fSJosef 'Jeff' Sipek 	if (path == NULL || data == NULL)
276*45bf502fSJosef 'Jeff' Sipek 		return (-1);
277*45bf502fSJosef 'Jeff' Sipek 
278*45bf502fSJosef 'Jeff' Sipek 	if (cpio_strcmp(path, "TRAILER!!!") == 0)
279*45bf502fSJosef 'Jeff' Sipek 		return (-2);
280*45bf502fSJosef 'Jeff' Sipek 
281*45bf502fSJosef 'Jeff' Sipek 	if (cpio_strcmp(path, wanted_path) != 0)
282*45bf502fSJosef 'Jeff' Sipek 		return (offsetof(struct cpio_hdr, data[namesize + filesize]));
283*45bf502fSJosef 'Jeff' Sipek 
284*45bf502fSJosef 'Jeff' Sipek 	/*
285*45bf502fSJosef 'Jeff' Sipek 	 * This is the file we want!
286*45bf502fSJosef 'Jeff' Sipek 	 */
287*45bf502fSJosef 'Jeff' Sipek 
288*45bf502fSJosef 'Jeff' Sipek 	if (!parse_stat(hdr, &stat))
289*45bf502fSJosef 'Jeff' Sipek 		return (-1);
290*45bf502fSJosef 'Jeff' Sipek 
291*45bf502fSJosef 'Jeff' Sipek 	file->hdr = hdr;
292*45bf502fSJosef 'Jeff' Sipek 	file->path = path;
293*45bf502fSJosef 'Jeff' Sipek 	file->data = data;
294*45bf502fSJosef 'Jeff' Sipek 	file->stat = stat;
295*45bf502fSJosef 'Jeff' Sipek 
296*45bf502fSJosef 'Jeff' Sipek 	return (0);
297*45bf502fSJosef 'Jeff' Sipek }
298*45bf502fSJosef 'Jeff' Sipek 
299*45bf502fSJosef 'Jeff' Sipek static int
find_filename(char * path,struct cpio_file * file)300*45bf502fSJosef 'Jeff' Sipek find_filename(char *path, struct cpio_file *file)
301*45bf502fSJosef 'Jeff' Sipek {
302*45bf502fSJosef 'Jeff' Sipek 	size_t off;
303*45bf502fSJosef 'Jeff' Sipek 
304*45bf502fSJosef 'Jeff' Sipek 	/*
305*45bf502fSJosef 'Jeff' Sipek 	 * The paths in the cpio boot archive omit the leading '/'.  So,
306*45bf502fSJosef 'Jeff' Sipek 	 * skip checking for it.  If the searched for path does not include
307*45bf502fSJosef 'Jeff' Sipek 	 * the leading path (it's a relative path), fail the lookup.
308*45bf502fSJosef 'Jeff' Sipek 	 */
309*45bf502fSJosef 'Jeff' Sipek 	if (path[0] != '/')
310*45bf502fSJosef 'Jeff' Sipek 		return (-1);
311*45bf502fSJosef 'Jeff' Sipek 
312*45bf502fSJosef 'Jeff' Sipek 	path++;
313*45bf502fSJosef 'Jeff' Sipek 
314*45bf502fSJosef 'Jeff' Sipek 	/* now scan the archive for the relevant file */
315*45bf502fSJosef 'Jeff' Sipek 
316*45bf502fSJosef 'Jeff' Sipek 	off = 0;
317*45bf502fSJosef 'Jeff' Sipek 
318*45bf502fSJosef 'Jeff' Sipek 	for (;;) {
319*45bf502fSJosef 'Jeff' Sipek 		const struct cpio_hdr *hdr;
320*45bf502fSJosef 'Jeff' Sipek 		ssize_t size;
321*45bf502fSJosef 'Jeff' Sipek 
322*45bf502fSJosef 'Jeff' Sipek 		hdr = read_ramdisk(off, sizeof (struct cpio_hdr));
323*45bf502fSJosef 'Jeff' Sipek 		if (hdr == NULL)
324*45bf502fSJosef 'Jeff' Sipek 			return (-1);
325*45bf502fSJosef 'Jeff' Sipek 
326*45bf502fSJosef 'Jeff' Sipek 		size = scan_archive_hdr(hdr, off, file, path);
327*45bf502fSJosef 'Jeff' Sipek 		if (size <= 0)
328*45bf502fSJosef 'Jeff' Sipek 			return (size);
329*45bf502fSJosef 'Jeff' Sipek 
330*45bf502fSJosef 'Jeff' Sipek 		off += size;
331*45bf502fSJosef 'Jeff' Sipek 	}
332*45bf502fSJosef 'Jeff' Sipek }
333*45bf502fSJosef 'Jeff' Sipek 
334*45bf502fSJosef 'Jeff' Sipek /* ARGSUSED */
335*45bf502fSJosef 'Jeff' Sipek static int
bcpio_mountroot(char * str __unused)336*45bf502fSJosef 'Jeff' Sipek bcpio_mountroot(char *str __unused)
337*45bf502fSJosef 'Jeff' Sipek {
338*45bf502fSJosef 'Jeff' Sipek 	if (mounted)
339*45bf502fSJosef 'Jeff' Sipek 		return (-1);
340*45bf502fSJosef 'Jeff' Sipek 
341*45bf502fSJosef 'Jeff' Sipek 	mounted = true;
342*45bf502fSJosef 'Jeff' Sipek 
343*45bf502fSJosef 'Jeff' Sipek 	return (0);
344*45bf502fSJosef 'Jeff' Sipek }
345*45bf502fSJosef 'Jeff' Sipek 
346*45bf502fSJosef 'Jeff' Sipek static int
bcpio_unmountroot(void)347*45bf502fSJosef 'Jeff' Sipek bcpio_unmountroot(void)
348*45bf502fSJosef 'Jeff' Sipek {
349*45bf502fSJosef 'Jeff' Sipek 	if (!mounted)
350*45bf502fSJosef 'Jeff' Sipek 		return (-1);
351*45bf502fSJosef 'Jeff' Sipek 
352*45bf502fSJosef 'Jeff' Sipek 	mounted = false;
353*45bf502fSJosef 'Jeff' Sipek 
354*45bf502fSJosef 'Jeff' Sipek 	return (0);
355*45bf502fSJosef 'Jeff' Sipek }
356*45bf502fSJosef 'Jeff' Sipek 
357*45bf502fSJosef 'Jeff' Sipek /* ARGSUSED */
358*45bf502fSJosef 'Jeff' Sipek static int
bcpio_open(char * path,int flags __unused)359*45bf502fSJosef 'Jeff' Sipek bcpio_open(char *path, int flags __unused)
360*45bf502fSJosef 'Jeff' Sipek {
361*45bf502fSJosef 'Jeff' Sipek 	static int filedes = 1;
362*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file temp_file;
363*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file *file;
364*45bf502fSJosef 'Jeff' Sipek 
365*45bf502fSJosef 'Jeff' Sipek 	if (find_filename(path, &temp_file) != 0)
366*45bf502fSJosef 'Jeff' Sipek 		return (-1);
367*45bf502fSJosef 'Jeff' Sipek 
368*45bf502fSJosef 'Jeff' Sipek 	file = bkmem_alloc(sizeof (struct cpio_file));
369*45bf502fSJosef 'Jeff' Sipek 	file->hdr = temp_file.hdr;
370*45bf502fSJosef 'Jeff' Sipek 	file->path = temp_file.path;
371*45bf502fSJosef 'Jeff' Sipek 	file->data = temp_file.data;
372*45bf502fSJosef 'Jeff' Sipek 	file->stat = temp_file.stat;
373*45bf502fSJosef 'Jeff' Sipek 	file->fd = filedes++;
374*45bf502fSJosef 'Jeff' Sipek 	file->off = 0;
375*45bf502fSJosef 'Jeff' Sipek 
376*45bf502fSJosef 'Jeff' Sipek 	add_open_file(file);
377*45bf502fSJosef 'Jeff' Sipek 
378*45bf502fSJosef 'Jeff' Sipek 	return (file->fd);
379*45bf502fSJosef 'Jeff' Sipek }
380*45bf502fSJosef 'Jeff' Sipek 
381*45bf502fSJosef 'Jeff' Sipek static int
bcpio_close(int fd)382*45bf502fSJosef 'Jeff' Sipek bcpio_close(int fd)
383*45bf502fSJosef 'Jeff' Sipek {
384*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file *file;
385*45bf502fSJosef 'Jeff' Sipek 
386*45bf502fSJosef 'Jeff' Sipek 	file = find_open_file(fd);
387*45bf502fSJosef 'Jeff' Sipek 	if (file == NULL)
388*45bf502fSJosef 'Jeff' Sipek 		return (-1);
389*45bf502fSJosef 'Jeff' Sipek 
390*45bf502fSJosef 'Jeff' Sipek 	remove_open_file(file);
391*45bf502fSJosef 'Jeff' Sipek 
392*45bf502fSJosef 'Jeff' Sipek 	bkmem_free(file, sizeof (struct cpio_file));
393*45bf502fSJosef 'Jeff' Sipek 
394*45bf502fSJosef 'Jeff' Sipek 	return (0);
395*45bf502fSJosef 'Jeff' Sipek }
396*45bf502fSJosef 'Jeff' Sipek 
397*45bf502fSJosef 'Jeff' Sipek /* ARGSUSED */
398*45bf502fSJosef 'Jeff' Sipek static void
bcpio_closeall(int flag __unused)399*45bf502fSJosef 'Jeff' Sipek bcpio_closeall(int flag __unused)
400*45bf502fSJosef 'Jeff' Sipek {
401*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file *file;
402*45bf502fSJosef 'Jeff' Sipek 
403*45bf502fSJosef 'Jeff' Sipek 	while (!SLIST_EMPTY(&open_files)) {
404*45bf502fSJosef 'Jeff' Sipek 		file = SLIST_FIRST(&open_files);
405*45bf502fSJosef 'Jeff' Sipek 
406*45bf502fSJosef 'Jeff' Sipek 		if (bcpio_close(file->fd) != 0)
407*45bf502fSJosef 'Jeff' Sipek 			printf("closeall invoked close(%d) failed\n", file->fd);
408*45bf502fSJosef 'Jeff' Sipek 	}
409*45bf502fSJosef 'Jeff' Sipek }
410*45bf502fSJosef 'Jeff' Sipek 
411*45bf502fSJosef 'Jeff' Sipek static ssize_t
bcpio_read(int fd,caddr_t buf,size_t size)412*45bf502fSJosef 'Jeff' Sipek bcpio_read(int fd, caddr_t buf, size_t size)
413*45bf502fSJosef 'Jeff' Sipek {
414*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file *file;
415*45bf502fSJosef 'Jeff' Sipek 
416*45bf502fSJosef 'Jeff' Sipek 	file = find_open_file(fd);
417*45bf502fSJosef 'Jeff' Sipek 	if (file == NULL)
418*45bf502fSJosef 'Jeff' Sipek 		return (-1);
419*45bf502fSJosef 'Jeff' Sipek 
420*45bf502fSJosef 'Jeff' Sipek 	if (size == 0)
421*45bf502fSJosef 'Jeff' Sipek 		return (0);
422*45bf502fSJosef 'Jeff' Sipek 
423*45bf502fSJosef 'Jeff' Sipek 	if (file->off + size > file->stat.st_size)
424*45bf502fSJosef 'Jeff' Sipek 		size = file->stat.st_size - file->off;
425*45bf502fSJosef 'Jeff' Sipek 
426*45bf502fSJosef 'Jeff' Sipek 	bcopy((void *)((uintptr_t)file->data + file->off), buf, size);
427*45bf502fSJosef 'Jeff' Sipek 
428*45bf502fSJosef 'Jeff' Sipek 	file->off += size;
429*45bf502fSJosef 'Jeff' Sipek 
430*45bf502fSJosef 'Jeff' Sipek 	return (size);
431*45bf502fSJosef 'Jeff' Sipek }
432*45bf502fSJosef 'Jeff' Sipek 
433*45bf502fSJosef 'Jeff' Sipek static off_t
bcpio_lseek(int fd,off_t addr,int whence)434*45bf502fSJosef 'Jeff' Sipek bcpio_lseek(int fd, off_t addr, int whence)
435*45bf502fSJosef 'Jeff' Sipek {
436*45bf502fSJosef 'Jeff' Sipek 	struct cpio_file *file;
437*45bf502fSJosef 'Jeff' Sipek 
438*45bf502fSJosef 'Jeff' Sipek 	file = find_open_file(fd);
439*45bf502fSJosef 'Jeff' Sipek 	if (file == NULL)
440*45bf502fSJosef 'Jeff' Sipek 		return (-1);
441*45bf502fSJosef 'Jeff' Sipek 
442*45bf502fSJosef 'Jeff' Sipek 	switch (whence) {
443*45bf502fSJosef 'Jeff' Sipek 		case SEEK_CUR:
444*45bf502fSJosef 'Jeff' Sipek 			file->off += addr;
445*45bf502fSJosef 'Jeff' Sipek 			break;
446*45bf502fSJosef 'Jeff' Sipek 		case SEEK_SET:
447*45bf502fSJosef 'Jeff' Sipek 			file->off = addr;
448*45bf502fSJosef 'Jeff' Sipek 			break;
449*45bf502fSJosef 'Jeff' Sipek 		case SEEK_END:
450*45bf502fSJosef 'Jeff' Sipek 			file->off = file->stat.st_size;
451*45bf502fSJosef 'Jeff' Sipek 			break;
452*45bf502fSJosef 'Jeff' Sipek 		default:
453*45bf502fSJosef 'Jeff' Sipek 			printf("lseek(): invalid whence value %d\n", whence);
454*45bf502fSJosef 'Jeff' Sipek 			return (-1);
455*45bf502fSJosef 'Jeff' Sipek 	}
456*45bf502fSJosef 'Jeff' Sipek 
457*45bf502fSJosef 'Jeff' Sipek 	return (0);
458*45bf502fSJosef 'Jeff' Sipek }
459*45bf502fSJosef 'Jeff' Sipek 
460*45bf502fSJosef 'Jeff' Sipek static int
bcpio_fstat(int fd,struct bootstat * buf)461*45bf502fSJosef 'Jeff' Sipek bcpio_fstat(int fd, struct bootstat *buf)
462*45bf502fSJosef 'Jeff' Sipek {
463*45bf502fSJosef 'Jeff' Sipek 	const struct cpio_file *file;
464*45bf502fSJosef 'Jeff' Sipek 
465*45bf502fSJosef 'Jeff' Sipek 	file = find_open_file(fd);
466*45bf502fSJosef 'Jeff' Sipek 	if (file == NULL)
467*45bf502fSJosef 'Jeff' Sipek 		return (-1);
468*45bf502fSJosef 'Jeff' Sipek 
469*45bf502fSJosef 'Jeff' Sipek 	*buf = file->stat;
470*45bf502fSJosef 'Jeff' Sipek 
471*45bf502fSJosef 'Jeff' Sipek 	return (0);
472*45bf502fSJosef 'Jeff' Sipek }
473*45bf502fSJosef 'Jeff' Sipek 
474*45bf502fSJosef 'Jeff' Sipek struct boot_fs_ops bcpio_ops = {
475*45bf502fSJosef 'Jeff' Sipek 	.fsw_name		= "boot_cpio",
476*45bf502fSJosef 'Jeff' Sipek 	.fsw_mountroot		= bcpio_mountroot,
477*45bf502fSJosef 'Jeff' Sipek 	.fsw_unmountroot	= bcpio_unmountroot,
478*45bf502fSJosef 'Jeff' Sipek 	.fsw_open		= bcpio_open,
479*45bf502fSJosef 'Jeff' Sipek 	.fsw_close		= bcpio_close,
480*45bf502fSJosef 'Jeff' Sipek 	.fsw_closeall		= bcpio_closeall,
481*45bf502fSJosef 'Jeff' Sipek 	.fsw_read		= bcpio_read,
482*45bf502fSJosef 'Jeff' Sipek 	.fsw_lseek		= bcpio_lseek,
483*45bf502fSJosef 'Jeff' Sipek 	.fsw_fstat		= bcpio_fstat,
484*45bf502fSJosef 'Jeff' Sipek };
485