1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <cpio/cpio.h> 14 15#ifndef NULL 16#define NULL ((void *)0) 17#endif 18 19struct cpio_header_info { 20 const char *filename; 21 unsigned long filesize; 22 void *data; 23 struct cpio_header *next; 24}; 25 26/* Align 'n' up to the value 'align', which must be a power of two. */ 27static unsigned long align_up(unsigned long n, unsigned long align) 28{ 29 return (n + align - 1) & (~(align - 1)); 30} 31 32/* Parse an ASCII hex string into an integer. */ 33static unsigned long parse_hex_str(char *s, unsigned int max_len) 34{ 35 unsigned long r = 0; 36 unsigned long i; 37 38 for (i = 0; i < max_len; i++) { 39 r *= 16; 40 if (s[i] >= '0' && s[i] <= '9') { 41 r += s[i] - '0'; 42 } else if (s[i] >= 'a' && s[i] <= 'f') { 43 r += s[i] - 'a' + 10; 44 } else if (s[i] >= 'A' && s[i] <= 'F') { 45 r += s[i] - 'A' + 10; 46 } else { 47 return r; 48 } 49 continue; 50 } 51 return r; 52} 53 54/* 55 * Compare up to 'n' characters in a string. 56 * 57 * We re-implement the wheel to avoid dependencies on 'libc', required for 58 * certain environments that are particularly impoverished. 59 */ 60static int cpio_strncmp(const char *a, const char *b, unsigned long n) 61{ 62 unsigned long i; 63 for (i = 0; i < n; i++) { 64 if (a[i] != b[i]) { 65 return a[i] - b[i]; 66 } 67 if (a[i] == 0) { 68 return 0; 69 } 70 } 71 return 0; 72} 73 74/** 75 * This is an implementation of string copy because, cpi doesn't want to 76 * use string.h. 77 */ 78static char* cpio_strcpy(char *to, const char *from) { 79 char *save = to; 80 while (*from != 0) { 81 *to = *from; 82 to++; 83 from++; 84 } 85 return save; 86} 87 88static unsigned int cpio_strlen(const char *str) { 89 const char *s; 90 for (s = str; *s; ++s) {} 91 return (s - str); 92} 93 94/* Calculate the remaining length in a CPIO file after reading a header. */ 95static unsigned long cpio_len_next(unsigned long len, void *prev, void *next) { 96 unsigned long diff = (unsigned long) (next - prev); 97 if (len < diff) { 98 return 0; 99 } 100 return len; 101} 102 103/* 104 * Parse the header of the given CPIO entry. 105 * 106 * Return -1 if the header is not valid, 1 if it is EOF. 107 */ 108int cpio_parse_header(struct cpio_header *archive, unsigned long len, 109 struct cpio_header_info *info) 110{ 111 const char *filename; 112 unsigned long filesize; 113 unsigned long filename_length; 114 void *data; 115 struct cpio_header *next; 116 117 /* Ensure header is accessible */ 118 if (len < sizeof(struct cpio_header)) { 119 return -1; 120 } 121 122 /* Ensure magic header exists. */ 123 if (cpio_strncmp(archive->c_magic, CPIO_HEADER_MAGIC, sizeof(archive->c_magic)) != 0) { 124 return -1; 125 } 126 127 /* Get filename and file size. */ 128 filesize = parse_hex_str(archive->c_filesize, sizeof(archive->c_filesize)); 129 filename_length = parse_hex_str(archive->c_namesize, sizeof(archive->c_namesize)); 130 131 /* Ensure header + filename + file contents are accessible */ 132 if (len < sizeof(struct cpio_header) + filename_length + filesize) { 133 return -1; 134 } 135 136 filename = (char *) archive + sizeof(struct cpio_header); 137 /* Ensure filename is terminated */ 138 if (filename[filename_length - 1] != 0) { 139 return -1; 140 } 141 142 /* Ensure filename is not the trailer indicating EOF. */ 143 if (filename_length >= sizeof(CPIO_FOOTER_MAGIC) && cpio_strncmp(filename, 144 CPIO_FOOTER_MAGIC, sizeof(CPIO_FOOTER_MAGIC)) == 0) { 145 return 1; 146 } 147 148 /* Find offset to data. */ 149 data = (void *) align_up((unsigned long) archive + sizeof(struct cpio_header) + 150 filename_length, CPIO_ALIGNMENT); 151 next = (struct cpio_header *) align_up((unsigned long) data + filesize, CPIO_ALIGNMENT); 152 153 if (info) { 154 info->filename = filename; 155 info->filesize = filesize; 156 info->data = data; 157 info->next = next; 158 } 159 return 0; 160} 161 162/* 163 * Get the location of the data in the n'th entry in the given archive file. 164 * 165 * We also return a pointer to the name of the file (not NUL terminated). 166 * 167 * Return NULL if the n'th entry doesn't exist. 168 * 169 * Runs in O(n) time. 170 */ 171void *cpio_get_entry(void *archive, unsigned long len, int n, const char **name, unsigned long *size) 172{ 173 struct cpio_header *header = archive; 174 struct cpio_header_info header_info; 175 176 /* Find n'th entry. */ 177 for (int i = 0; i <= n; i++) { 178 int error = cpio_parse_header(header, len, &header_info); 179 if (error) { 180 return NULL; 181 } 182 len = cpio_len_next(len, header, header_info.next); 183 header = header_info.next; 184 } 185 186 if (name) { 187 *name = header_info.filename; 188 } 189 if (size) { 190 *size = header_info.filesize; 191 } 192 return header_info.data; 193} 194 195/* 196 * Find the location and size of the file named "name" in the given 'cpio' 197 * archive. 198 * 199 * Return NULL if the entry doesn't exist. 200 * 201 * Runs in O(n) time. 202 */ 203void *cpio_get_file(void *archive, unsigned long len, const char *name, unsigned long *size) 204{ 205 struct cpio_header *header = archive; 206 struct cpio_header_info header_info; 207 208 /* Find n'th entry. */ 209 while (1) { 210 int error = cpio_parse_header(header, len, &header_info); 211 if (error) { 212 return NULL; 213 } 214 if (cpio_strncmp(header_info.filename, name, -1) == 0) { 215 break; 216 } 217 len = cpio_len_next(len, header, header_info.next); 218 header = header_info.next; 219 } 220 221 if (size) { 222 *size = header_info.filesize; 223 } 224 return header_info.data; 225} 226 227int cpio_info(void *archive, unsigned long len, struct cpio_info *info) { 228 struct cpio_header *header; 229 unsigned long current_path_sz; 230 struct cpio_header_info header_info; 231 232 if (info == NULL) return 1; 233 info->file_count = 0; 234 info->max_path_sz = 0; 235 236 header = archive; 237 while (1) { 238 int error = cpio_parse_header(header, len, &header_info); 239 if (error == -1) { 240 return error; 241 } else if (error == 1) { 242 /* EOF */ 243 return 0; 244 } 245 info->file_count++; 246 len = cpio_len_next(len, header, header_info.next); 247 header = header_info.next; 248 249 // Check if this is the maximum file path size. 250 current_path_sz = cpio_strlen(header_info.filename); 251 if (current_path_sz > info->max_path_sz) { 252 info->max_path_sz = current_path_sz; 253 } 254 } 255 256 return 0; 257} 258 259void cpio_ls(void *archive, unsigned long len, char **buf, unsigned long buf_len) { 260 struct cpio_header *header; 261 struct cpio_header_info header_info; 262 263 header = archive; 264 for (unsigned long i = 0; i < buf_len; i++) { 265 int error = cpio_parse_header(header, len, &header_info); 266 // Break on an error or nothing left to read. 267 if (error) break; 268 cpio_strcpy(buf[i], header_info.filename); 269 len = cpio_len_next(len, header, header_info.next); 270 header = header_info.next; 271 } 272} 273