udf.c revision 1.2
1/* $NetBSD: udf.c,v 1.2 2013/08/05 16:43:46 reinoud Exp $ */ 2 3/* 4 * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28 29#include <sys/cdefs.h> 30#ifndef lint 31__RCSID("$NetBSD: udf.c,v 1.2 2013/08/05 16:43:46 reinoud Exp $"); 32#endif /* not lint */ 33 34#define _EXPOSE_MMC 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <errno.h> 40#include <time.h> 41#include <assert.h> 42#include <err.h> 43#include <unistd.h> 44#include <fcntl.h> 45#include <sys/types.h> 46#include <sys/param.h> 47#include <sys/cdio.h> 48#include <sys/stat.h> 49#include <util.h> 50 51#include "makefs.h" 52#include "udf_create.h" 53#include "udf_write.h" 54#include "newfs_udf.h" 55 56 57/* 58 * Note: due to the setup of the newfs code, the current state of the program 59 * and its options are helt in a few global variables. The FS specific parts 60 * are in a global `context' structure. 61 */ 62 63/* global variables describing disc and format requests */ 64int fd; /* device: file descriptor */ 65char *dev; /* device: name */ 66struct mmc_discinfo mmc_discinfo; /* device: disc info */ 67 68char *format_str; /* format: string representation */ 69int format_flags; /* format: attribute flags */ 70int media_accesstype; /* derived from current mmc cap */ 71int check_surface; /* for rewritables */ 72int imagefile_secsize; /* for files */ 73 74int display_progressbar; 75 76int wrtrack_skew; 77float meta_fract = (float) UDF_META_PERC / 100.0; 78 79int mmc_profile; /* emulated profile */ 80int req_enable, req_disable; 81 82 83/* --------------------------------------------------------------------- */ 84 85int 86udf_write_sector(void *sector, uint32_t location) 87{ 88 uint64_t wpos; 89 ssize_t ret; 90 91 wpos = (uint64_t) location * context.sector_size; 92 ret = pwrite(fd, sector, context.sector_size, wpos); 93 if (ret == -1) 94 return errno; 95 if (ret < context.sector_size) 96 return EIO; 97 return 0; 98} 99 100 101/* not implemented for files */ 102int 103udf_surface_check(void) 104{ 105 return 0; 106} 107 108 109/* 110 * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main 111 * code in sys/fs/udf/ 112 */ 113 114#ifdef DEBUG 115static void 116udf_dump_discinfo(struct mmc_discinfo *di) 117{ 118 char bits[128]; 119 120 printf("Device/media info :\n"); 121 printf("\tMMC profile 0x%02x\n", di->mmc_profile); 122 printf("\tderived class %d\n", di->mmc_class); 123 printf("\tsector size %d\n", di->sector_size); 124 printf("\tdisc state %d\n", di->disc_state); 125 printf("\tlast ses state %d\n", di->last_session_state); 126 printf("\tbg format state %d\n", di->bg_format_state); 127 printf("\tfrst track %d\n", di->first_track); 128 printf("\tfst on last ses %d\n", di->first_track_last_session); 129 printf("\tlst on last ses %d\n", di->last_track_last_session); 130 printf("\tlink block penalty %d\n", di->link_block_penalty); 131 snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, (uint64_t) di->disc_flags); 132 printf("\tdisc flags %s\n", bits); 133 printf("\tdisc id %x\n", di->disc_id); 134 printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); 135 136 printf("\tnum sessions %d\n", di->num_sessions); 137 printf("\tnum tracks %d\n", di->num_tracks); 138 139 snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); 140 printf("\tcapabilities cur %s\n", bits); 141 snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); 142 printf("\tcapabilities cap %s\n", bits); 143 printf("\n"); 144 printf("\tlast_possible_lba %d\n", di->last_possible_lba); 145 printf("\n"); 146} 147#else 148#define udf_dump_discinfo(a); 149#endif 150 151/* --------------------------------------------------------------------- */ 152 153static int 154udf_emulate_discinfo(fsinfo_t *fsopts, struct mmc_discinfo *di, 155 int mmc_emuprofile) 156{ 157 off_t sectors; 158 159 memset(di, 0, sizeof(*di)); 160 161 /* file support */ 162 if ((mmc_emuprofile != 0x01) && (fsopts->sectorsize != 2048)) 163 warnx("cd/dvd/bd sectorsize is not set to default 2048"); 164 165 sectors = fsopts->size / fsopts->sectorsize; 166 167 /* commons */ 168 di->mmc_profile = mmc_emuprofile; 169 di->disc_state = MMC_STATE_CLOSED; 170 di->last_session_state = MMC_STATE_CLOSED; 171 di->bg_format_state = MMC_BGFSTATE_COMPLETED; 172 di->link_block_penalty = 0; 173 174 di->disc_flags = MMC_DFLAGS_UNRESTRICTED; 175 176 di->last_possible_lba = sectors - 1; 177 di->sector_size = fsopts->sectorsize; 178 179 di->num_sessions = 1; 180 di->num_tracks = 1; 181 182 di->first_track = 1; 183 di->first_track_last_session = di->last_track_last_session = 1; 184 185 di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_ZEROLINKBLK; 186 switch (mmc_emuprofile) { 187 case 0x00: /* unknown, treat as CDROM */ 188 case 0x08: /* CDROM */ 189 case 0x10: /* DVD-ROM */ 190 req_enable |= FORMAT_READONLY; 191 /* FALLTROUGH */ 192 case 0x01: /* disc */ 193 /* set up a disc info profile for partitions/files */ 194 di->mmc_class = MMC_CLASS_DISC; 195 di->mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; 196 break; 197 case 0x09: /* CD-R */ 198 di->mmc_class = MMC_CLASS_CD; 199 di->mmc_cur |= MMC_CAP_SEQUENTIAL; 200 di->disc_state = MMC_STATE_EMPTY; 201 break; 202 case 0x0a: /* CD-RW + CD-MRW (regretably) */ 203 di->mmc_class = MMC_CLASS_CD; 204 di->mmc_cur |= MMC_CAP_REWRITABLE; 205 break; 206 case 0x13: /* DVD-RW */ 207 di->mmc_class = MMC_CLASS_DVD; 208 di->mmc_cur |= MMC_CAP_REWRITABLE; 209 break; 210 case 0x11: /* DVD-R */ 211 case 0x14: /* DVD-RW sequential */ 212 case 0x1b: /* DVD+R */ 213 case 0x2b: /* DVD+R DL */ 214 case 0x51: /* HD DVD-R */ 215 di->mmc_class = MMC_CLASS_DVD; 216 di->mmc_cur |= MMC_CAP_SEQUENTIAL; 217 di->disc_state = MMC_STATE_EMPTY; 218 break; 219 case 0x41: /* BD-R */ 220 di->mmc_class = MMC_CLASS_BD; 221 di->mmc_cur |= MMC_CAP_SEQUENTIAL | MMC_CAP_HW_DEFECTFREE; 222 di->disc_state = MMC_STATE_EMPTY; 223 break; 224 case 0x43: /* BD-RE */ 225 di->mmc_class = MMC_CLASS_BD; 226 di->mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; 227 break; 228 default: 229 err(EINVAL, "makefs_udf: unknown or unimplemented device type"); 230 return EINVAL; 231 } 232 di->mmc_cap = di->mmc_cur; 233 234 udf_dump_discinfo(di); 235 return 0; 236} 237 238 239int 240udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti) 241{ 242 /* discs partition support */ 243 if (ti->tracknr != 1) 244 return EIO; 245 246 /* create fake ti */ 247 ti->sessionnr = 1; 248 249 ti->track_mode = 0; /* XXX */ 250 ti->data_mode = 0; /* XXX */ 251 ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; 252 253 ti->track_start = 0; 254 ti->packet_size = 32; /* take sensible default */ 255 256 ti->track_size = di->last_possible_lba; 257 ti->next_writable = di->last_possible_lba; 258 ti->last_recorded = ti->next_writable; 259 ti->free_blocks = 0; 260 261 return 0; 262} 263 264 265#define OPT_STR(letter, name, desc) \ 266 { letter, name, NULL, OPT_STRBUF, 0, 0, desc } 267 268#define OPT_NUM(letter, name, field, min, max, desc) \ 269 { letter, name, &diskStructure->field, \ 270 sizeof(diskStructure->field) == 8 ? OPT_INT64 : \ 271 (sizeof(diskStructure->field) == 4 ? OPT_INT32 : \ 272 (sizeof(diskStructure->field) == 2 ? OPT_INT16 : OPT_INT8)), \ 273 min, max, desc } 274 275#define OPT_BOOL(letter, name, field, desc) \ 276 OPT_NUM(letter, name, field, 0, 1, desc) 277 278void 279udf_prep_opts(fsinfo_t *fsopts) 280{ 281 struct tm *tm; 282 time_t now; 283 284 const option_t udf_options[] = { 285 OPT_STR('T', "disctype", "disc type (cdrom,dvdrom,dvdram,bdre,disk,cdr,dvdr,cdrw,dvdrw)"), 286// { 'P', "progress", &display_progressbar, OPT_INT32, false, true, 287// "display progress bar" }, 288 { .name = NULL } 289 }; 290 291 /* initialise */ 292 format_str = strdup(""); 293 req_enable = req_disable = 0; 294 format_flags = FORMAT_INVALID; 295 fsopts->sectorsize = 512; /* minimum allowed sector size */ 296 297 srandom((unsigned long) time(NULL)); 298 299 mmc_profile = 0x01; /* 'disc'/file */ 300 301 udf_init_create_context(); 302 context.app_name = APP_NAME; 303 context.impl_name = IMPL_NAME; 304 context.app_version_main = APP_VERSION_MAIN; 305 context.app_version_sub = APP_VERSION_SUB; 306 307 /* minimum and maximum UDF versions we advise */ 308 context.min_udf = 0x102; 309 context.max_udf = 0x201; 310 311 /* use user's time zone as default */ 312 (void)time(&now); 313 tm = localtime(&now); 314 context.gmtoff = tm->tm_gmtoff; 315 316 /* return info */ 317 fsopts->fs_specific = NULL; 318 fsopts->fs_options = copy_opts(udf_options); 319} 320 321 322void 323udf_cleanup_opts(fsinfo_t *fsopts) 324{ 325 free(fsopts->fs_options); 326} 327 328 329int 330udf_parse_opts(const char *option, fsinfo_t *fsopts) 331{ 332 option_t *udf_options = fsopts->fs_options; 333 const char *name, *desc; 334 char buf[1024]; 335 int i; 336 337 assert(option != NULL); 338 339 if (debug & DEBUG_FS_PARSE_OPTS) 340 printf("udf_parse_opts: got `%s'\n", option); 341 342 i = set_option(udf_options, option, buf, sizeof(buf)); 343 if (i == -1) 344 return 0; 345 346 if (udf_options[i].name == NULL) 347 abort(); 348 349 name = udf_options[i].name; 350 desc = udf_options[i].desc; 351 switch (udf_options[i].letter) { 352 case 'T': 353 if (strcmp(buf, "cdrom") == 0) { 354 mmc_profile = 0x00; 355 } else if (strcmp(buf, "dvdrom") == 0) { 356 mmc_profile = 0x10; 357 } else if (strcmp(buf, "dvdram") == 0) { 358 mmc_profile = 0x12; 359 } else if (strcmp(buf, "bdre") == 0) { 360 mmc_profile = 0x43; 361 } else if (strcmp(buf, "disk") == 0) { 362 mmc_profile = 0x01; 363 } else if (strcmp(buf, "cdr") == 0) { 364 mmc_profile = 0x09; 365 } else if (strcmp(buf, "dvdr") == 0) { 366 mmc_profile = 0x1b; 367 } else if (strcmp(buf, "cdrw") == 0) { 368 mmc_profile = 0x0a; 369 } else if (strcmp(buf, "dvdrw") == 0) { 370 mmc_profile = 0x13; 371 } else { 372 errx(EINVAL, "Unknown or unimplemented disc format"); 373 return 0; 374 } 375 } 376 return 1; 377} 378 379/* --------------------------------------------------------------------- */ 380 381struct udf_stats { 382 uint32_t nfiles; 383 uint32_t ndirs; 384 uint32_t ndescr; 385 uint32_t nmetadatablocks; 386 uint32_t ndatablocks; 387}; 388 389 390/* node reference administration */ 391static void 392udf_inc_link(union dscrptr *dscr) 393{ 394 struct file_entry *fe; 395 struct extfile_entry *efe; 396 397 if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { 398 fe = &dscr->fe; 399 fe->link_cnt = udf_rw16(udf_rw16(fe->link_cnt) + 1); 400 } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { 401 efe = &dscr->efe; 402 efe->link_cnt = udf_rw16(udf_rw16(efe->link_cnt) + 1); 403 } else { 404 errx(1, "Bad tag passed to udf_inc_link"); 405 } 406} 407 408 409static void 410udf_set_link_cnt(union dscrptr *dscr, int num) 411{ 412 struct file_entry *fe; 413 struct extfile_entry *efe; 414 415 if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { 416 fe = &dscr->fe; 417 fe->link_cnt = udf_rw16(num); 418 } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { 419 efe = &dscr->efe; 420 efe->link_cnt = udf_rw16(num); 421 } else { 422 errx(1, "Bad tag passed to udf_set_link_cnt"); 423 } 424} 425 426 427static uint32_t 428udf_datablocks(off_t sz) 429{ 430 /* predictor if it can be written inside the node */ 431 if (sz < context.sector_size - UDF_EXTFENTRY_SIZE - 16) 432 return 0; 433 434 return UDF_ROUNDUP(sz, context.sector_size) / context.sector_size; 435} 436 437 438static void 439udf_prepare_fids(struct long_ad *dir_icb, struct long_ad *dirdata_icb, 440 uint8_t *dirdata, uint32_t dirdata_size) 441{ 442 struct fileid_desc *fid; 443 struct long_ad *icb; 444 uint32_t fidsize, offset; 445 uint32_t location; 446 447 if (udf_datablocks(dirdata_size) == 0) { 448 /* going internal */ 449 icb = dir_icb; 450 } else { 451 /* external blocks to write to */ 452 icb = dirdata_icb; 453 } 454 455 for (offset = 0; offset < dirdata_size; offset += fidsize) { 456 /* for each FID: */ 457 fid = (struct fileid_desc *) (dirdata + offset); 458 assert(udf_rw16(fid->tag.id) == TAGID_FID); 459 460 location = udf_rw32(icb->loc.lb_num); 461 location += offset / context.sector_size; 462 463 fid->tag.tag_loc = udf_rw32(location); 464 udf_validate_tag_and_crc_sums((union dscrptr *) fid); 465 466 fidsize = udf_fidsize(fid); 467 } 468} 469 470static int 471udf_file_inject_blob(union dscrptr *dscr, uint8_t *blob, size_t size) 472{ 473 struct icb_tag *icb; 474 struct file_entry *fe; 475 struct extfile_entry *efe; 476 uint64_t inf_len, obj_size; 477 uint32_t l_ea, l_ad; 478 uint32_t free_space, desc_size; 479 uint16_t crclen; 480 uint8_t *data, *pos; 481 482 fe = NULL; 483 efe = NULL; 484 if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { 485 fe = &dscr->fe; 486 data = fe->data; 487 l_ea = udf_rw32(fe->l_ea); 488 l_ad = udf_rw32(fe->l_ad); 489 icb = &fe->icbtag; 490 inf_len = udf_rw64(fe->inf_len); 491 obj_size = 0; 492 desc_size = sizeof(struct file_entry); 493 } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { 494 efe = &dscr->efe; 495 data = efe->data; 496 l_ea = udf_rw32(efe->l_ea); 497 l_ad = udf_rw32(efe->l_ad); 498 icb = &efe->icbtag; 499 inf_len = udf_rw64(efe->inf_len); 500 obj_size = udf_rw64(efe->obj_size); 501 desc_size = sizeof(struct extfile_entry); 502 } else { 503 errx(1, "Bad tag passed to udf_file_inject_blob"); 504 } 505 crclen = udf_rw16(dscr->tag.desc_crc_len); 506 507 /* calculate free space */ 508 free_space = context.sector_size - (l_ea + l_ad) - desc_size; 509 if (udf_datablocks(size)) { 510 assert(free_space < size); 511 return 1; 512 } 513 514 /* going internal */ 515 assert(l_ad == 0); 516 assert(udf_rw16(icb->flags) == UDF_ICB_INTERN_ALLOC); 517 518 // assert(free_space >= size); 519 pos = data + l_ea + l_ad; 520 memcpy(pos, blob, size); 521 l_ad += size; 522 crclen += size; 523 524 inf_len += size; 525 obj_size += size; 526 527 if (fe) { 528 fe->l_ad = udf_rw32(l_ad); 529 fe->inf_len = udf_rw64(inf_len); 530 } else if (efe) { 531 efe->l_ad = udf_rw32(l_ad); 532 efe->inf_len = udf_rw64(inf_len); 533 efe->obj_size = udf_rw64(inf_len); 534 } 535 536 /* make sure the header sums stays correct */ 537 dscr->tag.desc_crc_len = udf_rw16(crclen); 538 udf_validate_tag_and_crc_sums(dscr); 539 540 return 0; 541} 542 543 544/* XXX no sparse file support */ 545static void 546udf_append_file_mapping(union dscrptr *dscr, struct long_ad *piece) 547{ 548 struct icb_tag *icb; 549 struct file_entry *fe; 550 struct extfile_entry *efe; 551 struct long_ad *last_long, last_piece; 552 struct short_ad *last_short, new_short; 553 uint64_t inf_len, obj_size, logblks_rec; 554 uint32_t l_ea, l_ad, size; 555 uint32_t last_lb_num, piece_lb_num; 556 uint32_t last_len, piece_len, last_flags; 557 uint32_t rest_len, merge_len, last_end; 558 uint16_t last_part_num, piece_part_num; 559 uint16_t crclen, cur_alloc; 560 uint8_t *data, *pos; 561 const int short_len = sizeof(struct short_ad); 562 const int long_len = sizeof(struct long_ad); 563 const int sector_size = context.sector_size; 564 const int use_shorts = (context.data_part == context.metadata_part); 565 uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size); 566 567 if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { 568 fe = &dscr->fe; 569 data = fe->data; 570 l_ea = fe->l_ea; 571 l_ad = udf_rw32(fe->l_ad); 572 icb = &fe->icbtag; 573 inf_len = udf_rw64(fe->inf_len); 574 logblks_rec = udf_rw64(fe->logblks_rec); 575 obj_size = 0; 576 } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { 577 efe = &dscr->efe; 578 data = efe->data; 579 l_ea = efe->l_ea; 580 l_ad = udf_rw32(efe->l_ad); 581 icb = &efe->icbtag; 582 inf_len = udf_rw64(efe->inf_len); 583 obj_size = udf_rw64(efe->obj_size); 584 logblks_rec = udf_rw64(efe->logblks_rec); 585 } else { 586 errx(1, "Bad tag passed to udf_file_append_blob"); 587 } 588 crclen = udf_rw16(dscr->tag.desc_crc_len); 589 590 pos = data + l_ea; 591 cur_alloc = udf_rw16(icb->flags); 592 size = UDF_EXT_LEN(udf_rw32(piece->len)); 593 594 /* extract last entry as a long_ad */ 595 memset(&last_piece, 0, sizeof(last_piece)); 596 last_len = 0; 597 last_lb_num = 0; 598 last_part_num = 0; 599 if (l_ad != 0) { 600 if (use_shorts) { 601 assert(cur_alloc == UDF_ICB_SHORT_ALLOC); 602 pos += l_ad - short_len; 603 last_short = (struct short_ad *) pos; 604 last_lb_num = udf_rw32(last_short->lb_num); 605 last_part_num = udf_rw16(piece->loc.part_num); 606 last_len = UDF_EXT_LEN(udf_rw32(last_short->len)); 607 last_flags = UDF_EXT_FLAGS(udf_rw32(last_short->len)); 608 } else { 609 assert(cur_alloc == UDF_ICB_LONG_ALLOC); 610 pos += l_ad - long_len; 611 last_long = (struct long_ad *) pos; 612 last_lb_num = udf_rw32(last_long->loc.lb_num); 613 last_part_num = udf_rw16(last_long->loc.part_num); 614 last_len = UDF_EXT_LEN(udf_rw32(last_long->len)); 615 last_flags = UDF_EXT_FLAGS(udf_rw32(last_long->len)); 616 } 617 } 618 619 piece_len = UDF_EXT_LEN(udf_rw32(piece->len)); 620 piece_lb_num = udf_rw32(piece->loc.lb_num); 621 piece_part_num = udf_rw16(piece->loc.part_num); 622 623 /* try merging */ 624 rest_len = max_len - last_len; 625 626 merge_len = MIN(udf_rw32(piece->len), rest_len); 627 last_end = last_lb_num + (last_len / sector_size); 628 629 if ((piece_lb_num == last_end) && (last_part_num == piece_part_num)) { 630 /* we can merge */ 631 last_len += merge_len; 632 piece_len -= merge_len; 633 634 /* write back merge result */ 635 if (use_shorts) { 636 last_short->len = udf_rw32(last_len | last_flags); 637 } else { 638 last_long->len = udf_rw32(last_len | last_flags); 639 } 640 piece_lb_num += merge_len / sector_size; 641 } 642 643 if (piece_len) { 644 /* append new entry */ 645 pos = data + l_ea + l_ad; 646 if (use_shorts) { 647 icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC); 648 memset(&new_short, 0, short_len); 649 new_short.len = udf_rw32(piece_len); 650 new_short.lb_num = udf_rw32(piece_lb_num); 651 memcpy(pos, &new_short, short_len); 652 l_ad += short_len; 653 crclen += short_len; 654 } else { 655 icb->flags = udf_rw16(UDF_ICB_LONG_ALLOC); 656 piece->len = udf_rw32(piece_len); 657 piece->loc.lb_num = udf_rw32(piece_lb_num); 658 memcpy(pos, piece, long_len); 659 l_ad += long_len; 660 crclen += long_len; 661 } 662 } 663 piece->len = udf_rw32(0); 664 665 inf_len += size; 666 obj_size += size; 667 logblks_rec += UDF_ROUNDUP(size, sector_size) / sector_size; 668 669 dscr->tag.desc_crc_len = udf_rw16(crclen); 670 if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { 671 fe->l_ad = udf_rw32(l_ad); 672 fe->inf_len = udf_rw64(inf_len); 673 fe->logblks_rec = udf_rw64(logblks_rec); 674 } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { 675 efe->l_ad = udf_rw32(l_ad); 676 efe->inf_len = udf_rw64(inf_len); 677 efe->obj_size = udf_rw64(inf_len); 678 efe->logblks_rec = udf_rw64(logblks_rec); 679 } 680} 681 682 683static int 684udf_append_file_contents(union dscrptr *dscr, struct long_ad *data_icb, 685 uint8_t *fdata, size_t flen) 686{ 687 struct long_ad icb; 688 uint32_t location; 689 uint32_t phys; 690 uint16_t vpart; 691 uint8_t *bpos; 692 int cnt, sects; 693 int error; 694 695 if (udf_file_inject_blob(dscr, fdata, flen) == 0) 696 return 0; 697 698 /* has to be appended in mappings */ 699 icb = *data_icb; 700 icb.len = udf_rw32(flen); 701 while (udf_rw32(icb.len) > 0) 702 udf_append_file_mapping(dscr, &icb); 703 udf_validate_tag_and_crc_sums(dscr); 704 705 /* write out data piece */ 706 vpart = udf_rw16(data_icb->loc.part_num); 707 location = udf_rw32(data_icb->loc.lb_num); 708 sects = udf_datablocks(flen); 709 for (cnt = 0; cnt < sects; cnt++) { 710 bpos = fdata + cnt*context.sector_size; 711 phys = context.vtop_offset[vpart] + location + cnt; 712 error = udf_write_sector(bpos, phys); 713 if (error) 714 return error; 715 } 716 return 0; 717} 718 719 720static int 721udf_create_new_file(struct stat *st, union dscrptr **dscr, 722 int filetype, struct long_ad *icb) 723{ 724 struct file_entry *fe; 725 struct extfile_entry *efe; 726 int error; 727 728 fe = NULL; 729 efe = NULL; 730 if (context.dscrver == 2) { 731 error = udf_create_new_fe(&fe, filetype, st); 732 if (error) 733 errx(error, "can't create fe"); 734 *dscr = (union dscrptr *) fe; 735 icb->longad_uniqueid = fe->unique_id; 736 } else { 737 error = udf_create_new_efe(&efe, filetype, st); 738 if (error) 739 errx(error, "can't create fe"); 740 *dscr = (union dscrptr *) efe; 741 icb->longad_uniqueid = efe->unique_id; 742 } 743 744 return 0; 745} 746 747 748static void 749udf_estimate_walk(fsinfo_t *fsopts, 750 fsnode *root, char *dir, struct udf_stats *stats) 751{ 752 struct fileid_desc *fid; 753 struct long_ad dummy_ref; 754 fsnode *cur; 755 fsinode *fnode; 756 size_t pathlen = strlen(dir); 757 char *mydir = dir + pathlen; 758 off_t sz; 759 uint32_t nblk, ddoff; 760 uint32_t softlink_len; 761 uint8_t *softlink_buf; 762 int nentries; 763 int error; 764 765 stats->ndirs++; 766 767 /* 768 * Count number of directory entries and count directory size; needed 769 * for the reservation of enough space for the directory. Pity we 770 * don't keep the FIDs we created. If it turns out to be a issue we 771 * can cache it later. 772 */ 773 fid = (struct fileid_desc *) malloc(context.sector_size); 774 assert(fid); 775 776 ddoff = 40; /* '..' entry */ 777 for (cur = root, nentries = 0; cur != NULL; cur = cur->next) { 778 switch (cur->type & S_IFMT) { 779 default: 780 /* what kind of nodes? */ 781 break; 782 case S_IFCHR: 783 case S_IFBLK: 784 /* not supported yet */ 785 continue; 786 case S_IFDIR: 787 if (strcmp(cur->name, ".") == 0) 788 continue; 789 case S_IFLNK: 790 case S_IFREG: 791 /* create dummy FID to see how long name will become */ 792 udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref); 793 794 nentries++; 795 ddoff += udf_fidsize(fid); 796 } 797 } 798 sz = ddoff; 799 800 root->inode->st.st_size = sz; /* max now */ 801 root->inode->flags |= FI_SIZED; 802 803 nblk = udf_datablocks(sz); 804 stats->nmetadatablocks += nblk; 805 806 /* for each entry in the directory, there needs to be a (E)FE */ 807 stats->nmetadatablocks += nentries + 1; 808 809 /* recurse */ 810 for (cur = root; cur != NULL; cur = cur->next) { 811 switch (cur->type & S_IFMT) { 812 default: 813 /* what kind of nodes? */ 814 break; 815 case S_IFCHR: 816 case S_IFBLK: 817 /* not supported yet */ 818 // stats->nfiles++; 819 break; 820 case S_IFDIR: 821 if (strcmp(cur->name, ".") == 0) 822 continue; 823 /* empty dir? */ 824 if (!cur->child) 825 break; 826 mydir[0] = '/'; 827 strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); 828 udf_estimate_walk(fsopts, cur->child, dir, stats); 829 mydir[0] = '\0'; 830 break; 831 case S_IFREG: 832 fnode = cur->inode; 833 /* don't double-count hard-links */ 834 if (!(fnode->flags & FI_SIZED)) { 835 sz = fnode->st.st_size; 836 nblk = udf_datablocks(sz); 837 stats->ndatablocks += nblk; 838 /* ... */ 839 fnode->flags |= FI_SIZED; 840 } 841 stats->nfiles++; 842 break; 843 case S_IFLNK: 844 /* softlink */ 845 fnode = cur->inode; 846 /* don't double-count hard-links */ 847 if (!(fnode->flags & FI_SIZED)) { 848 error = udf_encode_symlink(&softlink_buf, 849 &softlink_len, cur->symlink); 850 if (error) { 851 printf("SOFTLINK error %d\n", error); 852 break; 853 } 854 nblk = udf_datablocks(softlink_len); 855 stats->ndatablocks += nblk; 856 fnode->flags |= FI_SIZED; 857 858 free(softlink_buf); 859 } 860 stats->nfiles++; 861 break; 862 } 863 } 864} 865 866 867#define UDF_MAX_CHUNK_SIZE (4*1024*1024) 868static int 869udf_copy_file(struct stat *st, char *path, fsnode *cur, struct fileid_desc *fid, 870 struct long_ad *icb) 871{ 872 union dscrptr *dscr; 873 struct long_ad data_icb; 874 fsinode *fnode; 875 size_t sz, chunk, rd; 876 uint8_t *data; 877 int nblk; 878 int i, f; 879 880 fnode = cur->inode; 881 882 f = open(path, O_RDONLY, 0, 0); 883 if (f < 0) { 884 warn("Can't open file %s for reading", cur->name); 885 return errno; 886 } 887 888 /* claim disc space for the (e)fe descriptor for this file */ 889 udf_metadata_alloc(1, icb); 890 udf_create_new_file(st, &dscr, UDF_ICB_FILETYPE_RANDOMACCESS, icb); 891 892 sz = fnode->st.st_size; 893 894 chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); 895 data = malloc(MAX(chunk, context.sector_size)); 896 assert(data); 897 898 printf(" "); 899 i = 0; 900 while (chunk) { 901 rd = read(f, data, chunk); 902 if (rd != chunk) { 903 warn("Short read of file %s\n", cur->name); 904 close(f); 905 free(data); 906 return errno; 907 } 908 printf("\b%c", "\\|/-"[i++ % 4]); fflush(stdout);fflush(stderr); 909 910 nblk = udf_datablocks(chunk); 911 if (nblk > 0) 912 udf_data_alloc(nblk, &data_icb); 913 udf_append_file_contents(dscr, &data_icb, data, chunk); 914 915 sz -= chunk; 916 chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); 917 } 918 printf("\b \n"); 919 close(f); 920 free(data); 921 922 /* write out dscr (e)fe */ 923 udf_set_link_cnt(dscr, fnode->nlink); 924 udf_write_dscr_virt(dscr, udf_rw32(icb->loc.lb_num), 925 udf_rw16(icb->loc.part_num), 1); 926 927 /* remember our location for hardlinks */ 928 cur->inode->fsuse = malloc(sizeof(struct long_ad)); 929 memcpy(cur->inode->fsuse, icb, sizeof(struct long_ad)); 930 931 return 0; 932} 933 934 935static int 936udf_populate_walk(fsinfo_t *fsopts, fsnode *root, char *dir, 937 struct long_ad *parent_icb, struct long_ad *dir_icb) 938{ 939 union dscrptr *dir_dscr, *dscr; 940 struct fileid_desc *fid; 941 struct long_ad icb, data_icb, dirdata_icb; 942 fsnode *cur; 943 fsinode *fnode; 944 size_t pathlen = strlen(dir); 945 size_t dirlen; 946 char *mydir = dir + pathlen; 947 uint32_t nblk, ddoff; 948 uint32_t softlink_len; 949 uint8_t *softlink_buf; 950 uint8_t *dirdata; 951 int error, ret, retval; 952 953 /* claim disc space for the (e)fe descriptor for this dir */ 954 udf_metadata_alloc(1, dir_icb); 955 956 /* create new e(fe) */ 957 udf_create_new_file(&root->inode->st, &dir_dscr, 958 UDF_ICB_FILETYPE_DIRECTORY, dir_icb); 959 960 /* claim space for the directory contents */ 961 dirlen = root->inode->st.st_size; 962 nblk = udf_datablocks(dirlen); 963 if (nblk > 0) { 964 /* claim disc space for the dir contents */ 965 udf_data_alloc(nblk, &dirdata_icb); 966 } 967 968 /* allocate memory for the directory contents */ 969 nblk++; 970 dirdata = malloc(nblk * context.sector_size); 971 assert(dirdata); 972 memset(dirdata, 0, nblk * context.sector_size); 973 974 /* create and append '..' */ 975 fid = (struct fileid_desc *) dirdata; 976 ddoff = udf_create_parentfid(fid, parent_icb); 977 978 /* for '..' */ 979 udf_inc_link(dir_dscr); 980 981 /* recurse */ 982 retval = 0; 983 for (cur = root; cur != NULL; cur = cur->next) { 984 mydir[0] = '/'; 985 strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); 986 987 fid = (struct fileid_desc *) (dirdata + ddoff); 988 switch (cur->type & S_IFMT) { 989 default: 990 /* what kind of nodes? */ 991 retval = 2; 992 break; 993 case S_IFCHR: 994 case S_IFBLK: 995 /* not supported */ 996 retval = 2; 997 warnx("device node %s not supported", dir); 998 break; 999 case S_IFDIR: 1000 /* not an empty dir? */ 1001 if (strcmp(cur->name, ".") == 0) 1002 break; 1003 assert(cur->child); 1004 if (cur->child) { 1005 ret = udf_populate_walk(fsopts, cur->child, 1006 dir, dir_icb, &icb); 1007 if (ret) 1008 retval = 2; 1009 } 1010 udf_create_fid(ddoff, fid, cur->name, 1011 UDF_FILE_CHAR_DIR, &icb); 1012 udf_inc_link(dir_dscr); 1013 ddoff += udf_fidsize(fid); 1014 break; 1015 case S_IFREG: 1016 fnode = cur->inode; 1017 /* don't re-copy hard-links */ 1018 if (!(fnode->flags & FI_WRITTEN)) { 1019 printf("%s", dir); 1020 error = udf_copy_file(&fnode->st, dir, cur, 1021 fid, &icb); 1022 if (!error) { 1023 fnode->flags |= FI_WRITTEN; 1024 udf_create_fid(ddoff, fid, cur->name, 1025 0, &icb); 1026 ddoff += udf_fidsize(fid); 1027 } else { 1028 retval = 2; 1029 } 1030 } else { 1031 /* hardlink! */ 1032 printf("%s (hardlink)\n", dir); 1033 udf_create_fid(ddoff, fid, cur->name, 1034 0, (struct long_ad *) (fnode->fsuse)); 1035 ddoff += udf_fidsize(fid); 1036 } 1037 fnode->nlink--; 1038 if (fnode->nlink == 0) 1039 free(fnode->fsuse); 1040 break; 1041 case S_IFLNK: 1042 /* softlink */ 1043 fnode = cur->inode; 1044 printf("%s -> %s\n", dir, cur->symlink); 1045 error = udf_encode_symlink(&softlink_buf, 1046 &softlink_len, cur->symlink); 1047 if (error) { 1048 printf("SOFTLINK error %d\n", error); 1049 retval = 2; 1050 break; 1051 } 1052 1053 udf_metadata_alloc(1, &icb); 1054 udf_create_new_file(&fnode->st, &dscr, 1055 UDF_ICB_FILETYPE_SYMLINK, &icb); 1056 1057 nblk = udf_datablocks(softlink_len); 1058 if (nblk > 0) 1059 udf_data_alloc(nblk, &data_icb); 1060 udf_append_file_contents(dscr, &data_icb, 1061 softlink_buf, softlink_len); 1062 1063 /* write out dscr (e)fe */ 1064 udf_inc_link(dscr); 1065 udf_write_dscr_virt(dscr, udf_rw32(icb.loc.lb_num), 1066 udf_rw16(icb.loc.part_num), 1); 1067 1068 free(softlink_buf); 1069 1070 udf_create_fid(ddoff, fid, cur->name, 0, &icb); 1071 ddoff += udf_fidsize(fid); 1072 break; 1073 } 1074 mydir[0] = '\0'; 1075 } 1076 1077 /* writeout directory contents */ 1078 dirlen = ddoff; /* XXX might bite back */ 1079 1080 udf_prepare_fids(dir_icb, &dirdata_icb, dirdata, dirlen); 1081 udf_append_file_contents(dir_dscr, &dirdata_icb, dirdata, dirlen); 1082 1083 /* write out dir_dscr (e)fe */ 1084 udf_write_dscr_virt(dir_dscr, udf_rw32(dir_icb->loc.lb_num), 1085 udf_rw16(dir_icb->loc.part_num), 1); 1086 1087 return retval; 1088} 1089 1090 1091static int 1092udf_populate(const char *dir, fsnode *root, fsinfo_t *fsopts, 1093 struct udf_stats *stats) 1094{ 1095 struct long_ad rooticb; 1096 static char path[MAXPATHLEN+1]; 1097 int error; 1098 1099 /* make sure the root gets the rootdir entry */ 1100 context.metadata_alloc_pos = layout.rootdir; 1101 context.data_alloc_pos = layout.rootdir; 1102 1103 strncpy(path, dir, sizeof(path)); 1104 error = udf_populate_walk(fsopts, root, path, &rooticb, &rooticb); 1105 1106 return error; 1107} 1108 1109 1110static void 1111udf_enumerate_and_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts, 1112 struct udf_stats *stats) 1113{ 1114 char path[MAXPATHLEN + 1]; 1115 uint32_t n, nblk; 1116 1117 strncpy(path, dir, sizeof(path)); 1118 1119 /* calculate strict minimal size */ 1120 udf_estimate_walk(fsopts, root, path, stats); 1121 printf("ndirs\t\t%d\n", stats->ndirs); 1122 printf("nfiles\t\t%d\n", stats->nfiles); 1123 printf("ndata_blocks\t%d\n", stats->ndatablocks); 1124 printf("nmetadata_blocks\t%d\n", stats->nmetadatablocks); 1125 1126 /* adjust for options : free file nodes */ 1127 if (fsopts->freefiles) { 1128 /* be mercifull and reserve more for the FID */ 1129 stats->nmetadatablocks += fsopts->freefiles * 1.5; 1130 } else if ((n = fsopts->freefilepc)) { 1131 stats->nmetadatablocks += (stats->nmetadatablocks*n) / (100-n); 1132 } 1133 1134 /* adjust for options : free data blocks */ 1135 if (fsopts->freeblocks) { 1136 stats->ndatablocks += fsopts->freeblocks; 1137 } else if ((n = fsopts->freeblockpc)) { 1138 stats->ndatablocks += (stats->ndatablocks * n) / (100-n); 1139 } 1140 1141 /* rough predictor of minimum disc size */ 1142 nblk = stats->ndatablocks + stats->nmetadatablocks; 1143 nblk = (double) nblk * (1.0 + 1.0/8.0); /* free space map */ 1144 nblk += 256; /* pre-volume space */ 1145 nblk += 256; /* post-volume space */ 1146 nblk += 64; /* safeguard */ 1147 1148 /* try to honour minimum size */ 1149 n = fsopts->minsize / fsopts->sectorsize; 1150 if (nblk < n) { 1151 stats->ndatablocks += (n - nblk); 1152 nblk += n - nblk; 1153 } 1154 fsopts->size = (uint64_t) nblk * fsopts->sectorsize; 1155 1156 /* sanity size */ 1157 if (fsopts->size < 512*1024) 1158 fsopts->size = 512*1024; 1159 1160 fsopts->inodes = stats->nfiles + stats->ndirs; 1161} 1162 1163 1164void 1165udf_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) 1166{ 1167 struct udf_stats stats; 1168 uint64_t truncate_len; 1169 int error; 1170 1171 /* determine format */ 1172 udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile); 1173 printf("req_enable %d, req_disable %d\n", req_enable, req_disable); 1174 1175 context.sector_size = fsopts->sectorsize; 1176 error = udf_derive_format(req_enable, req_disable, false); 1177 if (error) 1178 err(EINVAL, "makefs_udf: can't determine format"); 1179 1180 /* names */ 1181 error = udf_proces_names(); 1182 if (error) 1183 err(EINVAL, "makefs_udf: bad names given"); 1184 1185 /* set return value to 1 indicating error */ 1186 error = 1; 1187 1188 /* estimate the amount of space needed */ 1189 memset(&stats, 0, sizeof(stats)); 1190 udf_enumerate_and_estimate(dir, root, fsopts, &stats); 1191 1192 printf("Calculated size of `%s': %lld bytes, %ld inodes\n", 1193 image, (long long)fsopts->size, (long)fsopts->inodes); 1194 1195 /* create file image */ 1196 if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { 1197 err(EXIT_FAILURE, "%s", image); 1198 } 1199 if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) { 1200 goto err_exit; 1201 } 1202 if (write(fd, &fd, 1) != 1) { 1203 goto err_exit; 1204 } 1205 if (lseek(fd, 0, SEEK_SET) == -1) { 1206 goto err_exit; 1207 } 1208 fsopts->fd = fd; 1209 1210 /* calculate metadata percentage */ 1211 meta_fract = fsopts->size / (stats.nmetadatablocks*fsopts->sectorsize); 1212 meta_fract = ((int) ((meta_fract + 0.005)*100.0)) / 100; 1213 1214 /* update mmc info but now with correct size */ 1215 udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile); 1216 1217 udf_do_newfs_prefix(); 1218 1219 /* update context */ 1220 context.unique_id = 0; 1221 1222 /* XXX are the next two needed? or should be re-count them? */ 1223 context.num_files = stats.nfiles; 1224 context.num_directories = stats.ndirs; 1225 1226 error = udf_populate(dir, root, fsopts, &stats); 1227 1228 udf_do_newfs_postfix(); 1229 1230 if (format_flags & FORMAT_VAT) { 1231 truncate_len = context.vtop_offset[context.data_part] + 1232 context.data_alloc_pos; 1233 truncate_len *= context.sector_size; 1234 1235 printf("\nTruncing the disc-image to allow for VAT\n"); 1236 ftruncate(fd, truncate_len); 1237 } 1238 1239 if (error) { 1240 error = 2; /* some files couldn't be added */ 1241 goto err_exit; 1242 } 1243 1244 close(fd); 1245 return; 1246 1247err_exit: 1248 close(fd); 1249 if (error == 2) { 1250 errx(error, "Not all files could be added"); 1251 } else { 1252 errx(error, "creation of %s failed", image); 1253 } 1254} 1255 1256