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>
25 * A cpio archive is just a sequence of files, each consisting of a header
26 * (struct cpio_hdr) and the file contents.
45 * This structure represents an open file. The list of all open files is
46 * rooted in the open_files global.
49 /* pointers into the archive */
50 const struct cpio_hdr
*hdr
;
51 const char *path
; /* pointer into the archive */
52 const void *data
; /* pointer into the archive */
58 struct cpio_file
*next
;
59 struct cpio_file
*prev
;
62 extern void *bkmem_alloc(size_t);
63 extern void bkmem_free(void *, size_t);
65 static void cpio_closeall(int flag
);
68 static struct cpio_file
*open_files
;
71 cpio_strcmp(const char *a
, const char *b
)
73 while ((*a
!= '\0') && (*b
!= '\0') && (*a
== *b
)) {
86 * Returns the parsed number on success, or UINT64_MAX on error. This is
87 * ok because we will never deal with numbers that large in a cpio archive.
90 __get_uint64(const uint8_t *str
, size_t len
, const size_t output_size
)
94 /* check that we can represent every number */
95 if (len
* 3 > output_size
)
98 for (v
= 0; len
> 0; len
--, str
++) {
99 const uint8_t c
= *str
;
101 if ((c
< '0') || (c
> '7'))
104 v
= (v
* 8) + (c
- '0');
111 get_uint64(const uint8_t *str
, size_t len
, uint64_t *out
)
113 *out
= __get_uint64(str
, len
, NBBY
* sizeof(*out
));
114 return *out
!= UINT64_MAX
;
118 get_int64(const uint8_t *str
, size_t len
, int64_t *out
)
122 tmp
= __get_uint64(str
, len
, NBBY
* sizeof(*out
) - 1);
126 return tmp
!= UINT64_MAX
;
130 get_uint32(const uint8_t *str
, size_t len
, uint32_t *out
)
134 tmp
= __get_uint64(str
, len
, NBBY
* sizeof(*out
));
138 return tmp
!= UINT64_MAX
;
142 get_int32(const uint8_t *str
, size_t len
, int32_t *out
)
146 tmp
= __get_uint64(str
, len
, NBBY
* sizeof(*out
) - 1);
150 return tmp
!= UINT64_MAX
;
154 add_open_file(struct cpio_file
*file
)
156 file
->next
= open_files
;
162 remove_open_file(struct cpio_file
*file
)
164 if (file
== open_files
)
165 open_files
= file
->next
;
167 file
->prev
->next
= file
->next
;
169 if (file
->next
!= NULL
)
170 file
->next
->prev
= file
->prev
;
173 static struct cpio_file
*
174 find_open_file(int fd
)
176 struct cpio_file
*file
;
181 for (file
= open_files
; file
!= NULL
; file
= file
->next
)
189 read_ramdisk(size_t off
, size_t len
)
191 const size_t first_block_offset
= off
% DEV_BSIZE
;
194 /* return a dummy non-NULL pointer */
198 /* we have to read the stuff before the desired location as well */
199 len
+= first_block_offset
;
201 tmpfile
.fi_blocknum
= off
/ DEV_BSIZE
;
202 tmpfile
.fi_count
= P2ROUNDUP_TYPED(len
, DEV_BSIZE
, size_t);
203 tmpfile
.fi_memp
= NULL
;
205 if (diskread(&tmpfile
) != 0)
208 return tmpfile
.fi_memp
+ first_block_offset
;
212 parse_stat(const struct cpio_hdr
*hdr
, struct bootstat
*stat
)
214 if (!get_uint64(hdr
->dev
, sizeof(hdr
->dev
), &stat
->st_dev
))
216 if (!get_uint64(hdr
->ino
, sizeof(hdr
->ino
), &stat
->st_ino
))
218 if (!get_uint32(hdr
->mode
, sizeof(hdr
->mode
), &stat
->st_mode
))
220 if (!get_int32(hdr
->uid
, sizeof(hdr
->uid
), &stat
->st_uid
))
222 if (!get_int32(hdr
->gid
, sizeof(hdr
->gid
), &stat
->st_gid
))
224 if (!get_uint32(hdr
->nlink
, sizeof(hdr
->nlink
), &stat
->st_nlink
))
226 if (!get_uint64(hdr
->rdev
, sizeof(hdr
->rdev
), &stat
->st_rdev
))
229 stat
->st_mtim
.tv_nsec
= 0;
230 if (!get_int64(hdr
->mtime
, sizeof(hdr
->mtime
), &stat
->st_mtim
.tv_sec
))
233 stat
->st_atim
= stat
->st_mtim
;
234 stat
->st_ctim
= stat
->st_mtim
;
236 if (!get_uint64(hdr
->filesize
, sizeof(hdr
->filesize
), &stat
->st_size
))
239 stat
->st_blksize
= DEV_BSIZE
;
240 stat
->st_blocks
= P2ROUNDUP(stat
->st_size
, DEV_BSIZE
);
246 * Check if specified header is for a file with a specific path. If so,
247 * fill in the file struct and return 0. If not, return number of bytes to
248 * skip over to get to the next header. If an error occurs, -1 is returned.
249 * If end of archive is reached, return -2 instead.
252 scan_archive_hdr(const struct cpio_hdr
*hdr
, size_t off
,
253 struct cpio_file
*file
, const char *wanted_path
)
255 struct bootstat stat
;
261 if ((hdr
->magic
[0] != '0') || (hdr
->magic
[1] != '7') ||
262 (hdr
->magic
[2] != '0') || (hdr
->magic
[3] != '7') ||
263 (hdr
->magic
[4] != '0') || (hdr
->magic
[5] != '7'))
266 if (!get_uint32(hdr
->namesize
, sizeof(hdr
->namesize
), &namesize
))
268 if (!get_uint64(hdr
->filesize
, sizeof(hdr
->filesize
), &filesize
))
272 * We have the two sizes, let's try to read the name and file
273 * contents to make sure they are part of the ramdisk.
276 off
+= offsetof(struct cpio_hdr
, data
[0]);
277 path
= read_ramdisk(off
, namesize
);
278 data
= read_ramdisk(off
+ namesize
, filesize
);
280 /* either read failing is fatal */
281 if (path
== NULL
|| data
== NULL
)
284 if (cpio_strcmp(path
, "TRAILER!!!") == 0)
287 if (cpio_strcmp(path
, wanted_path
) != 0)
288 return offsetof(struct cpio_hdr
, data
[namesize
+ filesize
]);
291 * This is the file we want!
294 if (!parse_stat(hdr
, &stat
))
306 find_filename(char *path
, struct cpio_file
*file
)
311 * The paths in the cpio boot archive omit the leading '/'. So,
312 * skip checking for it. If the searched for path does not include
313 * the leading path (it's a relative path), fail the lookup.
320 /* now scan the archive for the relevant file */
325 const struct cpio_hdr
*hdr
;
328 hdr
= read_ramdisk(off
, sizeof(struct cpio_hdr
));
332 size
= scan_archive_hdr(hdr
, off
, file
, path
);
341 bcpio_mountroot(char *str
)
352 bcpio_unmountroot(void)
363 bcpio_open(char *path
, int flags
)
365 static int filedes
= 1;
366 struct cpio_file temp_file
;
367 struct cpio_file
*file
;
369 if (find_filename(path
, &temp_file
) != 0)
372 file
= bkmem_alloc(sizeof(struct cpio_file
));
373 file
->hdr
= temp_file
.hdr
;
374 file
->path
= temp_file
.path
;
375 file
->data
= temp_file
.data
;
376 file
->stat
= temp_file
.stat
;
377 file
->fd
= filedes
++;
388 struct cpio_file
*file
;
390 file
= find_open_file(fd
);
394 remove_open_file(file
);
396 bkmem_free(file
, sizeof(struct cpio_file
));
402 bcpio_closeall(int flag
)
404 struct cpio_file
*file
, *next
;
408 while (file
!= NULL
) {
413 if (bcpio_close(fd
) != 0)
414 printf("closeall invoked close(%d) failed\n", fd
);
421 bcpio_read(int fd
, caddr_t buf
, size_t size
)
423 struct cpio_file
*file
;
425 file
= find_open_file(fd
);
432 if (file
->off
+ size
> file
->stat
.st_size
)
433 size
= file
->stat
.st_size
- file
->off
;
435 bcopy(file
->data
+ file
->off
, buf
, size
);
443 bcpio_lseek(int fd
, off_t addr
, int whence
)
445 struct cpio_file
*file
;
447 file
= find_open_file(fd
);
459 file
->off
= file
->stat
.st_size
;
462 printf("lseek(): invalid whence value %d\n", whence
);
470 bcpio_fstat(int fd
, struct bootstat
*buf
)
472 const struct cpio_file
*file
;
474 file
= find_open_file(fd
);
483 struct boot_fs_ops bcpio_ops
= {
484 .fsw_name
= "boot_cpio",
485 .fsw_mountroot
= bcpio_mountroot
,
486 .fsw_unmountroot
= bcpio_unmountroot
,
487 .fsw_open
= bcpio_open
,
488 .fsw_close
= bcpio_close
,
489 .fsw_closeall
= bcpio_closeall
,
490 .fsw_read
= bcpio_read
,
491 .fsw_lseek
= bcpio_lseek
,
492 .fsw_fstat
= bcpio_fstat
,