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/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 * On mount, we parse the whole archive and instantiate a struct cpio_file
46 * for each. While this may be a bit wasteful, it makes the subsequent
47 * operations much faster and the code much simpler. The list of all files
48 * is held by 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 struct cpio_file
*next
;
61 struct cpio_file
*prev
;
64 extern void *bkmem_alloc(size_t);
65 extern void bkmem_free(void *, size_t);
67 static void cpio_closeall(int flag
);
70 static struct cpio_file
*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 file
->next
= open_files
;
164 remove_open_file(struct cpio_file
*file
)
166 if (file
== open_files
)
167 open_files
= file
->next
;
169 file
->prev
->next
= file
->next
;
171 if (file
->next
!= NULL
)
172 file
->next
->prev
= file
->prev
;
175 static struct cpio_file
*
176 find_open_file(int fd
)
178 struct cpio_file
*file
;
183 for (file
= open_files
; file
!= NULL
; file
= file
->next
)
191 read_ramdisk(size_t off
, size_t len
)
193 const size_t first_block_offset
= off
% DEV_BSIZE
;
196 /* return a dummy non-NULL pointer */
200 /* we have to read the stuff before the desired location as well */
201 len
+= first_block_offset
;
203 tmpfile
.fi_blocknum
= off
/ DEV_BSIZE
;
204 tmpfile
.fi_count
= P2ROUNDUP_TYPED(len
, DEV_BSIZE
, size_t);
205 tmpfile
.fi_memp
= NULL
;
207 if (diskread(&tmpfile
) != 0)
210 return tmpfile
.fi_memp
+ first_block_offset
;
214 parse_stat(const struct cpio_hdr
*hdr
, struct bootstat
*stat
)
216 if (!get_uint64(hdr
->dev
, sizeof(hdr
->dev
), &stat
->st_dev
))
218 if (!get_uint64(hdr
->ino
, sizeof(hdr
->ino
), &stat
->st_ino
))
220 if (!get_uint32(hdr
->mode
, sizeof(hdr
->mode
), &stat
->st_mode
))
222 if (!get_int32(hdr
->uid
, sizeof(hdr
->uid
), &stat
->st_uid
))
224 if (!get_int32(hdr
->gid
, sizeof(hdr
->gid
), &stat
->st_gid
))
226 if (!get_uint32(hdr
->nlink
, sizeof(hdr
->nlink
), &stat
->st_nlink
))
228 if (!get_uint64(hdr
->rdev
, sizeof(hdr
->rdev
), &stat
->st_rdev
))
231 stat
->st_mtim
.tv_nsec
= 0;
232 if (!get_int64(hdr
->mtime
, sizeof(hdr
->mtime
), &stat
->st_mtim
.tv_sec
))
235 stat
->st_atim
= stat
->st_mtim
;
236 stat
->st_ctim
= stat
->st_mtim
;
238 if (!get_uint64(hdr
->filesize
, sizeof(hdr
->filesize
), &stat
->st_size
))
241 stat
->st_blksize
= DEV_BSIZE
;
242 stat
->st_blocks
= P2ROUNDUP(stat
->st_size
, DEV_BSIZE
);
248 * Check if specified header is for a file with a specific path. If so,
249 * fill in the file struct and return 0. If not, return number of bytes to
250 * skip over to get to the next header. If an error occurs, -1 is returned.
251 * If end of archive is reached, return -2 instead.
254 scan_archive_hdr(const struct cpio_hdr
*hdr
, size_t off
,
255 struct cpio_file
*file
, const char *wanted_path
)
257 struct bootstat stat
;
263 if ((hdr
->magic
[0] != '0') || (hdr
->magic
[1] != '7') ||
264 (hdr
->magic
[2] != '0') || (hdr
->magic
[3] != '7') ||
265 (hdr
->magic
[4] != '0') || (hdr
->magic
[5] != '7'))
268 if (!get_uint32(hdr
->namesize
, sizeof(hdr
->namesize
), &namesize
))
270 if (!get_uint64(hdr
->filesize
, sizeof(hdr
->filesize
), &filesize
))
274 * We have the two sizes, let's try to read the name and file
275 * contents to make sure they are part of the ramdisk.
278 off
+= offsetof(struct cpio_hdr
, data
[0]);
279 path
= read_ramdisk(off
, namesize
);
280 data
= read_ramdisk(off
+ namesize
, filesize
);
282 /* either read failing is fatal */
283 if (path
== NULL
|| data
== NULL
)
286 if (cpio_strcmp(path
, "TRAILER!!!") == 0)
289 if (cpio_strcmp(path
, wanted_path
) != 0)
290 return offsetof(struct cpio_hdr
, data
[namesize
+ filesize
]);
293 * This is the file we want!
296 if (!parse_stat(hdr
, &stat
))
308 find_filename(char *path
, struct cpio_file
*file
)
313 * The paths in the cpio boot archive omit the leading '/'. So,
314 * skip checking for it. If the searched for path does not include
315 * the leading path (it's a relative path), fail the lookup.
322 /* now scan the archive for the relevant file */
327 const struct cpio_hdr
*hdr
;
330 hdr
= read_ramdisk(off
, sizeof(struct cpio_hdr
));
334 size
= scan_archive_hdr(hdr
, off
, file
, path
);
343 bcpio_mountroot(char *str
)
354 bcpio_unmountroot(void)
365 bcpio_open(char *path
, int flags
)
367 static int filedes
= 1;
368 struct cpio_file temp_file
;
369 struct cpio_file
*file
;
371 if (find_filename(path
, &temp_file
) != 0)
374 file
= bkmem_alloc(sizeof(struct cpio_file
));
375 file
->hdr
= temp_file
.hdr
;
376 file
->path
= temp_file
.path
;
377 file
->data
= temp_file
.data
;
378 file
->stat
= temp_file
.stat
;
379 file
->fd
= filedes
++;
390 struct cpio_file
*file
;
392 file
= find_open_file(fd
);
396 remove_open_file(file
);
398 bkmem_free(file
, sizeof(struct cpio_file
));
404 bcpio_closeall(int flag
)
406 struct cpio_file
*file
, *next
;
410 while (file
!= NULL
) {
415 if (bcpio_close(fd
) != 0)
416 printf("closeall invoked close(%d) failed\n", fd
);
423 bcpio_read(int fd
, caddr_t buf
, size_t size
)
425 struct cpio_file
*file
;
427 file
= find_open_file(fd
);
434 if (file
->off
+ size
> file
->stat
.st_size
)
435 size
= file
->stat
.st_size
- file
->off
;
437 bcopy(file
->data
+ file
->off
, buf
, size
);
445 bcpio_lseek(int fd
, off_t addr
, int whence
)
447 struct cpio_file
*file
;
449 file
= find_open_file(fd
);
461 file
->off
= file
->stat
.st_size
;
464 printf("lseek(): invalid whence value %d\n", whence
);
472 bcpio_fstat(int fd
, struct bootstat
*buf
)
474 const struct cpio_file
*file
;
476 file
= find_open_file(fd
);
485 struct boot_fs_ops bcpio_ops
= {
486 .fsw_name
= "boot_cpio",
487 .fsw_mountroot
= bcpio_mountroot
,
488 .fsw_unmountroot
= bcpio_unmountroot
,
489 .fsw_open
= bcpio_open
,
490 .fsw_close
= bcpio_close
,
491 .fsw_closeall
= bcpio_closeall
,
492 .fsw_read
= bcpio_read
,
493 .fsw_lseek
= bcpio_lseek
,
494 .fsw_fstat
= bcpio_fstat
,