1 #if HAVE_NBTOOL_CONFIG_H 2 #include "nbtool_config.h" 3 #else 4 #include <sys/cdefs.h> 5 #endif 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <errno.h> 11 #include <assert.h> 12 13 #include <getopt.h> 14 #include <unistd.h> 15 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 #include <fcntl.h> 19 20 #include "pack_dev.h" 21 22 #define MAX_ENTRIES 100000 23 #define MAX_LINE_SIZE 0xfff 24 25 26 /* 27 * Tool to convert the netbsd METALOG into a proto file usable by mkfs.mfs 28 * 29 * todo: 30 * Possibly use netbsd usr.sbin/makefs to create mfs file systems. 31 */ 32 33 enum entry_type 34 { ENTRY_DIR, ENTRY_FILE, ENTRY_LINK, ENTRY_BLOCK, ENTRY_CHAR }; 35 36 37 struct entry 38 { 39 char *path; 40 char *filename; /* point to last component in the path */ 41 enum entry_type type; /* entry type */ 42 int mode; /* unix mode e.g. 0755 */ 43 char *uid; 44 char *gid; 45 char *time; /* time 1365836670.000000000 */ 46 char *size; 47 char *link; 48 /* Can't use devmajor_t/devminor_t on linux systems :( */ 49 int32_t dev_major; 50 int32_t dev_minor; 51 52 /* just internal variables used to create a tree */ 53 int depth; 54 struct entry *parent; 55 56 }; 57 58 static struct entry entries[MAX_ENTRIES]; 59 static int entry_total_count; 60 61 static int 62 convert_to_entry(char *line, struct entry *entry) 63 { 64 /* convert a input line from sanitized input into an entry */ 65 char *saveptr; 66 char *key, *value; 67 68 saveptr = NULL; 69 70 /* we need to have a terminated string */ 71 assert(strnlen(line, MAX_LINE_SIZE - 1) != MAX_LINE_SIZE - 1); 72 73 /* skip comment lines */ 74 if (*line == '#') 75 return 1; 76 77 line = strtok_r(line, " ", &saveptr); 78 79 /* skip empty lines */ 80 if (!line) { 81 return 1; 82 } 83 84 memset(entry, 0, sizeof(struct entry)); 85 86 /* the first entry is the path name */ 87 entry->path = strndup(line, MAX_LINE_SIZE); 88 89 /* the next entries are key,value pairs */ 90 while ((line = strtok_r(NULL, " ", &saveptr)) != NULL) { 91 key = value = NULL; 92 char *p; 93 if (strstr(line, "=") == NULL) { 94 fprintf(stderr, "expected key/value pair in %s\n", 95 line); 96 free(entry->path); 97 return 1; 98 } 99 p = NULL; 100 key = strtok_r(line, "=", &p); 101 value = strtok_r(NULL, "=", &p); 102 if (value) { 103 if (strncmp(key, "type", 5) == 0) { 104 if (strncmp(value, "dir", 4) == 0) { 105 entry->type = ENTRY_DIR; 106 } else if (strncmp(value, "file", 5) == 0) { 107 entry->type = ENTRY_FILE; 108 } else if (strncmp(value, "link", 5) == 0) { 109 entry->type = ENTRY_LINK; 110 } else if (strncmp(value, "block", 6) == 0) { 111 entry->type = ENTRY_BLOCK; 112 } else if (strncmp(value, "char", 5) == 0) { 113 entry->type = ENTRY_CHAR; 114 } else { 115 fprintf(stderr, 116 "\tunknown type %s -> '%s'\n", key, 117 value); 118 } 119 } else if (strncmp(key, "mode", 5) == 0) { 120 sscanf(value,"%o",&entry->mode); 121 } else if (strncmp(key, "uid", 4) == 0) { 122 entry->uid = strndup(value, MAX_LINE_SIZE); 123 } else if (strncmp(key, "gid", 4) == 0) { 124 entry->gid = strndup(value, MAX_LINE_SIZE); 125 } else if (strncmp(key, "time", 5) == 0) { 126 entry->time = strndup(value, MAX_LINE_SIZE); 127 } else if (strncmp(key, "size", 5) == 0) { 128 entry->size = strndup(value, MAX_LINE_SIZE); 129 } else if (strncmp(key, "link", 5) == 0) { 130 entry->link = strndup(value, MAX_LINE_SIZE); 131 } else if (strncmp(key, "device", 7) == 0) { 132 long int dev_id; 133 dev_id = strtoul(value, NULL, 16); 134 entry->dev_major = major_netbsd(dev_id); 135 entry->dev_minor = minor_netbsd(dev_id); 136 } else { 137 fprintf(stderr, 138 "\tunknown attribute %s -> %s\n", key, 139 value); 140 } 141 } 142 } 143 return 0; 144 } 145 146 static int 147 iterate_over_input(int fh_in, void (*callback) (char *line)) 148 { 149 char buf[MAX_LINE_SIZE]; 150 int r_size, err; 151 int line_size; 152 int buf_end; 153 int line_nr; 154 char *p; 155 memset(buf, 0, MAX_LINE_SIZE); 156 157 r_size = 0; 158 buf_end = 0; 159 line_nr = 0; 160 161 while (1 == 1) { 162 /* fill buffer taking into account there can already be 163 * content at the start */ 164 r_size = read(fh_in, &buf[buf_end], MAX_LINE_SIZE - buf_end); 165 if (r_size == -1) { 166 err = errno; 167 fprintf(stderr, "failed reading input:%s\n", 168 strerror(err)); 169 return 1; 170 } 171 /* checking for read size of 0 is not enough as the buffer 172 * still can contain content */ 173 buf_end = buf_end + r_size; 174 175 /* is there data we need to process ? */ 176 if (buf_end == 0) { 177 return 0; /* normal exit is here */ 178 } 179 180 /* find end of line or eof. start a the start of the buffer */ 181 p = buf; 182 while (p < buf + buf_end) { 183 if (*p == '\n' || *p == '\0') { 184 /* replace either by a null terminator */ 185 line_nr++; 186 *p = '\0'; 187 break; 188 } 189 p++; 190 } 191 192 /* If we are at the end of the buffer we did not find a 193 * terminator */ 194 if (p == buf + buf_end) { 195 fprintf(stderr, 196 "Line(%d) does not fit the buffer %d\n", line_nr, 197 MAX_LINE_SIZE); 198 return 1; 199 } 200 201 line_size = p - buf; /* size of the line we currently are 202 * reading */ 203 204 /* here we have a valid line */ 205 callback(buf); 206 207 /* copy the remaining data over to the start */ 208 memmove(buf, p + 1, MAX_LINE_SIZE - line_size); 209 buf_end -= (line_size + 1); 210 } 211 return 0; 212 } 213 214 static void 215 parse_line_cb(char *line) 216 { 217 if (convert_to_entry(line, &entries[entry_total_count]) == 0) { 218 entry_total_count++; 219 assert(entry_total_count < MAX_ENTRIES); 220 } else { 221 memset(&entries[entry_total_count], 0, sizeof(struct entry)); 222 } 223 } 224 225 static int 226 create_entries(int handle) 227 { 228 int c; 229 char *p; 230 struct entry *entry; 231 232 char tmppath[MAX_LINE_SIZE]; 233 int i; 234 235 if (iterate_over_input(handle, parse_line_cb)) { 236 return 1; 237 } 238 239 /* calculate depth for each entry */ 240 for (c = 0; c < entry_total_count; c++) { 241 p = entries[c].path; 242 while (*p != 0) { 243 if (*p == '/') { 244 entries[c].depth++; 245 } 246 p++; 247 } 248 } 249 250 /* find parent entry and set the filename */ 251 for (c = 0; c < entry_total_count; c++) { 252 entry = &entries[c]; 253 if (entry->depth > 0) { 254 /* calculate path */ 255 /* find last "/" element and "null" it */ 256 strncpy(tmppath, entry->path, MAX_LINE_SIZE - 1); 257 i = strlen(tmppath); 258 while (i > 0) { 259 if (tmppath[i] == '/') { 260 entry->filename = &entry->path[i + 1]; 261 tmppath[i] = '\0'; 262 break; 263 } 264 i--; 265 } 266 if (i == 0) { 267 fprintf 268 (stderr, 269 "error while searching for parent path of %s\n", 270 entry->path); 271 return 1; 272 } 273 274 /* now compare with the other entries */ 275 for (i = 0; i < entry_total_count; i++) { 276 if (strncmp(entries[i].path, tmppath, 277 MAX_LINE_SIZE) == 0) { 278 /* found entry */ 279 entry->parent = &entries[i]; 280 break; 281 } 282 } 283 if (entry->parent == NULL) { 284 fprintf(stderr, 285 "Failed to find parent directory of %s\n", 286 entry->path); 287 return 1; 288 } 289 assert(entry->parent->type == ENTRY_DIR); 290 } else { 291 /* same in this case */ 292 entry->filename = entry->path; 293 } 294 } 295 296 return 0; 297 } 298 299 static char * parse_mode(int mode){ 300 /* Convert a 4 digit octal number int a proto entry as described in 301 the mkfs.mfs man page e.g. [suid-char][guid-char]0777 mode */ 302 303 static char m[6]; 304 memset(m,0,6); 305 char suid,guid; 306 suid = (mode & 04000)?'u':'-'; 307 guid = (mode & 02000)?'g':'-'; 308 snprintf(m,6,"%c%c%3o",suid,guid,mode & 0777); 309 return m; 310 } 311 312 static char *parse_filename(char *path) 313 { 314 /* Skipping the first . in the path */ 315 return &path[1]; 316 } 317 318 static int 319 dump_entry(FILE * out, int mindex, const char *base_dir) 320 { 321 322 int space; 323 int i; 324 struct entry *entry = &entries[mindex]; 325 326 /* Ensure uid & gid are set to something meaningful. */ 327 if (entry->uid == NULL) { 328 entry->uid = __UNCONST("0"); 329 } 330 if (entry->gid == NULL) { 331 entry->gid = __UNCONST("0"); 332 } 333 334 /* Indent the line */ 335 for (space = 0; space < entries[mindex].depth; space++) { 336 fprintf(out, " "); 337 } 338 if (entry->type == ENTRY_DIR) { 339 if (entries[mindex].depth > 0) { 340 fprintf(out, "%s ", entry->filename); 341 } 342 fprintf(out, "d%s", parse_mode(entry->mode)); 343 fprintf(out, " %s", entry->uid); 344 fprintf(out, " %s", entry->gid); 345 fprintf(out, "\n"); 346 for (i = 0; i < entry_total_count; i++) { 347 if (entries[i].parent == entry) { 348 dump_entry(out, i, base_dir); 349 } 350 } 351 for (space = 0; space < entries[mindex].depth; space++) { 352 fprintf(out, " "); 353 } 354 fprintf(out, "$\n"); 355 } else if (entry->type == ENTRY_FILE) { 356 fprintf(out, "%s -%s %s %s %s%s\n", entry->filename, 357 parse_mode(entry->mode), entry->uid, entry->gid, 358 base_dir, parse_filename(entry->path)); 359 } else if (entry->type == ENTRY_LINK) { 360 fprintf(out, "%s s%s %s %s %s\n", entry->filename, 361 parse_mode(entry->mode), entry->uid, entry->gid, 362 entry->link); 363 } else if (entry->type == ENTRY_CHAR) { 364 fprintf(out, "%s c%s %s %s %d %d\n", entry->filename, 365 parse_mode(entry->mode), entry->uid, entry->gid, 366 entry->dev_major, entry->dev_minor); 367 } else if (entry->type == ENTRY_BLOCK) { 368 fprintf(out, "%s b%s %s %s %d %d\n", entry->filename, 369 parse_mode(entry->mode), entry->uid, entry->gid, 370 entry->dev_major, entry->dev_minor); 371 } else { 372 /* Unknown line type. */ 373 fprintf(out, "#"); 374 fprintf(out, "%i %s\n", entry->type, entry->path); 375 exit(1); 376 return 1; 377 } 378 return 0; 379 } 380 381 static int 382 dump_proto(FILE * out, const char *base_dir) 383 { 384 int i; 385 fprintf(out, "boot\n0 0"); 386 for (i = 0; i < entry_total_count; i++) { 387 if (entries[i].depth == 0) { 388 fprintf(out, "\n"); 389 dump_entry(out, i, base_dir); 390 } 391 } 392 return 0; 393 } 394 395 static void 396 print_usage(void) 397 { 398 printf("Usage: toproto [OPTION]...\n"); 399 printf 400 ("Convert a netbsd METALOG file into a proto file for mkfs.mfs.\n"); 401 printf("\n"); 402 printf(" -i input METALOG\n"); 403 printf(" -b base_path\n"); 404 printf(" -o output proto\n"); 405 printf(" -h show this this help and exit\n"); 406 } 407 408 int 409 main(int argc, char **argv) 410 { 411 int ch, fh_in; 412 FILE *out; 413 const char *base_path; 414 char *input_file, *output_file; 415 416 input_file = NULL; 417 output_file = NULL; 418 base_path = "."; 419 fh_in = STDIN_FILENO; 420 out = stdout; 421 422 while ((ch = getopt(argc, argv, "i:b:o:h")) != -1) { 423 switch (ch) { 424 case 'i': 425 input_file = optarg; 426 break; 427 case 'b': 428 base_path = optarg; 429 break; 430 case 'o': 431 output_file = optarg; 432 break; 433 case 'h': 434 print_usage(); 435 exit(0); 436 break; 437 default: 438 print_usage(); 439 exit(1); 440 } 441 } 442 argc -= optind; 443 argv += optind; 444 445 if (input_file) { 446 fh_in = open(input_file, O_RDONLY); 447 if (fh_in == -1) { 448 fprintf(stderr, "Failed to open input file (%s):%s\n", 449 input_file, strerror(errno)); 450 exit(1); 451 } 452 } 453 if (output_file) { 454 out = fopen(output_file, "w+"); 455 if (!out) { 456 fprintf(stderr, "Failed to open input file (%s):%s\n", 457 input_file, strerror(errno)); 458 exit(1); 459 } 460 } 461 462 if (create_entries(fh_in)) { 463 fprintf(stderr, "Failed to create entries\n"); 464 exit(1); 465 } 466 if (input_file) 467 close(fh_in); 468 469 if (dump_proto(out, base_path)) { 470 fprintf(stderr, "Failed to create entries\n"); 471 exit(1); 472 } 473 474 if (output_file) 475 fclose(out); 476 return 0; 477 } 478