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