1 /*
2  * Copyright 2018 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/cpio.h"
10 
11 #include <stdint.h>
12 
13 #include "hf/dlog.h"
14 #include "hf/std.h"
15 
16 #define CPIO_OLD_BINARY_FORMAT_MAGIC 070707
17 
18 #pragma pack(push, 1)
19 struct cpio_header {
20 	uint16_t magic;
21 	uint16_t dev;
22 	uint16_t ino;
23 	uint16_t mode;
24 	uint16_t uid;
25 	uint16_t gid;
26 	uint16_t nlink;
27 	uint16_t rdev;
28 	uint16_t mtime[2];
29 	uint16_t namesize;
30 	uint16_t filesize[2];
31 };
32 #pragma pack(pop)
33 
34 /**
35  * Retrieves the next file stored in the cpio archive stored in the cpio, and
36  * advances the iterator such that another call to this function would return
37  * the following file.
38  */
cpio_next(struct memiter * iter,const char ** name,const void ** contents,size_t * size)39 static bool cpio_next(struct memiter *iter, const char **name,
40 		      const void **contents, size_t *size)
41 {
42 	static const char trailer[] = "TRAILER!!!";
43 	size_t len;
44 	struct memiter lit;
45 	const struct cpio_header *h;
46 
47 	if (!iter) {
48 		return false;
49 	}
50 
51 	lit = *iter;
52 
53 	h = (const struct cpio_header *)lit.next;
54 	if (!h) {
55 		return false;
56 	}
57 
58 	if (!memiter_advance(&lit, sizeof(struct cpio_header))) {
59 		return false;
60 	}
61 
62 	if (h->magic != CPIO_OLD_BINARY_FORMAT_MAGIC) {
63 		dlog_error("cpio: only old binary format is supported\n");
64 		return false;
65 	}
66 
67 	*name = lit.next;
68 
69 	len = (h->namesize + 1) & ~1;
70 	if (!memiter_advance(&lit, len)) {
71 		return false;
72 	}
73 
74 	/* previous memiter_advance checks for boundaries */
75 	if (h->namesize == 0U || (*name)[h->namesize - 1] != '\0') {
76 		return false;
77 	}
78 
79 	*contents = lit.next;
80 
81 	len = (size_t)h->filesize[0] << 16 | h->filesize[1];
82 	if (!memiter_advance(&lit, (len + 1) & ~1)) {
83 		return false;
84 	}
85 
86 	/* Stop enumerating files when we hit the end marker. */
87 	if (!strncmp(*name, trailer, sizeof(trailer))) {
88 		return false;
89 	}
90 
91 	*size = len;
92 	*iter = lit;
93 
94 	return true;
95 }
96 
97 /**
98  * Looks for a file in the given cpio archive. The file, if found, is returned
99  * in the "it" argument.
100  */
cpio_get_file(const struct memiter * cpio,const struct string * name,struct memiter * it)101 bool cpio_get_file(const struct memiter *cpio, const struct string *name,
102 		   struct memiter *it)
103 {
104 	const char *fname;
105 	const void *fcontents;
106 	size_t fsize;
107 	struct memiter iter = *cpio;
108 
109 	while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
110 		if (!strncmp(fname, string_data(name), STRING_MAX_SIZE)) {
111 			memiter_init(it, fcontents, fsize);
112 			return true;
113 		}
114 	}
115 
116 	return false;
117 }
118