eal: handle compressed firmware
[dpdk.git] / lib / eal / unix / eal_firmware.c
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
18 #ifdef RTE_HAS_LIBARCHIVE
19
20 struct firmware_read_ctx {
21         struct archive *a;
22 };
23
24 static int
25 firmware_open(struct firmware_read_ctx *ctx, const char *name, size_t blocksize)
26 {
27         struct archive_entry *e;
28
29         ctx->a = archive_read_new();
30         if (ctx->a == NULL)
31                 return -1;
32         if (archive_read_support_format_raw(ctx->a) != ARCHIVE_OK ||
33                         archive_read_support_filter_xz(ctx->a) != ARCHIVE_OK ||
34                         archive_read_open_filename(ctx->a, name, blocksize) != ARCHIVE_OK ||
35                         archive_read_next_header(ctx->a, &e) != ARCHIVE_OK) {
36                 archive_read_free(ctx->a);
37                 ctx->a = NULL;
38                 return -1;
39         }
40         return 0;
41 }
42
43 static ssize_t
44 firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
45 {
46         return archive_read_data(ctx->a, buf, count);
47 }
48
49 static void
50 firmware_close(struct firmware_read_ctx *ctx)
51 {
52         archive_read_free(ctx->a);
53         ctx->a = NULL;
54 }
55
56 #else /* !RTE_HAS_LIBARCHIVE */
57
58 struct firmware_read_ctx {
59         int fd;
60 };
61
62 static int
63 firmware_open(struct firmware_read_ctx *ctx, const char *name,
64         __rte_unused size_t blocksize)
65 {
66         ctx->fd = open(name, O_RDONLY);
67         if (ctx->fd < 0)
68                 return -1;
69         return 0;
70 }
71
72 static ssize_t
73 firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
74 {
75         return read(ctx->fd, buf, count);
76 }
77
78 static void
79 firmware_close(struct firmware_read_ctx *ctx)
80 {
81         close(ctx->fd);
82         ctx->fd = -1;
83 }
84
85 #endif /* !RTE_HAS_LIBARCHIVE */
86
87 static int
88 firmware_read(const char *name, void **buf, size_t *bufsz)
89 {
90         const size_t blocksize = 4096;
91         struct firmware_read_ctx ctx;
92         int ret = -1;
93         int err;
94
95         *buf = NULL;
96         *bufsz = 0;
97
98         if (firmware_open(&ctx, name, blocksize) < 0)
99                 return -1;
100
101         do {
102                 void *tmp;
103
104                 tmp = realloc(*buf, *bufsz + blocksize);
105                 if (tmp == NULL) {
106                         free(*buf);
107                         *buf = NULL;
108                         *bufsz = 0;
109                         goto out;
110                 }
111                 *buf = tmp;
112
113                 err = firmware_read_block(&ctx, RTE_PTR_ADD(*buf, *bufsz), blocksize);
114                 if (err < 0) {
115                         free(*buf);
116                         *buf = NULL;
117                         *bufsz = 0;
118                         goto out;
119                 }
120                 *bufsz += err;
121
122         } while (err != 0);
123
124         ret = 0;
125 out:
126         firmware_close(&ctx);
127         return ret;
128 }
129
130 int
131 rte_firmware_read(const char *name, void **buf, size_t *bufsz)
132 {
133         char path[PATH_MAX];
134         int ret;
135
136         ret = firmware_read(name, buf, bufsz);
137         if (ret < 0) {
138                 snprintf(path, sizeof(path), "%s.xz", name);
139                 path[PATH_MAX - 1] = '\0';
140 #ifndef RTE_HAS_LIBARCHIVE
141                 if (access(path, F_OK) == 0) {
142                         RTE_LOG(WARNING, EAL, "libarchive not linked, %s cannot be decompressed\n",
143                                 path);
144                 }
145 #else
146                 ret = firmware_read(path, buf, bufsz);
147 #endif
148         }
149         return ret;
150 }