archive.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 #include "archive.h" 4 #include <endian.h> 5 #include <errno.h> 6 #include <libgen.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 static struct directory *archive; 13 14 static void usage(void) 15 { 16 printf("Name:\n"); 17 printf(" archive - concatenate files and create an archive\n"); 18 printf("Usage:\n"); 19 printf(" archive archive_name create file0 file1 ...\n"); 20 } 21 22 static int get_file_size(const char *file) 23 { 24 FILE *fp = fopen(file, "rb"); 25 int size; 26 27 if (!fp) { 28 fprintf(stderr, "Error: failed to open %s\n", file); 29 return -1; 30 } 31 fseek(fp, 0, SEEK_END); 32 size = ftell(fp); 33 fclose(fp); 34 if (size < 0) { 35 fprintf(stderr, "Error: failed to get file size\n"); 36 return -1; 37 } 38 39 return size; 40 } 41 42 static int set_file_name(const char *path, struct dentry *dest) 43 { 44 struct dentry *entry; 45 char *name, *copy; 46 uint32_t i; 47 48 copy = strdup(path); 49 name = basename(copy); 50 51 /* check name length */ 52 if (strlen(name) > NAME_LENGTH) { 53 fprintf(stderr, "Error: file name '%s' exceeds %d chars\n", 54 name, NAME_LENGTH); 55 free(copy); 56 return -1; 57 } 58 59 /* check if there is a duplicate name */ 60 entry = get_first_dentry(archive); 61 for (i = 0; i < archive->count && &entry[i] != dest; i++) { 62 if (!strncmp(entry[i].name, name, NAME_LENGTH)) { 63 fprintf(stderr, "Error: duplicate name '%s'\n", name); 64 free(copy); 65 return -1; 66 } 67 } 68 69 /* copy the name to the entry */ 70 strncpy(dest->name, name, NAME_LENGTH); 71 free(copy); 72 73 return 0; 74 } 75 76 /* 77 * Add a file to the archive in RAM 78 * 79 * path: path to the file to be added 80 * entry: pointer to struct dentry where file header is created 81 * offset: offset of the file contents from the archive header 82 * 83 * return: 0 on success or -1 on error 84 */ 85 static int add_file(const char *path, struct dentry *entry, uint32_t offset) 86 { 87 FILE *fp; 88 int size; 89 90 if (!path || !*path || !entry) { 91 fprintf(stderr, "Error: invalid path or entry\n"); 92 return -1; 93 } 94 95 size = get_file_size(path); 96 if (size < 0) 97 return -1; 98 if (offset + size > archive->size) { 99 fprintf(stderr, "Error: invalid offset or size\n"); 100 return -1; 101 } 102 103 fp = fopen(path, "rb"); 104 if (!fp) { 105 fprintf(stderr, "Error: failed to open %s (%d: %s)\n", 106 path, errno, strerror(errno)); 107 return -1; 108 } 109 if (fread((char *)archive + offset, sizeof(char), size, fp) != (size_t)size) { 110 fprintf(stderr, "Error: failed to read %s\n", path); 111 fclose(fp); 112 return -1; 113 } 114 fclose(fp); 115 116 /* set file name*/ 117 if (set_file_name(path, entry)) 118 return -1; 119 120 entry->offset = offset; 121 entry->size = size; 122 123 return 0; 124 } 125 126 /* 127 * Allocate memory for archive 128 * 129 * count: number of files to add 130 * files: pointer to the array of file names 131 * 132 * return: 0 on success or -1 on error 133 */ 134 static int setup_archive(int count, const char **files) 135 { 136 uint32_t size; 137 int i, s; 138 139 size = sizeof(*archive); 140 for (i = 0; i < count; i++) { 141 s = get_file_size(files[i]); 142 if (s < 0) 143 return -1; 144 size += sizeof(struct dentry); 145 size += s; 146 } 147 148 archive = calloc(size, 1); 149 if (!archive) { 150 fprintf(stderr, "Error: failed to allocate memory\n"); 151 return -1; 152 } 153 154 /* install magic string */ 155 memcpy(archive->magic, CBAR_MAGIC, sizeof(archive->magic)); 156 archive->version = VERSION; 157 archive->size = size; 158 archive->count = count; 159 160 printf("Set up archive: size=%d count=%d\n", size, count); 161 162 return 0; 163 } 164 165 /* 166 * Store files in archive 167 */ 168 static int archive_files(const char **files) 169 { 170 struct dentry *entry; 171 uint32_t offset; 172 uint32_t i; 173 174 entry = get_first_dentry(archive); 175 offset = get_first_offset(archive); 176 for (i = 0; i < archive->count; i++) { 177 if (add_file(files[i], entry, offset)) 178 return -1; 179 offset += entry->size; 180 entry++; 181 } 182 183 return 0; 184 } 185 186 static void convert_endian(void) 187 { 188 struct dentry *entry; 189 uint32_t i; 190 191 entry = get_first_dentry(archive); 192 for (i = 0; i < archive->count; i++) { 193 entry[i].offset = htole32(entry[i].offset); 194 entry[i].size = htole32(entry[i].size); 195 } 196 197 archive->version = htole32(archive->version); 198 archive->size = htole32(archive->size); 199 archive->count = htole32(archive->count); 200 } 201 202 /* 203 * Write archive to file 204 */ 205 static int output_archive(const char *path) 206 { 207 FILE *fp; 208 209 convert_endian(); 210 211 fp = fopen(path, "wb"); 212 if (!fp) { 213 fprintf(stderr, "Error: failed to open %s\n", path); 214 fclose(fp); 215 return -1; 216 } 217 if (fwrite(archive, sizeof(char), archive->size, fp) != archive->size) { 218 fprintf(stderr, "Error: failed to write to %s\n", path); 219 fclose(fp); 220 return -1; 221 } 222 fclose(fp); 223 printf("Wrote archive to %s\n", path); 224 225 return 0; 226 } 227 228 static int cmd_create(const char *archive_path, int count, const char **files) 229 { 230 if (count < 1 || !files) { 231 fprintf(stderr, "Error: no input files specified\n"); 232 return -1; 233 } 234 235 if (setup_archive(count, files)) 236 return -1; 237 238 if (archive_files(files)) 239 return -1; 240 241 if (output_archive(archive_path)) 242 return -1; 243 244 return 0; 245 } 246 247 int main(int argc, const char *argv[]) 248 { 249 const char *command; 250 251 if (argc < 3) { 252 fprintf(stderr, "Error: invalid number of arguments\n"); 253 usage(); 254 return -1; 255 } 256 257 command = argv[2]; 258 259 /* branch by command name */ 260 if (!strncmp(command, "create", sizeof("create"))) { 261 if (cmd_create(argv[1], argc - 3, &argv[3])) 262 return -1; 263 } else { 264 fprintf(stderr, "Error: invalid command: %s\n", command); 265 return -1; 266 } 267 268 return 0; 269 }