2 * Copyright 2011-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/types.h>
18 #include <sys/stdbool.h>
19 #include <sys/sysmacros.h>
20 #include <sys/bootvfs.h>
21 #include <sys/filep.h>
22 #include <sys/sunddi.h>
23 #include <sys/ccompile.h>
24 #include <sys/queue.h>
27 * A cpio archive is just a sequence of files, each consisting of a header
28 * (struct cpio_hdr) and the file contents.
47 * This structure represents an open file. The list of all open files is
48 * rooted in the open_files global.
51 /* pointers into the archive */
52 const struct cpio_hdr
*hdr
;
53 const char *path
; /* pointer into the archive */
54 const void *data
; /* pointer into the archive */
60 SLIST_ENTRY(cpio_file
) next
;
63 extern void *bkmem_alloc(size_t);
64 extern void bkmem_free(void *, size_t);
66 static void cpio_closeall(int flag
);
69 static SLIST_HEAD(cpio_file_list
, cpio_file
)
70 open_files
= SLIST_HEAD_INITIALIZER(open_files
);
73 cpio_strcmp(const char *a
, const char *b
)
75 while ((*a
!= '\0') && (*b
!= '\0') && (*a
== *b
)) {
88 * Returns the parsed number on success, or UINT64_MAX on error. This is
89 * ok because we will never deal with numbers that large in a cpio archive.
92 __get_uint64(const uint8_t *str
, size_t len
, const size_t output_size
)
96 /* check that we can represent every number */
97 if (len
* 3 > output_size
)
100 for (v
= 0; len
> 0; len
--, str
++) {
101 const uint8_t c
= *str
;
103 if ((c
< '0') || (c
> '7'))
106 v
= (v
* 8) + (c
- '0');
113 get_uint64(const uint8_t *str
, size_t len
, uint64_t *out
)
115 *out
= __get_uint64(str
, len
, NBBY
* sizeof (*out
));
116 return (*out
!= UINT64_MAX
);
120 get_int64(const uint8_t *str
, size_t len
, int64_t *out
)
124 tmp
= __get_uint64(str
, len
, NBBY
* sizeof (*out
) - 1);
128 return (tmp
!= UINT64_MAX
);
132 get_uint32(const uint8_t *str
, size_t len
, uint32_t *out
)
136 tmp
= __get_uint64(str
, len
, NBBY
* sizeof (*out
));
140 return (tmp
!= UINT64_MAX
);
144 get_int32(const uint8_t *str
, size_t len
, int32_t *out
)
148 tmp
= __get_uint64(str
, len
, NBBY
* sizeof (*out
) - 1);
152 return (tmp
!= UINT64_MAX
);
156 add_open_file(struct cpio_file
*file
)
158 SLIST_INSERT_HEAD(&open_files
, file
, next
);
162 remove_open_file(struct cpio_file
*file
)
164 SLIST_REMOVE(&open_files
, file
, cpio_file
, next
);
167 static struct cpio_file
*
168 find_open_file(int fd
)
170 struct cpio_file
*file
;
175 SLIST_FOREACH(file
, &open_files
, next
)
183 read_ramdisk(size_t off
, size_t len
)
185 const size_t first_block_offset
= off
% DEV_BSIZE
;
188 /* return a dummy non-NULL pointer */
192 /* we have to read the stuff before the desired location as well */
193 len
+= first_block_offset
;
195 tmpfile
.fi_blocknum
= off
/ DEV_BSIZE
;
196 tmpfile
.fi_count
= P2ROUNDUP_TYPED(len
, DEV_BSIZE
, size_t);
197 tmpfile
.fi_memp
= NULL
;
199 if (diskread(&tmpfile
) != 0)
202 return (tmpfile
.fi_memp
+ first_block_offset
);
206 parse_stat(const struct cpio_hdr
*hdr
, struct bootstat
*stat
)
208 if (!get_uint64(hdr
->dev
, sizeof (hdr
->dev
), &stat
->st_dev
))
210 if (!get_uint64(hdr
->ino
, sizeof (hdr
->ino
), &stat
->st_ino
))
212 if (!get_uint32(hdr
->mode
, sizeof (hdr
->mode
), &stat
->st_mode
))
214 if (!get_int32(hdr
->uid
, sizeof (hdr
->uid
), &stat
->st_uid
))
216 if (!get_int32(hdr
->gid
, sizeof (hdr
->gid
), &stat
->st_gid
))
218 if (!get_uint32(hdr
->nlink
, sizeof (hdr
->nlink
), &stat
->st_nlink
))
220 if (!get_uint64(hdr
->rdev
, sizeof (hdr
->rdev
), &stat
->st_rdev
))
223 stat
->st_mtim
.tv_nsec
= 0;
224 if (!get_int64(hdr
->mtime
, sizeof (hdr
->mtime
), &stat
->st_mtim
.tv_sec
))
227 stat
->st_atim
= stat
->st_mtim
;
228 stat
->st_ctim
= stat
->st_mtim
;
230 if (!get_uint64(hdr
->filesize
, sizeof (hdr
->filesize
), &stat
->st_size
))
233 stat
->st_blksize
= DEV_BSIZE
;
234 stat
->st_blocks
= P2ROUNDUP(stat
->st_size
, DEV_BSIZE
);
240 * Check if specified header is for a file with a specific path. If so,
241 * fill in the file struct and return 0. If not, return number of bytes to
242 * skip over to get to the next header. If an error occurs, -1 is returned.
243 * If end of archive is reached, return -2 instead.
246 scan_archive_hdr(const struct cpio_hdr
*hdr
, size_t off
,
247 struct cpio_file
*file
, const char *wanted_path
)
249 struct bootstat stat
;
255 if ((hdr
->magic
[0] != '0') || (hdr
->magic
[1] != '7') ||
256 (hdr
->magic
[2] != '0') || (hdr
->magic
[3] != '7') ||
257 (hdr
->magic
[4] != '0') || (hdr
->magic
[5] != '7'))
260 if (!get_uint32(hdr
->namesize
, sizeof (hdr
->namesize
), &namesize
))
262 if (!get_uint64(hdr
->filesize
, sizeof (hdr
->filesize
), &filesize
))
266 * We have the two sizes, let's try to read the name and file
267 * contents to make sure they are part of the ramdisk.
270 off
+= offsetof(struct cpio_hdr
, data
[0]);
271 path
= read_ramdisk(off
, namesize
);
272 data
= read_ramdisk(off
+ namesize
, filesize
);
274 /* either read failing is fatal */
275 if (path
== NULL
|| data
== NULL
)
278 if (cpio_strcmp(path
, "TRAILER!!!") == 0)
281 if (cpio_strcmp(path
, wanted_path
) != 0)
282 return (offsetof(struct cpio_hdr
, data
[namesize
+ filesize
]));
285 * This is the file we want!
288 if (!parse_stat(hdr
, &stat
))
300 find_filename(char *path
, struct cpio_file
*file
)
305 * The paths in the cpio boot archive omit the leading '/'. So,
306 * skip checking for it. If the searched for path does not include
307 * the leading path (it's a relative path), fail the lookup.
314 /* now scan the archive for the relevant file */
319 const struct cpio_hdr
*hdr
;
322 hdr
= read_ramdisk(off
, sizeof (struct cpio_hdr
));
326 size
= scan_archive_hdr(hdr
, off
, file
, path
);
336 bcpio_mountroot(char *str __unused
)
347 bcpio_unmountroot(void)
359 bcpio_open(char *path
, int flags __unused
)
361 static int filedes
= 1;
362 struct cpio_file temp_file
;
363 struct cpio_file
*file
;
365 if (find_filename(path
, &temp_file
) != 0)
368 file
= bkmem_alloc(sizeof (struct cpio_file
));
369 file
->hdr
= temp_file
.hdr
;
370 file
->path
= temp_file
.path
;
371 file
->data
= temp_file
.data
;
372 file
->stat
= temp_file
.stat
;
373 file
->fd
= filedes
++;
384 struct cpio_file
*file
;
386 file
= find_open_file(fd
);
390 remove_open_file(file
);
392 bkmem_free(file
, sizeof (struct cpio_file
));
399 bcpio_closeall(int flag __unused
)
401 struct cpio_file
*file
;
403 while (!SLIST_EMPTY(&open_files
)) {
404 file
= SLIST_FIRST(&open_files
);
406 if (bcpio_close(file
->fd
) != 0)
407 printf("closeall invoked close(%d) failed\n", file
->fd
);
412 bcpio_read(int fd
, caddr_t buf
, size_t size
)
414 struct cpio_file
*file
;
416 file
= find_open_file(fd
);
423 if (file
->off
+ size
> file
->stat
.st_size
)
424 size
= file
->stat
.st_size
- file
->off
;
426 bcopy((void *)((uintptr_t)file
->data
+ file
->off
), buf
, size
);
434 bcpio_lseek(int fd
, off_t addr
, int whence
)
436 struct cpio_file
*file
;
438 file
= find_open_file(fd
);
450 file
->off
= file
->stat
.st_size
;
453 printf("lseek(): invalid whence value %d\n", whence
);
461 bcpio_fstat(int fd
, struct bootstat
*buf
)
463 const struct cpio_file
*file
;
465 file
= find_open_file(fd
);
474 struct boot_fs_ops bcpio_ops
= {
475 .fsw_name
= "boot_cpio",
476 .fsw_mountroot
= bcpio_mountroot
,
477 .fsw_unmountroot
= bcpio_unmountroot
,
478 .fsw_open
= bcpio_open
,
479 .fsw_close
= bcpio_close
,
480 .fsw_closeall
= bcpio_closeall
,
481 .fsw_read
= bcpio_read
,
482 .fsw_lseek
= bcpio_lseek
,
483 .fsw_fstat
= bcpio_fstat
,