2 * Copyright 2011-2018 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/sysmacros.h>
19 #include <sys/bootvfs.h>
20 #include <sys/filep.h>
21 #include <sys/sunddi.h>
22 #include <sys/ccompile.h>
23 #include <sys/queue.h>
26 * A cpio archive is just a sequence of files, each consisting of a header
27 * (struct cpio_hdr) and the file contents.
46 * This structure represents an open file. The list of all open files is
47 * rooted in the open_files global.
50 /* pointers into the archive */
51 const struct cpio_hdr
*hdr
;
52 const char *path
; /* pointer into the archive */
53 const void *data
; /* pointer into the archive */
59 SLIST_ENTRY(cpio_file
) next
;
62 extern void *bkmem_alloc(size_t);
63 extern void bkmem_free(void *, size_t);
65 static void cpio_closeall(int flag
);
68 static SLIST_HEAD(cpio_file_list
, cpio_file
)
69 open_files
= SLIST_HEAD_INITIALIZER(open_files
);
72 cpio_strcmp(const char *a
, const char *b
)
74 while ((*a
!= '\0') && (*b
!= '\0') && (*a
== *b
)) {
87 * Returns the parsed number on success, or UINT64_MAX on error. This is
88 * ok because we will never deal with numbers that large in a cpio archive.
91 __get_uint64(const uint8_t *str
, size_t len
, const size_t output_size
)
95 /* check that we can represent every number */
96 if (len
* 3 > output_size
)
99 for (v
= 0; len
> 0; len
--, str
++) {
100 const uint8_t c
= *str
;
102 if ((c
< '0') || (c
> '7'))
105 v
= (v
* 8) + (c
- '0');
112 get_uint64(const uint8_t *str
, size_t len
, uint64_t *out
)
114 *out
= __get_uint64(str
, len
, NBBY
* sizeof(*out
));
115 return *out
!= UINT64_MAX
;
119 get_int64(const uint8_t *str
, size_t len
, int64_t *out
)
123 tmp
= __get_uint64(str
, len
, NBBY
* sizeof(*out
) - 1);
127 return tmp
!= UINT64_MAX
;
131 get_uint32(const uint8_t *str
, size_t len
, uint32_t *out
)
135 tmp
= __get_uint64(str
, len
, NBBY
* sizeof(*out
));
139 return tmp
!= UINT64_MAX
;
143 get_int32(const uint8_t *str
, size_t len
, int32_t *out
)
147 tmp
= __get_uint64(str
, len
, NBBY
* sizeof(*out
) - 1);
151 return tmp
!= UINT64_MAX
;
155 add_open_file(struct cpio_file
*file
)
157 SLIST_INSERT_HEAD(&open_files
, file
, next
);
161 remove_open_file(struct cpio_file
*file
)
163 SLIST_REMOVE(&open_files
, file
, cpio_file
, next
);
166 static struct cpio_file
*
167 find_open_file(int fd
)
169 struct cpio_file
*file
;
174 SLIST_FOREACH(file
, &open_files
, next
)
182 read_ramdisk(size_t off
, size_t len
)
184 const size_t first_block_offset
= off
% DEV_BSIZE
;
187 /* return a dummy non-NULL pointer */
191 /* we have to read the stuff before the desired location as well */
192 len
+= first_block_offset
;
194 tmpfile
.fi_blocknum
= off
/ DEV_BSIZE
;
195 tmpfile
.fi_count
= P2ROUNDUP_TYPED(len
, DEV_BSIZE
, size_t);
196 tmpfile
.fi_memp
= NULL
;
198 if (diskread(&tmpfile
) != 0)
201 return tmpfile
.fi_memp
+ first_block_offset
;
205 parse_stat(const struct cpio_hdr
*hdr
, struct bootstat
*stat
)
207 if (!get_uint64(hdr
->dev
, sizeof(hdr
->dev
), &stat
->st_dev
))
209 if (!get_uint64(hdr
->ino
, sizeof(hdr
->ino
), &stat
->st_ino
))
211 if (!get_uint32(hdr
->mode
, sizeof(hdr
->mode
), &stat
->st_mode
))
213 if (!get_int32(hdr
->uid
, sizeof(hdr
->uid
), &stat
->st_uid
))
215 if (!get_int32(hdr
->gid
, sizeof(hdr
->gid
), &stat
->st_gid
))
217 if (!get_uint32(hdr
->nlink
, sizeof(hdr
->nlink
), &stat
->st_nlink
))
219 if (!get_uint64(hdr
->rdev
, sizeof(hdr
->rdev
), &stat
->st_rdev
))
222 stat
->st_mtim
.tv_nsec
= 0;
223 if (!get_int64(hdr
->mtime
, sizeof(hdr
->mtime
), &stat
->st_mtim
.tv_sec
))
226 stat
->st_atim
= stat
->st_mtim
;
227 stat
->st_ctim
= stat
->st_mtim
;
229 if (!get_uint64(hdr
->filesize
, sizeof(hdr
->filesize
), &stat
->st_size
))
232 stat
->st_blksize
= DEV_BSIZE
;
233 stat
->st_blocks
= P2ROUNDUP(stat
->st_size
, DEV_BSIZE
);
239 * Check if specified header is for a file with a specific path. If so,
240 * fill in the file struct and return 0. If not, return number of bytes to
241 * skip over to get to the next header. If an error occurs, -1 is returned.
242 * If end of archive is reached, return -2 instead.
245 scan_archive_hdr(const struct cpio_hdr
*hdr
, size_t off
,
246 struct cpio_file
*file
, const char *wanted_path
)
248 struct bootstat stat
;
254 if ((hdr
->magic
[0] != '0') || (hdr
->magic
[1] != '7') ||
255 (hdr
->magic
[2] != '0') || (hdr
->magic
[3] != '7') ||
256 (hdr
->magic
[4] != '0') || (hdr
->magic
[5] != '7'))
259 if (!get_uint32(hdr
->namesize
, sizeof(hdr
->namesize
), &namesize
))
261 if (!get_uint64(hdr
->filesize
, sizeof(hdr
->filesize
), &filesize
))
265 * We have the two sizes, let's try to read the name and file
266 * contents to make sure they are part of the ramdisk.
269 off
+= offsetof(struct cpio_hdr
, data
[0]);
270 path
= read_ramdisk(off
, namesize
);
271 data
= read_ramdisk(off
+ namesize
, filesize
);
273 /* either read failing is fatal */
274 if (path
== NULL
|| data
== NULL
)
277 if (cpio_strcmp(path
, "TRAILER!!!") == 0)
280 if (cpio_strcmp(path
, wanted_path
) != 0)
281 return offsetof(struct cpio_hdr
, data
[namesize
+ filesize
]);
284 * This is the file we want!
287 if (!parse_stat(hdr
, &stat
))
299 find_filename(char *path
, struct cpio_file
*file
)
304 * The paths in the cpio boot archive omit the leading '/'. So,
305 * skip checking for it. If the searched for path does not include
306 * the leading path (it's a relative path), fail the lookup.
313 /* now scan the archive for the relevant file */
318 const struct cpio_hdr
*hdr
;
321 hdr
= read_ramdisk(off
, sizeof(struct cpio_hdr
));
325 size
= scan_archive_hdr(hdr
, off
, file
, path
);
334 bcpio_mountroot(char *str
)
345 bcpio_unmountroot(void)
356 bcpio_open(char *path
, int flags
)
358 static int filedes
= 1;
359 struct cpio_file temp_file
;
360 struct cpio_file
*file
;
362 if (find_filename(path
, &temp_file
) != 0)
365 file
= bkmem_alloc(sizeof(struct cpio_file
));
366 file
->hdr
= temp_file
.hdr
;
367 file
->path
= temp_file
.path
;
368 file
->data
= temp_file
.data
;
369 file
->stat
= temp_file
.stat
;
370 file
->fd
= filedes
++;
381 struct cpio_file
*file
;
383 file
= find_open_file(fd
);
387 remove_open_file(file
);
389 bkmem_free(file
, sizeof(struct cpio_file
));
395 bcpio_closeall(int flag
)
397 struct cpio_file
*file
;
399 while (!SLIST_EMPTY(&open_files
)) {
400 file
= SLIST_FIRST(&open_files
);
402 if (bcpio_close(file
->fd
) != 0)
403 printf("closeall invoked close(%d) failed\n", file
->fd
);
408 bcpio_read(int fd
, caddr_t buf
, size_t size
)
410 struct cpio_file
*file
;
412 file
= find_open_file(fd
);
419 if (file
->off
+ size
> file
->stat
.st_size
)
420 size
= file
->stat
.st_size
- file
->off
;
422 bcopy(file
->data
+ file
->off
, buf
, size
);
430 bcpio_lseek(int fd
, off_t addr
, int whence
)
432 struct cpio_file
*file
;
434 file
= find_open_file(fd
);
446 file
->off
= file
->stat
.st_size
;
449 printf("lseek(): invalid whence value %d\n", whence
);
457 bcpio_fstat(int fd
, struct bootstat
*buf
)
459 const struct cpio_file
*file
;
461 file
= find_open_file(fd
);
470 struct boot_fs_ops bcpio_ops
= {
471 .fsw_name
= "boot_cpio",
472 .fsw_mountroot
= bcpio_mountroot
,
473 .fsw_unmountroot
= bcpio_unmountroot
,
474 .fsw_open
= bcpio_open
,
475 .fsw_close
= bcpio_close
,
476 .fsw_closeall
= bcpio_closeall
,
477 .fsw_read
= bcpio_read
,
478 .fsw_lseek
= bcpio_lseek
,
479 .fsw_fstat
= bcpio_fstat
,