1*4418919fSjohnjiang /* SPDX-License-Identifier: BSD-3-Clause
2*4418919fSjohnjiang * Copyright(c) 2016 RehiveTech. All rights reserved.
3*4418919fSjohnjiang */
4*4418919fSjohnjiang
5*4418919fSjohnjiang #include <stdio.h>
6*4418919fSjohnjiang #include <string.h>
7*4418919fSjohnjiang #include <errno.h>
8*4418919fSjohnjiang #include <sys/queue.h>
9*4418919fSjohnjiang
10*4418919fSjohnjiang #include <rte_debug.h>
11*4418919fSjohnjiang
12*4418919fSjohnjiang #include "resource.h"
13*4418919fSjohnjiang
14*4418919fSjohnjiang struct resource_list resource_list = TAILQ_HEAD_INITIALIZER(resource_list);
15*4418919fSjohnjiang
resource_size(const struct resource * r)16*4418919fSjohnjiang size_t resource_size(const struct resource *r)
17*4418919fSjohnjiang {
18*4418919fSjohnjiang return r->end - r->begin;
19*4418919fSjohnjiang }
20*4418919fSjohnjiang
resource_find(const char * name)21*4418919fSjohnjiang const struct resource *resource_find(const char *name)
22*4418919fSjohnjiang {
23*4418919fSjohnjiang struct resource *r;
24*4418919fSjohnjiang
25*4418919fSjohnjiang TAILQ_FOREACH(r, &resource_list, next) {
26*4418919fSjohnjiang RTE_VERIFY(r->name);
27*4418919fSjohnjiang
28*4418919fSjohnjiang if (!strcmp(r->name, name))
29*4418919fSjohnjiang return r;
30*4418919fSjohnjiang }
31*4418919fSjohnjiang
32*4418919fSjohnjiang return NULL;
33*4418919fSjohnjiang }
34*4418919fSjohnjiang
resource_fwrite(const struct resource * r,FILE * f)35*4418919fSjohnjiang int resource_fwrite(const struct resource *r, FILE *f)
36*4418919fSjohnjiang {
37*4418919fSjohnjiang const size_t goal = resource_size(r);
38*4418919fSjohnjiang size_t total = 0;
39*4418919fSjohnjiang
40*4418919fSjohnjiang while (total < goal) {
41*4418919fSjohnjiang size_t wlen = fwrite(r->begin + total, 1, goal - total, f);
42*4418919fSjohnjiang if (wlen == 0) {
43*4418919fSjohnjiang perror(__func__);
44*4418919fSjohnjiang return -1;
45*4418919fSjohnjiang }
46*4418919fSjohnjiang
47*4418919fSjohnjiang total += wlen;
48*4418919fSjohnjiang }
49*4418919fSjohnjiang
50*4418919fSjohnjiang return 0;
51*4418919fSjohnjiang }
52*4418919fSjohnjiang
resource_fwrite_file(const struct resource * r,const char * fname)53*4418919fSjohnjiang int resource_fwrite_file(const struct resource *r, const char *fname)
54*4418919fSjohnjiang {
55*4418919fSjohnjiang FILE *f;
56*4418919fSjohnjiang int ret;
57*4418919fSjohnjiang
58*4418919fSjohnjiang f = fopen(fname, "w");
59*4418919fSjohnjiang if (f == NULL) {
60*4418919fSjohnjiang perror(__func__);
61*4418919fSjohnjiang return -1;
62*4418919fSjohnjiang }
63*4418919fSjohnjiang
64*4418919fSjohnjiang ret = resource_fwrite(r, f);
65*4418919fSjohnjiang fclose(f);
66*4418919fSjohnjiang return ret;
67*4418919fSjohnjiang }
68*4418919fSjohnjiang
69*4418919fSjohnjiang #ifdef RTE_APP_TEST_RESOURCE_TAR
70*4418919fSjohnjiang #include <archive.h>
71*4418919fSjohnjiang #include <archive_entry.h>
72*4418919fSjohnjiang
do_copy(struct archive * r,struct archive * w)73*4418919fSjohnjiang static int do_copy(struct archive *r, struct archive *w)
74*4418919fSjohnjiang {
75*4418919fSjohnjiang const void *buf;
76*4418919fSjohnjiang size_t len;
77*4418919fSjohnjiang #if ARCHIVE_VERSION_NUMBER >= 3000000
78*4418919fSjohnjiang int64_t off;
79*4418919fSjohnjiang #else
80*4418919fSjohnjiang off_t off;
81*4418919fSjohnjiang #endif
82*4418919fSjohnjiang int ret;
83*4418919fSjohnjiang
84*4418919fSjohnjiang while (1) {
85*4418919fSjohnjiang ret = archive_read_data_block(r, &buf, &len, &off);
86*4418919fSjohnjiang if (ret == ARCHIVE_RETRY)
87*4418919fSjohnjiang continue;
88*4418919fSjohnjiang
89*4418919fSjohnjiang if (ret == ARCHIVE_EOF)
90*4418919fSjohnjiang return 0;
91*4418919fSjohnjiang
92*4418919fSjohnjiang if (ret != ARCHIVE_OK)
93*4418919fSjohnjiang return ret;
94*4418919fSjohnjiang
95*4418919fSjohnjiang do {
96*4418919fSjohnjiang ret = archive_write_data_block(w, buf, len, off);
97*4418919fSjohnjiang if (ret != ARCHIVE_OK && ret != ARCHIVE_RETRY)
98*4418919fSjohnjiang return ret;
99*4418919fSjohnjiang } while (ret != ARCHIVE_OK);
100*4418919fSjohnjiang }
101*4418919fSjohnjiang }
102*4418919fSjohnjiang
resource_untar(const struct resource * res)103*4418919fSjohnjiang int resource_untar(const struct resource *res)
104*4418919fSjohnjiang {
105*4418919fSjohnjiang struct archive *r;
106*4418919fSjohnjiang struct archive *w;
107*4418919fSjohnjiang struct archive_entry *e;
108*4418919fSjohnjiang void *p;
109*4418919fSjohnjiang int flags = 0;
110*4418919fSjohnjiang int ret;
111*4418919fSjohnjiang
112*4418919fSjohnjiang p = malloc(resource_size(res));
113*4418919fSjohnjiang if (p == NULL)
114*4418919fSjohnjiang rte_panic("Failed to malloc %zu B\n", resource_size(res));
115*4418919fSjohnjiang
116*4418919fSjohnjiang memcpy(p, res->begin, resource_size(res));
117*4418919fSjohnjiang
118*4418919fSjohnjiang r = archive_read_new();
119*4418919fSjohnjiang if (r == NULL) {
120*4418919fSjohnjiang free(p);
121*4418919fSjohnjiang return -1;
122*4418919fSjohnjiang }
123*4418919fSjohnjiang
124*4418919fSjohnjiang archive_read_support_format_all(r);
125*4418919fSjohnjiang archive_read_support_filter_all(r);
126*4418919fSjohnjiang
127*4418919fSjohnjiang w = archive_write_disk_new();
128*4418919fSjohnjiang if (w == NULL) {
129*4418919fSjohnjiang archive_read_free(r);
130*4418919fSjohnjiang free(p);
131*4418919fSjohnjiang return -1;
132*4418919fSjohnjiang }
133*4418919fSjohnjiang
134*4418919fSjohnjiang flags |= ARCHIVE_EXTRACT_PERM;
135*4418919fSjohnjiang flags |= ARCHIVE_EXTRACT_FFLAGS;
136*4418919fSjohnjiang archive_write_disk_set_options(w, flags);
137*4418919fSjohnjiang archive_write_disk_set_standard_lookup(w);
138*4418919fSjohnjiang
139*4418919fSjohnjiang ret = archive_read_open_memory(r, p, resource_size(res));
140*4418919fSjohnjiang if (ret != ARCHIVE_OK)
141*4418919fSjohnjiang goto fail;
142*4418919fSjohnjiang
143*4418919fSjohnjiang while (1) {
144*4418919fSjohnjiang ret = archive_read_next_header(r, &e);
145*4418919fSjohnjiang if (ret == ARCHIVE_EOF)
146*4418919fSjohnjiang break;
147*4418919fSjohnjiang if (ret != ARCHIVE_OK)
148*4418919fSjohnjiang goto fail;
149*4418919fSjohnjiang
150*4418919fSjohnjiang ret = archive_write_header(w, e);
151*4418919fSjohnjiang if (ret == ARCHIVE_EOF)
152*4418919fSjohnjiang break;
153*4418919fSjohnjiang if (ret != ARCHIVE_OK)
154*4418919fSjohnjiang goto fail;
155*4418919fSjohnjiang
156*4418919fSjohnjiang if (archive_entry_size(e) == 0)
157*4418919fSjohnjiang continue;
158*4418919fSjohnjiang
159*4418919fSjohnjiang ret = do_copy(r, w);
160*4418919fSjohnjiang if (ret != ARCHIVE_OK)
161*4418919fSjohnjiang goto fail;
162*4418919fSjohnjiang
163*4418919fSjohnjiang ret = archive_write_finish_entry(w);
164*4418919fSjohnjiang if (ret != ARCHIVE_OK)
165*4418919fSjohnjiang goto fail;
166*4418919fSjohnjiang }
167*4418919fSjohnjiang
168*4418919fSjohnjiang archive_write_free(w);
169*4418919fSjohnjiang archive_read_free(r);
170*4418919fSjohnjiang free(p);
171*4418919fSjohnjiang return 0;
172*4418919fSjohnjiang
173*4418919fSjohnjiang fail:
174*4418919fSjohnjiang archive_write_free(w);
175*4418919fSjohnjiang archive_read_free(r);
176*4418919fSjohnjiang free(p);
177*4418919fSjohnjiang rte_panic("Failed: %s\n", archive_error_string(r));
178*4418919fSjohnjiang return -1;
179*4418919fSjohnjiang }
180*4418919fSjohnjiang
resource_rm_by_tar(const struct resource * res)181*4418919fSjohnjiang int resource_rm_by_tar(const struct resource *res)
182*4418919fSjohnjiang {
183*4418919fSjohnjiang struct archive *r;
184*4418919fSjohnjiang struct archive_entry *e;
185*4418919fSjohnjiang void *p;
186*4418919fSjohnjiang int try_again = 1;
187*4418919fSjohnjiang int attempts = 0;
188*4418919fSjohnjiang int ret;
189*4418919fSjohnjiang
190*4418919fSjohnjiang p = malloc(resource_size(res));
191*4418919fSjohnjiang if (p == NULL)
192*4418919fSjohnjiang rte_panic("Failed to malloc %zu B\n", resource_size(res));
193*4418919fSjohnjiang
194*4418919fSjohnjiang memcpy(p, res->begin, resource_size(res));
195*4418919fSjohnjiang
196*4418919fSjohnjiang /*
197*4418919fSjohnjiang * If somebody creates a file somewhere inside the extracted TAR
198*4418919fSjohnjiang * hierarchy during a test the resource_rm_by_tar might loop
199*4418919fSjohnjiang * infinitely. We prevent this by adding the attempts counter there.
200*4418919fSjohnjiang * In normal case, max N iteration is done where N is the depth of
201*4418919fSjohnjiang * the file-hierarchy.
202*4418919fSjohnjiang */
203*4418919fSjohnjiang while (try_again && attempts < 10000) {
204*4418919fSjohnjiang r = archive_read_new();
205*4418919fSjohnjiang if (r == NULL) {
206*4418919fSjohnjiang free(p);
207*4418919fSjohnjiang return -1;
208*4418919fSjohnjiang }
209*4418919fSjohnjiang
210*4418919fSjohnjiang archive_read_support_format_all(r);
211*4418919fSjohnjiang archive_read_support_filter_all(r);
212*4418919fSjohnjiang
213*4418919fSjohnjiang ret = archive_read_open_memory(r, p, resource_size(res));
214*4418919fSjohnjiang if (ret != ARCHIVE_OK) {
215*4418919fSjohnjiang fprintf(stderr, "Failed: %s\n",
216*4418919fSjohnjiang archive_error_string(r));
217*4418919fSjohnjiang goto fail;
218*4418919fSjohnjiang }
219*4418919fSjohnjiang
220*4418919fSjohnjiang try_again = 0;
221*4418919fSjohnjiang
222*4418919fSjohnjiang while (1) {
223*4418919fSjohnjiang ret = archive_read_next_header(r, &e);
224*4418919fSjohnjiang if (ret == ARCHIVE_EOF)
225*4418919fSjohnjiang break;
226*4418919fSjohnjiang if (ret != ARCHIVE_OK)
227*4418919fSjohnjiang goto fail;
228*4418919fSjohnjiang
229*4418919fSjohnjiang ret = remove(archive_entry_pathname(e));
230*4418919fSjohnjiang if (ret < 0) {
231*4418919fSjohnjiang switch (errno) {
232*4418919fSjohnjiang case ENOTEMPTY:
233*4418919fSjohnjiang case EEXIST:
234*4418919fSjohnjiang try_again = 1;
235*4418919fSjohnjiang break;
236*4418919fSjohnjiang
237*4418919fSjohnjiang /* should not usually happen: */
238*4418919fSjohnjiang case ENOENT:
239*4418919fSjohnjiang case ENOTDIR:
240*4418919fSjohnjiang case EROFS:
241*4418919fSjohnjiang attempts += 1;
242*4418919fSjohnjiang continue;
243*4418919fSjohnjiang default:
244*4418919fSjohnjiang perror("Failed to remove file");
245*4418919fSjohnjiang goto fail;
246*4418919fSjohnjiang }
247*4418919fSjohnjiang }
248*4418919fSjohnjiang }
249*4418919fSjohnjiang
250*4418919fSjohnjiang archive_read_free(r);
251*4418919fSjohnjiang attempts += 1;
252*4418919fSjohnjiang }
253*4418919fSjohnjiang
254*4418919fSjohnjiang if (attempts >= 10000) {
255*4418919fSjohnjiang fprintf(stderr, "Failed to remove archive\n");
256*4418919fSjohnjiang free(p);
257*4418919fSjohnjiang return -1;
258*4418919fSjohnjiang }
259*4418919fSjohnjiang
260*4418919fSjohnjiang free(p);
261*4418919fSjohnjiang return 0;
262*4418919fSjohnjiang
263*4418919fSjohnjiang fail:
264*4418919fSjohnjiang archive_read_free(r);
265*4418919fSjohnjiang free(p);
266*4418919fSjohnjiang
267*4418919fSjohnjiang rte_panic("Failed: %s\n", archive_error_string(r));
268*4418919fSjohnjiang return -1;
269*4418919fSjohnjiang }
270*4418919fSjohnjiang
271*4418919fSjohnjiang #endif /* RTE_APP_TEST_RESOURCE_TAR */
272*4418919fSjohnjiang
resource_register(struct resource * r)273*4418919fSjohnjiang void resource_register(struct resource *r)
274*4418919fSjohnjiang {
275*4418919fSjohnjiang TAILQ_INSERT_TAIL(&resource_list, r, next);
276*4418919fSjohnjiang }
277