1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2021 Red Hat, Inc.
3 */
4
5 #ifdef RTE_HAS_LIBARCHIVE
6 #include <archive.h>
7 #endif
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12
13 #include <rte_common.h>
14 #include <rte_log.h>
15
16 #include "eal_firmware.h"
17 #include "eal_private.h"
18
19 static const char * const compression_suffixes[] = { "xz", "zst" };
20
21 #ifdef RTE_HAS_LIBARCHIVE
22
23 struct firmware_read_ctx {
24 struct archive *a;
25 };
26
27 static int
firmware_open(struct firmware_read_ctx * ctx,const char * name,size_t blocksize)28 firmware_open(struct firmware_read_ctx *ctx, const char *name, size_t blocksize)
29 {
30 struct archive_entry *e;
31 int err;
32
33 ctx->a = archive_read_new();
34 if (ctx->a == NULL)
35 return -1;
36
37 if (archive_read_support_format_raw(ctx->a) != ARCHIVE_OK)
38 goto error;
39
40 err = archive_read_support_filter_xz(ctx->a);
41 if (err != ARCHIVE_OK && err != ARCHIVE_WARN)
42 EAL_LOG(DEBUG, "could not initialise libarchive for xz compression");
43
44 err = archive_read_support_filter_zstd(ctx->a);
45 if (err != ARCHIVE_OK && err != ARCHIVE_WARN)
46 EAL_LOG(DEBUG, "could not initialise libarchive for zstd compression");
47
48 if (archive_read_open_filename(ctx->a, name, blocksize) != ARCHIVE_OK)
49 goto error;
50
51 if (archive_read_next_header(ctx->a, &e) != ARCHIVE_OK)
52 goto error;
53
54 return 0;
55
56 error:
57 archive_read_free(ctx->a);
58 ctx->a = NULL;
59 return -1;
60 }
61
62 static ssize_t
firmware_read_block(struct firmware_read_ctx * ctx,void * buf,size_t count)63 firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
64 {
65 return archive_read_data(ctx->a, buf, count);
66 }
67
68 static void
firmware_close(struct firmware_read_ctx * ctx)69 firmware_close(struct firmware_read_ctx *ctx)
70 {
71 archive_read_free(ctx->a);
72 ctx->a = NULL;
73 }
74
75 #else /* !RTE_HAS_LIBARCHIVE */
76
77 struct firmware_read_ctx {
78 int fd;
79 };
80
81 static int
firmware_open(struct firmware_read_ctx * ctx,const char * name,__rte_unused size_t blocksize)82 firmware_open(struct firmware_read_ctx *ctx, const char *name,
83 __rte_unused size_t blocksize)
84 {
85 ctx->fd = open(name, O_RDONLY);
86 if (ctx->fd < 0)
87 return -1;
88 return 0;
89 }
90
91 static ssize_t
firmware_read_block(struct firmware_read_ctx * ctx,void * buf,size_t count)92 firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
93 {
94 return read(ctx->fd, buf, count);
95 }
96
97 static void
firmware_close(struct firmware_read_ctx * ctx)98 firmware_close(struct firmware_read_ctx *ctx)
99 {
100 close(ctx->fd);
101 ctx->fd = -1;
102 }
103
104 #endif /* !RTE_HAS_LIBARCHIVE */
105
106 static int
firmware_read(const char * name,void ** buf,size_t * bufsz)107 firmware_read(const char *name, void **buf, size_t *bufsz)
108 {
109 const size_t blocksize = 4096;
110 struct firmware_read_ctx ctx;
111 int ret = -1;
112 int err;
113
114 *buf = NULL;
115 *bufsz = 0;
116
117 if (firmware_open(&ctx, name, blocksize) < 0)
118 return -1;
119
120 do {
121 void *tmp;
122
123 tmp = realloc(*buf, *bufsz + blocksize);
124 if (tmp == NULL) {
125 free(*buf);
126 *buf = NULL;
127 *bufsz = 0;
128 goto out;
129 }
130 *buf = tmp;
131
132 err = firmware_read_block(&ctx, RTE_PTR_ADD(*buf, *bufsz), blocksize);
133 if (err < 0) {
134 free(*buf);
135 *buf = NULL;
136 *bufsz = 0;
137 goto out;
138 }
139 *bufsz += err;
140
141 } while (err != 0);
142
143 ret = 0;
144 out:
145 firmware_close(&ctx);
146 return ret;
147 }
148
149 int
rte_firmware_read(const char * name,void ** buf,size_t * bufsz)150 rte_firmware_read(const char *name, void **buf, size_t *bufsz)
151 {
152 char path[PATH_MAX];
153 int ret;
154
155 ret = firmware_read(name, buf, bufsz);
156 if (ret < 0) {
157 unsigned int i;
158
159 for (i = 0; i < RTE_DIM(compression_suffixes); i++) {
160 snprintf(path, sizeof(path), "%s.%s", name, compression_suffixes[i]);
161 path[PATH_MAX - 1] = '\0';
162 if (access(path, F_OK) != 0)
163 continue;
164 #ifndef RTE_HAS_LIBARCHIVE
165 EAL_LOG(WARNING, "libarchive not linked, %s cannot be decompressed",
166 path);
167 #else
168 ret = firmware_read(path, buf, bufsz);
169 #endif
170 break;
171 }
172 }
173 return ret;
174 }
175