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