/* $NetBSD: udf_core.c,v 1.14 2024/02/05 21:46:05 andvar Exp $ */ /* * Copyright (c) 2006, 2008, 2021, 2022 Reinoud Zandijk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include __RCSID("$NetBSD: udf_core.c,v 1.14 2024/02/05 21:46:05 andvar Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "newfs_udf.h" #include "unicode.h" #include "udf_core.h" /* disk partition support */ #if !HAVE_NBTOOL_CONFIG_H #include "../fsck/partutil.h" #include "../fsck/partutil.c" #endif /* queue for temporary storage of sectors to be written out */ struct wrpacket { uint64_t start_sectornr; uint8_t *packet_data; uint64_t present; TAILQ_ENTRY(wrpacket) next; }; /* global variables describing disc and format requests */ struct udf_create_context context; struct udf_disclayout layout; int dev_fd_rdonly; /* device: open readonly! */ int dev_fd; /* device: file descriptor */ struct stat dev_fd_stat; /* device: last stat info */ char *dev_name; /* device: name */ int emul_mmc_profile; /* for files */ int emul_packetsize; /* for discs and files */ int emul_sectorsize; /* for files */ off_t emul_size; /* for files */ struct mmc_discinfo mmc_discinfo; /* device: disc info */ union dscrptr *terminator_dscr; /* generic terminator descriptor*/ /* write queue and track blocking skew */ TAILQ_HEAD(wrpacket_list, wrpacket) write_queue; int write_queuelen; int write_queue_suspend; uint32_t wrtrack_skew; /* offset for writing sector0 */ static void udf_init_writequeue(int write_strategy); static int udf_writeout_writequeue(bool complete); /* * NOTE that there is some overlap between this code and the udf kernel fs. * This is intentionally though it might better be factored out one day. */ void udf_init_create_context(void) { /* clear */ memset(&context, 0, sizeof(struct udf_create_context)); /* fill with defaults currently known */ context.dscrver = 3; context.min_udf = 0x0102; context.max_udf = 0x0250; context.serialnum = 1; /* default */ context.gmtoff = 0; context.meta_perc = UDF_META_PERC; context.check_surface = 0; context.create_new_session = 0; context.sector_size = 512; /* minimum for UDF */ context.media_accesstype = UDF_ACCESSTYPE_NOT_SPECIFIED; context.format_flags = FORMAT_INVALID; context.write_strategy = UDF_WRITE_PACKET; context.logvol_name = NULL; context.primary_name = NULL; context.volset_name = NULL; context.fileset_name = NULL; /* most basic identification */ context.app_name = "*NetBSD"; context.app_version_main = 0; context.app_version_sub = 0; context.impl_name = "*NetBSD"; context.vds_seq = 0; /* first one starts with zero */ /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */ context.unique_id = 0x10; context.num_files = 0; context.num_directories = 0; context.data_part = 0; context.metadata_part = 0; } /* version can be specified as 0xabc or a.bc */ static int parse_udfversion(const char *pos, uint32_t *version) { int hex = 0; char c1, c2, c3, c4; *version = 0; if (*pos == '0') { pos++; /* expect hex format */ hex = 1; if (*pos++ != 'x') return 1; } c1 = *pos++; if (c1 < '0' || c1 > '9') return 1; c1 -= '0'; c2 = *pos++; if (!hex) { if (c2 != '.') return 1; c2 = *pos++; } if (c2 < '0' || c2 > '9') return 1; c2 -= '0'; c3 = *pos++; if (c3 < '0' || c3 > '9') return 1; c3 -= '0'; c4 = *pos++; if (c4 != 0) return 1; *version = c1 * 0x100 + c2 * 0x10 + c3; return 0; } /* * Parse a given string for an udf version. * May exit. */ int a_udf_version(const char *s, const char *id_type) { uint32_t version; if (parse_udfversion(s, &version)) errx(1, "unknown %s version %s; specify as hex or float", id_type, s); switch (version) { case 0x102: case 0x150: case 0x200: case 0x201: case 0x250: break; case 0x260: /* we don't support this one */ errx(1, "UDF version 0x260 is not supported"); break; default: errx(1, "unknown %s version %s, choose from " "0x102, 0x150, 0x200, 0x201, 0x250", id_type, s); } return version; } static uint32_t udf_space_bitmap_len(uint32_t part_size) { return sizeof(struct space_bitmap_desc)-1 + part_size/8; } uint32_t udf_bytes_to_sectors(uint64_t bytes) { uint32_t sector_size = context.sector_size; return (bytes + sector_size -1) / sector_size; } void udf_dump_layout(void) { #ifdef DEBUG int format_flags = context.format_flags; int sector_size = context.sector_size; printf("Summary so far\n"); printf("\tiso9660_vrs\t\t%d\n", layout.iso9660_vrs); printf("\tanchor0\t\t\t%d\n", layout.anchors[0]); printf("\tanchor1\t\t\t%d\n", layout.anchors[1]); printf("\tanchor2\t\t\t%d\n", layout.anchors[2]); printf("\tvds1_size\t\t%d\n", layout.vds1_size); printf("\tvds2_size\t\t%d\n", layout.vds2_size); printf("\tvds1\t\t\t%d\n", layout.vds1); printf("\tvds2\t\t\t%d\n", layout.vds2); printf("\tlvis_size\t\t%d\n", layout.lvis_size); printf("\tlvis\t\t\t%d\n", layout.lvis); if (format_flags & FORMAT_SPAREABLE) { printf("\tspareable size\t\t%d\n", layout.spareable_area_size); printf("\tspareable\t\t%d\n", layout.spareable_area); } printf("\tpartition start lba\t%d\n", layout.part_start_lba); printf("\tpartition size\t\t%ld KiB, %ld MiB\n", ((uint64_t) layout.part_size_lba * sector_size) / 1024, ((uint64_t) layout.part_size_lba * sector_size) / (1024*1024)); if ((format_flags & FORMAT_SEQUENTIAL) == 0) { printf("\tpart bitmap start\t%d\n", layout.unalloc_space); printf("\t\tfor %d lba\n", layout.alloc_bitmap_dscr_size); } if (format_flags & FORMAT_META) { printf("\tmeta blockingnr\t\t%d\n", layout.meta_blockingnr); printf("\tmeta alignment\t\t%d\n", layout.meta_alignment); printf("\tmeta size\t\t%ld KiB, %ld MiB\n", ((uint64_t) layout.meta_part_size_lba * sector_size) / 1024, ((uint64_t) layout.meta_part_size_lba * sector_size) / (1024*1024)); printf("\tmeta file\t\t%d\n", layout.meta_file); printf("\tmeta mirror\t\t%d\n", layout.meta_mirror); printf("\tmeta bitmap\t\t%d\n", layout.meta_bitmap); printf("\tmeta bitmap start\t%d\n", layout.meta_bitmap_space); printf("\t\tfor %d lba\n", layout.meta_bitmap_dscr_size); printf("\tmeta space start\t%d\n", layout.meta_part_start_lba); printf("\t\tfor %d lba\n", layout.meta_part_size_lba); } printf("\n"); #endif } int udf_calculate_disc_layout(int min_udf, uint32_t first_lba, uint32_t last_lba, uint32_t sector_size, uint32_t blockingnr) { uint64_t kbsize, bytes; uint32_t spareable_blockingnr; uint32_t align_blockingnr; uint32_t pos, mpos; int format_flags = context.format_flags; /* clear */ memset(&layout, 0, sizeof(layout)); /* fill with parameters */ layout.wrtrack_skew = wrtrack_skew; layout.first_lba = first_lba; layout.last_lba = last_lba; layout.blockingnr = blockingnr; layout.spareable_blocks = udf_spareable_blocks(); /* start disc layouting */ /* * location of iso9660 vrs is defined as first sector AFTER 32kb, * minimum `sector size' 2048 */ layout.iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + first_lba; /* anchor starts at specified offset in sectors */ layout.anchors[0] = first_lba + 256; if (format_flags & FORMAT_TRACK512) layout.anchors[0] = first_lba + 512; layout.anchors[1] = last_lba - 256; layout.anchors[2] = last_lba; /* update workable space */ first_lba = layout.anchors[0] + blockingnr; last_lba = layout.anchors[1] - 1; /* XXX rest of anchor packet can be added to unallocated space descr */ /* reserve space for VRS and VRS copy and associated tables */ layout.vds1_size = MAX(16, blockingnr); /* UDF 2.2.3.1+2 */ layout.vds1 = first_lba; first_lba += layout.vds1_size; /* next packet */ layout.vds2_size = layout.vds1_size; if (format_flags & FORMAT_SEQUENTIAL) { /* for sequential, append them ASAP */ layout.vds2 = first_lba; first_lba += layout.vds2_size; } else { layout.vds2 = layout.anchors[1] +1 - layout.vds2_size; last_lba = layout.vds2 - 1; } /* * Reserve space for logvol integrity sequence, at least 8192 bytes * for overwritable and rewritable media UDF 2.2.4.6, ECMA 3/10.6.12. */ layout.lvis_size = MAX(8192.0/sector_size, 2 * blockingnr); if (layout.lvis_size * sector_size < 8192) layout.lvis_size++; if (format_flags & FORMAT_VAT) layout.lvis_size = 2; if (format_flags & FORMAT_WORM) layout.lvis_size = 64 * blockingnr; /* TODO skip bad blocks in LVID sequence */ layout.lvis = first_lba; first_lba += layout.lvis_size; /* initial guess of UDF partition size */ layout.part_start_lba = first_lba; layout.part_size_lba = last_lba - layout.part_start_lba; /* all non sequential media needs an unallocated space bitmap */ layout.alloc_bitmap_dscr_size = 0; if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { bytes = udf_space_bitmap_len(layout.part_size_lba); layout.alloc_bitmap_dscr_size = udf_bytes_to_sectors(bytes); /* XXX freed space map when applicable */ } spareable_blockingnr = udf_spareable_blockingnr(); align_blockingnr = blockingnr; if (format_flags & (FORMAT_SPAREABLE | FORMAT_META)) align_blockingnr = spareable_blockingnr; layout.align_blockingnr = align_blockingnr; layout.spareable_blockingnr = spareable_blockingnr; /* * Align partition LBA space to blocking granularity. Not strictly * necessary for non spareables but safer for the VRS data since it is * updated sporadically */ #ifdef DEBUG printf("Lost %lu slack sectors at start\n", UDF_ROUNDUP( first_lba, align_blockingnr) - first_lba); printf("Lost %lu slack sectors at end\n", last_lba - UDF_ROUNDDOWN( last_lba, align_blockingnr)); #endif first_lba = UDF_ROUNDUP(first_lba, align_blockingnr); last_lba = UDF_ROUNDDOWN(last_lba, align_blockingnr); if ((format_flags & FORMAT_SPAREABLE) == 0) layout.spareable_blocks = 0; if (format_flags & FORMAT_SPAREABLE) { layout.spareable_area_size = layout.spareable_blocks * spareable_blockingnr; /* a sparing table descriptor is a whole blockingnr sectors */ layout.sparing_table_dscr_lbas = spareable_blockingnr; /* place the descriptors at the start and end of the area */ layout.spt_1 = first_lba; first_lba += layout.sparing_table_dscr_lbas; layout.spt_2 = last_lba - layout.sparing_table_dscr_lbas; last_lba -= layout.sparing_table_dscr_lbas; /* allocate spareable section */ layout.spareable_area = first_lba; first_lba += layout.spareable_area_size; } /* update guess of UDF partition size */ layout.part_start_lba = first_lba; layout.part_size_lba = last_lba - layout.part_start_lba; /* determine partition selection for data and metadata */ context.data_part = 0; context.metadata_part = context.data_part; if ((format_flags & FORMAT_VAT) || (format_flags & FORMAT_META)) context.metadata_part = context.data_part + 1; context.fids_part = context.metadata_part; if (format_flags & FORMAT_VAT) context.fids_part = context.data_part; /* * Pick fixed logical space sector numbers for main FSD, rootdir and * unallocated space. The reason for this pre-allocation is that they * are referenced in the volume descriptor sequence and hence can't be * allocated later. */ pos = 0; layout.unalloc_space = pos; pos += layout.alloc_bitmap_dscr_size; /* claim metadata descriptors and partition space [UDF 2.2.10] */ if (format_flags & FORMAT_META) { /* note: all in backing partition space */ layout.meta_file = pos++; layout.meta_bitmap = 0xffffffff; if (!(context.format_flags & FORMAT_READONLY)) layout.meta_bitmap = pos++; layout.meta_mirror = layout.part_size_lba-1; layout.meta_alignment = MAX(blockingnr, spareable_blockingnr); layout.meta_blockingnr = MAX(layout.meta_alignment, 32); /* calculate our partition length and store in sectors */ layout.meta_part_size_lba = layout.part_size_lba * ((float) context.meta_perc / 100.0); layout.meta_part_size_lba = MAX(layout.meta_part_size_lba, 32); layout.meta_part_size_lba = UDF_ROUNDDOWN(layout.meta_part_size_lba, layout.meta_blockingnr); if (!(context.format_flags & FORMAT_READONLY)) { /* metadata partition free space bitmap */ bytes = udf_space_bitmap_len(layout.meta_part_size_lba); layout.meta_bitmap_dscr_size = udf_bytes_to_sectors(bytes); layout.meta_bitmap_space = pos; pos += layout.meta_bitmap_dscr_size; } layout.meta_part_start_lba = UDF_ROUNDUP(pos, layout.meta_alignment); pos = layout.meta_part_start_lba + layout.meta_part_size_lba; } if (context.metadata_part == context.data_part) { mpos = pos; layout.fsd = mpos; mpos += 1; layout.rootdir = mpos; pos = mpos; } else { mpos = 0; layout.fsd = mpos; mpos += 1; layout.rootdir = mpos; } /* pos and mpos now refer to the rootdir block */ context.alloc_pos[context.data_part] = pos; context.alloc_pos[context.metadata_part] = mpos; udf_dump_layout(); kbsize = (uint64_t) last_lba * sector_size; printf("Total space on this medium approx. " "%"PRIu64" KiB, %"PRIu64" MiB\n", kbsize/1024, kbsize/(1024*1024)); kbsize = (uint64_t)(layout.part_size_lba - layout.alloc_bitmap_dscr_size - layout.meta_bitmap_dscr_size) * sector_size; printf("Recordable free space on this volume approx. " "%"PRIu64" KiB, %"PRIu64" MiB\n\n", kbsize/1024, kbsize/(1024*1024)); return 0; } /* * Check if the blob starts with a good UDF tag. Tags are protected by a * checksum over the header, except one byte at position 4 that is the * checksum itself. */ int udf_check_tag(void *blob) { struct desc_tag *tag = blob; uint8_t *pos, sum, cnt; /* check TAG header checksum */ pos = (uint8_t *) tag; sum = 0; for(cnt = 0; cnt < 16; cnt++) { if (cnt != 4) sum += *pos; pos++; } if (sum != tag->cksum) { /* bad tag header checksum; this is not a valid tag */ return EINVAL; } return 0; } /* * check tag payload will check descriptor CRC as specified. * If the descriptor is too long, it will return EIO otherwise EINVAL. */ int udf_check_tag_payload(void *blob, uint32_t max_length) { struct desc_tag *tag = blob; uint16_t crc, crc_len; crc_len = udf_rw16(tag->desc_crc_len); /* check payload CRC if applicable */ if (crc_len == 0) return 0; if (crc_len > max_length) return EIO; crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, crc_len); if (crc != udf_rw16(tag->desc_crc)) { /* bad payload CRC; this is a broken tag */ return EINVAL; } return 0; } int udf_check_tag_and_location(void *blob, uint32_t location) { struct desc_tag *tag = blob; if (udf_check_tag(blob)) return 1; if (udf_rw32(tag->tag_loc) != location) return 1; return 0; } int udf_validate_tag_sum(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint8_t *pos, sum, cnt; /* calculate TAG header checksum */ pos = (uint8_t *) tag; sum = 0; for (cnt = 0; cnt < 16; cnt++) { if (cnt != 4) sum += *pos; pos++; }; tag->cksum = sum; /* 8 bit */ return 0; } /* assumes sector number of descriptor to be already present */ int udf_validate_tag_and_crc_sums(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint16_t crc; /* check payload CRC if applicable */ if (udf_rw16(tag->desc_crc_len) > 0) { crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len)); tag->desc_crc = udf_rw16(crc); }; /* calculate TAG header checksum */ return udf_validate_tag_sum(dscr); } void udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc) { tag->id = udf_rw16(tagid); tag->descriptor_ver = udf_rw16(context.dscrver); tag->cksum = 0; tag->reserved = 0; tag->serial_num = udf_rw16(context.serialnum); tag->tag_loc = udf_rw32(loc); } int udf_create_anchor(int num) { struct anchor_vdp *avdp; uint32_t vds1_extent_len = layout.vds1_size * context.sector_size; uint32_t vds2_extent_len = layout.vds2_size * context.sector_size; avdp = context.anchors[num]; if (!avdp) if ((avdp = calloc(1, context.sector_size)) == NULL) return ENOMEM; udf_inittag(&avdp->tag, TAGID_ANCHOR, layout.anchors[num]); avdp->main_vds_ex.loc = udf_rw32(layout.vds1); avdp->main_vds_ex.len = udf_rw32(vds1_extent_len); avdp->reserve_vds_ex.loc = udf_rw32(layout.vds2); avdp->reserve_vds_ex.len = udf_rw32(vds2_extent_len); /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ avdp->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); context.anchors[num] = avdp; return 0; } void udf_create_terminator(union dscrptr *dscr, uint32_t loc) { memset(dscr, 0, context.sector_size); udf_inittag(&dscr->tag, TAGID_TERM, loc); /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); } void udf_osta_charset(struct charspec *charspec) { memset(charspec, 0, sizeof(*charspec)); charspec->type = 0; strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); } /* ---- shared from kernel's udf_subr.c, slightly modified ---- */ void udf_to_unix_name(char *result, int result_len, char *id, int len, struct charspec *chsp) { uint16_t *raw_name, *unix_name; uint16_t *inchp, ch; char *outchp; const char *osta_id = "OSTA Compressed Unicode"; int ucode_chars, nice_uchars, is_osta_typ0, nout; raw_name = malloc(2048 * sizeof(uint16_t)); assert(raw_name); unix_name = raw_name + 1024; /* split space in half */ assert(sizeof(char) == sizeof(uint8_t)); outchp = result; is_osta_typ0 = (chsp->type == 0); is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); if (is_osta_typ0) { /* TODO clean up */ *raw_name = *unix_name = 0; ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); ucode_chars = MIN(ucode_chars, UnicodeLength((unicode_t *) raw_name)); nice_uchars = UDFTransName(unix_name, raw_name, ucode_chars); /* output UTF8 */ for (inchp = unix_name; nice_uchars>0; inchp++, nice_uchars--) { ch = *inchp; nout = wput_utf8(outchp, result_len, ch); outchp += nout; result_len -= nout; if (!ch) break; } *outchp++ = 0; } else { /* assume 8bit char length byte latin-1 */ assert(*id == 8); assert(strlen((char *) (id+1)) <= NAME_MAX); memcpy((char *) result, (char *) (id+1), strlen((char *) (id+1))); } free(raw_name); } void unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len, struct charspec *chsp) { uint16_t *raw_name; uint16_t *outchp; const char *inchp; const char *osta_id = "OSTA Compressed Unicode"; int udf_chars, is_osta_typ0, bits; size_t cnt; /* allocate temporary unicode-16 buffer */ raw_name = malloc(1024); assert(raw_name); /* convert utf8 to unicode-16 */ *raw_name = 0; inchp = name; outchp = raw_name; bits = 8; for (cnt = name_len, udf_chars = 0; cnt;) { *outchp = wget_utf8(&inchp, &cnt); if (*outchp > 0xff) bits=16; outchp++; udf_chars++; } /* null terminate just in case */ *outchp++ = 0; is_osta_typ0 = (chsp->type == 0); is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); if (is_osta_typ0) { udf_chars = udf_CompressUnicode(udf_chars, bits, (unicode_t *) raw_name, (byte *) result); } else { printf("unix to udf name: no CHSP0 ?\n"); /* XXX assume 8bit char length byte latin-1 */ *result++ = 8; udf_chars = 1; strncpy(result, name + 1, name_len); udf_chars += name_len; } *result_len = udf_chars; free(raw_name); } /* first call udf_set_regid and then the suffix */ void udf_set_regid(struct regid *regid, char const *name) { memset(regid, 0, sizeof(*regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, name); } void udf_add_domain_regid(struct regid *regid) { uint16_t *ver; ver = (uint16_t *) regid->id_suffix; *ver = udf_rw16(context.min_udf); } void udf_add_udf_regid(struct regid *regid) { uint16_t *ver; ver = (uint16_t *) regid->id_suffix; *ver = udf_rw16(context.min_udf); regid->id_suffix[2] = 4; /* unix */ regid->id_suffix[3] = 8; /* NetBSD */ } void udf_add_impl_regid(struct regid *regid) { regid->id_suffix[0] = 4; /* unix */ regid->id_suffix[1] = 8; /* NetBSD */ } void udf_add_app_regid(struct regid *regid) { regid->id_suffix[0] = context.app_version_main; regid->id_suffix[1] = context.app_version_sub; } /* * Timestamp to timespec conversion code is taken with small modifications * from FreeBSD /sys/fs/udf by Scott Long */ static int mon_lens[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static int udf_isaleapyear(int year) { int i; i = (year % 4) ? 0 : 1; i &= (year % 100) ? 1 : 0; i |= (year % 400) ? 0 : 1; return i; } void udf_timestamp_to_timespec(struct timestamp *timestamp, struct timespec *timespec) { uint32_t usecs, secs, nsecs; uint16_t tz; int i, lpyear, daysinyear, year; timespec->tv_sec = secs = 0; timespec->tv_nsec = nsecs = 0; /* * DirectCD seems to like using bogus year values. * Distrust time->month especially, since it will be used for an array * index. */ year = udf_rw16(timestamp->year); if ((year < 1970) || (timestamp->month > 12)) { return; } /* Calculate the time and day */ usecs = timestamp->usec + 100*timestamp->hund_usec + 10000*timestamp->centisec; nsecs = usecs * 1000; secs = timestamp->second; secs += timestamp->minute * 60; secs += timestamp->hour * 3600; secs += (timestamp->day-1) * 3600 * 24; /* day : 1-31 */ /* Calclulate the month */ lpyear = udf_isaleapyear(year); for (i = 1; i < timestamp->month; i++) secs += mon_lens[lpyear][i-1] * 3600 * 24; /* month: 1-12 */ for (i = 1970; i < year; i++) { daysinyear = udf_isaleapyear(i) + 365 ; secs += daysinyear * 3600 * 24; } /* * Calculate the time zone. The timezone is 12 bit signed 2's * compliment, so we gotta do some extra magic to handle it right. */ tz = udf_rw16(timestamp->type_tz); tz &= 0x0fff; /* only lower 12 bits are significant */ if (tz & 0x0800) /* sign extension */ tz |= 0xf000; /* TODO check timezone conversion */ #if 1 /* check if we are specified a timezone to convert */ if (udf_rw16(timestamp->type_tz) & 0x1000) if ((int16_t) tz != -2047) secs -= (int16_t) tz * 60; #endif timespec->tv_sec = secs; timespec->tv_nsec = nsecs; } /* * Fill in timestamp structure based on clock_gettime(). Time is reported back * as a time_t accompanied with a nano second field. * * The husec, usec and csec could be relaxed in type. */ void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) { struct tm tm; uint64_t husec, usec, csec; memset(timestamp, 0, sizeof(*timestamp)); gmtime_r(×pec->tv_sec, &tm); /* * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1. * * Lower 12 bits are two complement signed timezone offset if bit 12 * (method 1) is clear. Otherwise if bit 12 is set, specify timezone * offset to -2047 i.e. unsigned `zero' */ /* set method 1 for CUT/GMT */ timestamp->type_tz = udf_rw16((1<<12) + 0); timestamp->year = udf_rw16(tm.tm_year + 1900); timestamp->month = tm.tm_mon + 1; /* `tm' uses 0..11 for months */ timestamp->day = tm.tm_mday; timestamp->hour = tm.tm_hour; timestamp->minute = tm.tm_min; timestamp->second = tm.tm_sec; usec = (timespec->tv_nsec + 500) / 1000; /* round */ husec = usec / 100; usec -= husec * 100; /* only 0-99 in usec */ csec = husec / 100; /* only 0-99 in csec */ husec -= csec * 100; /* only 0-99 in husec */ /* in rare cases there is overflow in csec */ csec = MIN(99, csec); husec = MIN(99, husec); usec = MIN(99, usec); timestamp->centisec = csec; timestamp->hund_usec = husec; timestamp->usec = usec; } static void udf_set_timestamp(struct timestamp *timestamp, time_t value) { struct timespec t; memset(&t, 0, sizeof(struct timespec)); t.tv_sec = value; t.tv_nsec = 0; udf_timespec_to_timestamp(&t, timestamp); } static uint32_t unix_mode_to_udf_perm(mode_t mode) { uint32_t perm; perm = ((mode & S_IRWXO) ); perm |= ((mode & S_IRWXG) << 2); perm |= ((mode & S_IRWXU) << 4); perm |= ((mode & S_IWOTH) << 3); perm |= ((mode & S_IWGRP) << 5); perm |= ((mode & S_IWUSR) << 7); return perm; } /* end of copied code */ void udf_encode_osta_id(char *osta_id, uint16_t len, char *text) { struct charspec osta_charspec; uint8_t result_len; memset(osta_id, 0, len); if (!text || (strlen(text) == 0)) return; udf_osta_charset(&osta_charspec); unix_to_udf_name(osta_id, &result_len, text, strlen(text), &osta_charspec); /* Ecma 167/7.2.13 states that length is recorded in the last byte */ osta_id[len-1] = strlen(text)+1; } void udf_set_timestamp_now(struct timestamp *timestamp) { struct timespec now; #ifdef CLOCK_REALTIME (void)clock_gettime(CLOCK_REALTIME, &now); #else struct timeval time_of_day; (void)gettimeofday(&time_of_day, NULL); now.tv_sec = time_of_day.tv_sec; now.tv_nsec = time_of_day.tv_usec * 1000; #endif udf_timespec_to_timestamp(&now, timestamp); } int udf_create_primaryd(void) { struct pri_vol_desc *pri; uint16_t crclen; pri = calloc(1, context.sector_size); if (pri == NULL) return ENOMEM; memset(pri, 0, context.sector_size); udf_inittag(&pri->tag, TAGID_PRI_VOL, /* loc */ 0); pri->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; pri->pvd_num = udf_rw32(0); /* default serial */ udf_encode_osta_id(pri->vol_id, 32, context.primary_name); /* set defaults for single disc volumes as UDF prescribes */ pri->vds_num = udf_rw16(1); pri->max_vol_seq = udf_rw16(1); pri->ichg_lvl = udf_rw16(2); pri->max_ichg_lvl = udf_rw16(3); pri->flags = udf_rw16(0); pri->charset_list = udf_rw32(1); /* only CS0 */ pri->max_charset_list = udf_rw32(1); /* only CS0 */ udf_encode_osta_id(pri->volset_id, 128, context.volset_name); udf_osta_charset(&pri->desc_charset); udf_osta_charset(&pri->explanatory_charset); udf_set_regid(&pri->app_id, context.app_name); udf_add_app_regid(&pri->app_id); udf_set_regid(&pri->imp_id, context.impl_name); udf_add_impl_regid(&pri->imp_id); udf_set_timestamp_now(&pri->time); crclen = sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH; pri->tag.desc_crc_len = udf_rw16(crclen); context.primary_vol = pri; return 0; } /* * BUGALERT: some rogue implementations use random physical partition * numbers to break other implementations so lookup the number. */ uint16_t udf_find_raw_phys(uint16_t raw_phys_part) { struct part_desc *part; uint16_t phys_part; for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { part = context.partitions[phys_part]; if (part == NULL) break; if (udf_rw16(part->part_num) == raw_phys_part) break; } return phys_part; } /* XXX no support for unallocated or freed space tables yet (!) */ int udf_create_partitiond(int part_num) { struct part_desc *pd; struct part_hdr_desc *phd; uint32_t sector_size, bitmap_bytes; uint16_t crclen; int part_accesstype = context.media_accesstype; sector_size = context.sector_size; bitmap_bytes = layout.alloc_bitmap_dscr_size * sector_size; if (context.partitions[part_num]) errx(1, "internal error, partition %d already defined in %s", part_num, __func__); pd = calloc(1, context.sector_size); if (pd == NULL) return ENOMEM; phd = &pd->_impl_use.part_hdr; udf_inittag(&pd->tag, TAGID_PARTITION, /* loc */ 0); pd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; pd->flags = udf_rw16(1); /* allocated */ pd->part_num = udf_rw16(part_num); /* only one physical partition */ if (context.dscrver == 2) { udf_set_regid(&pd->contents, "+NSR02"); } else { udf_set_regid(&pd->contents, "+NSR03"); } udf_add_app_regid(&pd->contents); phd->unalloc_space_bitmap.len = udf_rw32(bitmap_bytes); phd->unalloc_space_bitmap.lb_num = udf_rw32(layout.unalloc_space); if (layout.freed_space) { phd->freed_space_bitmap.len = udf_rw32(bitmap_bytes); phd->freed_space_bitmap.lb_num = udf_rw32(layout.freed_space); } pd->access_type = udf_rw32(part_accesstype); pd->start_loc = udf_rw32(layout.part_start_lba); pd->part_len = udf_rw32(layout.part_size_lba); udf_set_regid(&pd->imp_id, context.impl_name); udf_add_impl_regid(&pd->imp_id); crclen = sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH; pd->tag.desc_crc_len = udf_rw16(crclen); context.partitions[part_num] = pd; return 0; } int udf_create_unalloc_spaced(void) { struct unalloc_sp_desc *usd; uint16_t crclen; usd = calloc(1, context.sector_size); if (usd == NULL) return ENOMEM; udf_inittag(&usd->tag, TAGID_UNALLOC_SPACE, /* loc */ 0); usd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; /* no default entries */ usd->alloc_desc_num = udf_rw32(0); /* no entries */ crclen = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad); crclen -= UDF_DESC_TAG_LENGTH; usd->tag.desc_crc_len = udf_rw16(crclen); context.unallocated = usd; return 0; } static int udf_create_base_logical_dscr(void) { struct logvol_desc *lvd; uint32_t sector_size; uint16_t crclen; sector_size = context.sector_size; lvd = calloc(1, sector_size); if (lvd == NULL) return ENOMEM; udf_inittag(&lvd->tag, TAGID_LOGVOL, /* loc */ 0); lvd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; udf_osta_charset(&lvd->desc_charset); udf_encode_osta_id(lvd->logvol_id, 128, context.logvol_name); lvd->lb_size = udf_rw32(sector_size); udf_set_regid(&lvd->domain_id, "*OSTA UDF Compliant"); udf_add_domain_regid(&lvd->domain_id); /* no partition mappings/entries yet */ lvd->mt_l = udf_rw32(0); lvd->n_pm = udf_rw32(0); udf_set_regid(&lvd->imp_id, context.impl_name); udf_add_impl_regid(&lvd->imp_id); lvd->integrity_seq_loc.loc = udf_rw32(layout.lvis); lvd->integrity_seq_loc.len = udf_rw32(layout.lvis_size * sector_size); /* just one fsd for now */ lvd->lv_fsd_loc.len = udf_rw32(sector_size); lvd->lv_fsd_loc.loc.part_num = udf_rw16(context.metadata_part); lvd->lv_fsd_loc.loc.lb_num = udf_rw32(layout.fsd); crclen = sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH; lvd->tag.desc_crc_len = udf_rw16(crclen); context.logical_vol = lvd; context.vtop_tp[UDF_VTOP_RAWPART] = UDF_VTOP_TYPE_RAW; return 0; } static void udf_add_logvol_part_physical(uint16_t phys_part) { struct logvol_desc *logvol = context.logical_vol; union udf_pmap *pmap; uint8_t *pmap_pos; uint16_t crclen; uint32_t pmap1_size, log_part; log_part = udf_rw32(logvol->n_pm); pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmap1_size = sizeof(struct part_map_1); pmap = (union udf_pmap *) pmap_pos; pmap->pm1.type = 1; pmap->pm1.len = sizeof(struct part_map_1); pmap->pm1.vol_seq_num = udf_rw16(1); /* no multi-volume */ pmap->pm1.part_num = udf_rw16(phys_part); context.vtop [log_part] = phys_part; context.vtop_tp [log_part] = UDF_VTOP_TYPE_PHYS; context.part_size[log_part] = layout.part_size_lba; context.part_free[log_part] = layout.part_size_lba; /* increment number of partitions and length */ logvol->n_pm = udf_rw32(log_part + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmap1_size); crclen = udf_rw16(logvol->tag.desc_crc_len) + pmap1_size; logvol->tag.desc_crc_len = udf_rw16(crclen); } static void udf_add_logvol_part_virtual(uint16_t phys_part) { union udf_pmap *pmap; struct logvol_desc *logvol = context.logical_vol; uint8_t *pmap_pos; uint16_t crclen; uint32_t pmapv_size, log_part; log_part = udf_rw32(logvol->n_pm); pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmapv_size = sizeof(struct part_map_2); pmap = (union udf_pmap *) pmap_pos; pmap->pmv.type = 2; pmap->pmv.len = pmapv_size; udf_set_regid(&pmap->pmv.id, "*UDF Virtual Partition"); udf_add_udf_regid(&pmap->pmv.id); pmap->pmv.vol_seq_num = udf_rw16(1); /* no multi-volume */ pmap->pmv.part_num = udf_rw16(phys_part); context.vtop [log_part] = phys_part; context.vtop_tp [log_part] = UDF_VTOP_TYPE_VIRT; context.part_size[log_part] = 0xffffffff; context.part_free[log_part] = 0xffffffff; /* increment number of partitions and length */ logvol->n_pm = udf_rw32(log_part + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size); crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size; logvol->tag.desc_crc_len = udf_rw16(crclen); } /* sparing table size is in bytes */ static void udf_add_logvol_part_spareable(uint16_t phys_part) { union udf_pmap *pmap; struct logvol_desc *logvol = context.logical_vol; uint32_t *st_pos, spareable_bytes, pmaps_size; uint8_t *pmap_pos, num; uint16_t crclen; uint32_t log_part; log_part = udf_rw32(logvol->n_pm); pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmaps_size = sizeof(struct part_map_2); spareable_bytes = layout.spareable_area_size * context.sector_size; pmap = (union udf_pmap *) pmap_pos; pmap->pms.type = 2; pmap->pms.len = pmaps_size; udf_set_regid(&pmap->pmv.id, "*UDF Sparable Partition"); udf_add_udf_regid(&pmap->pmv.id); pmap->pms.vol_seq_num = udf_rw16(1); /* no multi-volume */ pmap->pms.part_num = udf_rw16(phys_part); pmap->pms.packet_len = udf_rw16(layout.spareable_blockingnr); pmap->pms.st_size = udf_rw32(spareable_bytes); /* enter spare tables */ st_pos = &pmap->pms.st_loc[0]; *st_pos++ = udf_rw32(layout.spt_1); *st_pos++ = udf_rw32(layout.spt_2); num = 2; if (layout.spt_2 == 0) num--; if (layout.spt_1 == 0) num--; pmap->pms.n_st = num; /* 8 bit */ context.vtop [log_part] = phys_part; context.vtop_tp [log_part] = UDF_VTOP_TYPE_SPAREABLE; context.part_size[log_part] = layout.part_size_lba; context.part_free[log_part] = layout.part_size_lba; /* increment number of partitions and length */ logvol->n_pm = udf_rw32(log_part + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmaps_size); crclen = udf_rw16(logvol->tag.desc_crc_len) + pmaps_size; logvol->tag.desc_crc_len = udf_rw16(crclen); } int udf_create_sparing_tabled(void) { struct udf_sparing_table *spt; struct spare_map_entry *sme; uint32_t loc, cnt; uint32_t crclen; /* XXX: should be 16; need to detect overflow */ spt = calloc(context.sector_size, layout.sparing_table_dscr_lbas); if (spt == NULL) return ENOMEM; /* a sparing table descriptor is a whole spareable_blockingnr sectors */ udf_inittag(&spt->tag, TAGID_SPARING_TABLE, /* loc */ 0); udf_set_regid(&spt->id, "*UDF Sparing Table"); udf_add_udf_regid(&spt->id); spt->rt_l = udf_rw16(layout.spareable_blocks); spt->seq_num = udf_rw32(0); /* first generation */ for (cnt = 0; cnt < layout.spareable_blocks; cnt++) { sme = &spt->entries[cnt]; loc = layout.spareable_area + cnt * layout.spareable_blockingnr; sme->org = udf_rw32(0xffffffff); /* open for reloc */ sme->map = udf_rw32(loc); } /* calculate crc len for actual size */ crclen = sizeof(struct udf_sparing_table) - UDF_DESC_TAG_LENGTH; crclen += (layout.spareable_blocks-1) * sizeof(struct spare_map_entry); assert(crclen <= UINT16_MAX); spt->tag.desc_crc_len = udf_rw16((uint16_t)crclen); context.sparing_table = spt; return 0; } static void udf_add_logvol_part_meta(uint16_t phys_part) { union udf_pmap *pmap; struct logvol_desc *logvol = context.logical_vol; uint8_t *pmap_pos; uint32_t pmapv_size, log_part; uint16_t crclen; log_part = udf_rw32(logvol->n_pm); pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmapv_size = sizeof(struct part_map_2); pmap = (union udf_pmap *) pmap_pos; pmap->pmm.type = 2; pmap->pmm.len = pmapv_size; udf_set_regid(&pmap->pmm.id, "*UDF Metadata Partition"); udf_add_udf_regid(&pmap->pmm.id); pmap->pmm.vol_seq_num = udf_rw16(1); /* no multi-volume */ pmap->pmm.part_num = udf_rw16(phys_part); /* fill in meta data file(s) and alloc/alignment unit sizes */ pmap->pmm.meta_file_lbn = udf_rw32(layout.meta_file); pmap->pmm.meta_mirror_file_lbn = udf_rw32(layout.meta_mirror); pmap->pmm.meta_bitmap_file_lbn = udf_rw32(layout.meta_bitmap); pmap->pmm.alloc_unit_size = udf_rw32(layout.meta_blockingnr); pmap->pmm.alignment_unit_size = udf_rw16(layout.meta_alignment); pmap->pmm.flags = 0; /* METADATA_DUPLICATED */ context.vtop [log_part] = phys_part; context.vtop_tp [log_part] = UDF_VTOP_TYPE_META; context.part_size[log_part] = layout.meta_part_size_lba; context.part_free[log_part] = layout.meta_part_size_lba; /* increment number of partitions and length */ logvol->n_pm = udf_rw32(log_part + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size); crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size; logvol->tag.desc_crc_len = udf_rw16(crclen); } int udf_create_logical_dscr(void) { int error; if ((error = udf_create_base_logical_dscr())) return error; /* we pass data_part for there might be a read-only part one day */ if (context.format_flags & FORMAT_SPAREABLE) { /* spareable partition mapping has no physical mapping */ udf_add_logvol_part_spareable(context.data_part); } else { udf_add_logvol_part_physical(context.data_part); } if (context.format_flags & FORMAT_VAT) { /* add VAT virtual mapping; reflects on datapart */ udf_add_logvol_part_virtual(context.data_part); } if (context.format_flags & FORMAT_META) { /* add META data mapping; reflects on datapart */ udf_add_logvol_part_meta(context.data_part); } return 0; } int udf_create_impvold(char *field1, char *field2, char *field3) { struct impvol_desc *ivd; struct udf_lv_info *lvi; uint16_t crclen; ivd = calloc(1, context.sector_size); if (ivd == NULL) return ENOMEM; lvi = &ivd->_impl_use.lv_info; udf_inittag(&ivd->tag, TAGID_IMP_VOL, /* loc */ 0); ivd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; udf_set_regid(&ivd->impl_id, "*UDF LV Info"); udf_add_udf_regid(&ivd->impl_id); /* fill in UDF specific part */ udf_osta_charset(&lvi->lvi_charset); udf_encode_osta_id(lvi->logvol_id, 128, context.logvol_name); udf_encode_osta_id(lvi->lvinfo1, 36, field1); udf_encode_osta_id(lvi->lvinfo2, 36, field2); udf_encode_osta_id(lvi->lvinfo3, 36, field3); udf_set_regid(&lvi->impl_id, context.impl_name); udf_add_impl_regid(&lvi->impl_id); crclen = sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH; ivd->tag.desc_crc_len = udf_rw16(crclen); context.implementation = ivd; return 0; } /* XXX might need to be sanitised a bit */ void udf_update_lvintd(int type) { struct logvol_int_desc *lvid; struct udf_logvol_info *lvinfo; struct logvol_desc *logvol; uint32_t *pos; uint32_t cnt, num_partmappings; uint32_t crclen; /* XXX: should be 16; need to detect overflow */ lvid = context.logvol_integrity; logvol = context.logical_vol; assert(lvid); assert(logvol); lvid->integrity_type = udf_rw32(type); udf_set_timestamp_now(&lvid->time); /* initialise lvinfo just in case its not set yet */ num_partmappings = udf_rw32(logvol->n_pm); assert(num_partmappings > 0); lvinfo = (struct udf_logvol_info *) (lvid->tables + num_partmappings * 2); context.logvol_info = lvinfo; udf_set_regid(&lvinfo->impl_id, context.impl_name); udf_add_impl_regid(&lvinfo->impl_id); if (type == UDF_INTEGRITY_CLOSED) { lvinfo->num_files = udf_rw32(context.num_files); lvinfo->num_directories = udf_rw32(context.num_directories); lvid->lvint_next_unique_id = udf_rw64(context.unique_id); } /* sane enough? */ if (udf_rw16(lvinfo->min_udf_readver) < context.min_udf) lvinfo->min_udf_readver = udf_rw16(context.min_udf); if (udf_rw16(lvinfo->min_udf_writever) < context.min_udf) lvinfo->min_udf_writever = udf_rw16(context.min_udf); if (udf_rw16(lvinfo->max_udf_writever) < context.max_udf) lvinfo->max_udf_writever = udf_rw16(context.max_udf); lvid->num_part = udf_rw32(num_partmappings); pos = &lvid->tables[0]; for (cnt = 0; cnt < num_partmappings; cnt++) { *pos++ = udf_rw32(context.part_free[cnt]); } for (cnt = 0; cnt < num_partmappings; cnt++) { *pos++ = udf_rw32(context.part_size[cnt]); } crclen = sizeof(struct logvol_int_desc) -4 -UDF_DESC_TAG_LENGTH + udf_rw32(lvid->l_iu); crclen += num_partmappings * 2 * 4; assert(crclen <= UINT16_MAX); if (lvid->tag.desc_crc_len == 0) lvid->tag.desc_crc_len = udf_rw16(crclen); context.logvol_info = lvinfo; } int udf_create_lvintd(int type) { struct logvol_int_desc *lvid; int l_iu; lvid = calloc(1, context.sector_size); if (lvid == NULL) return ENOMEM; udf_inittag(&lvid->tag, TAGID_LOGVOL_INTEGRITY, /* loc */ 0); context.logvol_integrity = lvid; /* only set for standard UDF info, no extra impl. use needed */ l_iu = sizeof(struct udf_logvol_info); lvid->l_iu = udf_rw32(l_iu); udf_update_lvintd(type); return 0; } int udf_create_fsd(void) { struct fileset_desc *fsd; uint16_t crclen; fsd = calloc(1, context.sector_size); if (fsd == NULL) return ENOMEM; udf_inittag(&fsd->tag, TAGID_FSD, /* loc */ 0); udf_set_timestamp_now(&fsd->time); fsd->ichg_lvl = udf_rw16(3); /* UDF 2.3.2.1 */ fsd->max_ichg_lvl = udf_rw16(3); /* UDF 2.3.2.2 */ fsd->charset_list = udf_rw32(1); /* only CS0 */ fsd->max_charset_list = udf_rw32(1); /* only CS0 */ fsd->fileset_num = udf_rw32(0); /* only one fsd */ fsd->fileset_desc_num = udf_rw32(0); /* original */ udf_osta_charset(&fsd->logvol_id_charset); udf_encode_osta_id(fsd->logvol_id, 128, context.logvol_name); udf_osta_charset(&fsd->fileset_charset); udf_encode_osta_id(fsd->fileset_id, 32, context.fileset_name); /* copyright file and abstract file names obmitted */ fsd->rootdir_icb.len = udf_rw32(context.sector_size); fsd->rootdir_icb.loc.lb_num = udf_rw32(layout.rootdir); fsd->rootdir_icb.loc.part_num = udf_rw16(context.metadata_part); udf_set_regid(&fsd->domain_id, "*OSTA UDF Compliant"); udf_add_domain_regid(&fsd->domain_id); /* next_ex stays zero */ /* no system streamdirs yet */ crclen = sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH; fsd->tag.desc_crc_len = udf_rw16(crclen); context.fileset_desc = fsd; return 0; } int udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba, struct space_bitmap_desc **sbdp) { struct space_bitmap_desc *sbd; uint32_t cnt; uint16_t crclen; *sbdp = NULL; sbd = calloc(context.sector_size, dscr_size); if (sbd == NULL) return ENOMEM; udf_inittag(&sbd->tag, TAGID_SPACE_BITMAP, /* loc */ 0); sbd->num_bits = udf_rw32(part_size_lba); sbd->num_bytes = udf_rw32((part_size_lba + 7)/8); /* fill space with 0xff to indicate free */ for (cnt = 0; cnt < udf_rw32(sbd->num_bytes); cnt++) sbd->data[cnt] = 0xff; /* set crc to only cover the header (UDF 2.3.1.2, 2.3.8.1) */ crclen = sizeof(struct space_bitmap_desc) -1 - UDF_DESC_TAG_LENGTH; sbd->tag.desc_crc_len = udf_rw16(crclen); *sbdp = sbd; return 0; } /* --------------------------------------------------------------------- */ int udf_register_bad_block(uint32_t location) { struct udf_sparing_table *spt; struct spare_map_entry *sme, *free_sme; uint32_t cnt; spt = context.sparing_table; if (spt == NULL) errx(1, "internal error, adding bad block to " "non spareable in %s", __func__); /* find us a free spare map entry */ free_sme = NULL; for (cnt = 0; cnt < layout.spareable_blocks; cnt++) { sme = &spt->entries[cnt]; /* if we are already in it, bail out */ if (udf_rw32(sme->org) == location) return 0; if (udf_rw32(sme->org) == 0xffffffff) { free_sme = sme; break; } } if (free_sme == NULL) { warnx("disc relocation blocks full; disc too damaged"); return EINVAL; } free_sme->org = udf_rw32(location); return 0; } void udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks) { union dscrptr *dscr; uint8_t *bpos; uint32_t cnt, bit; /* account for space used on underlying partition */ #ifdef DEBUG printf("mark allocated : partnr %d, start_lb %d for %d blocks\n", partnr, start_lb, blocks); #endif switch (context.vtop_tp[partnr]) { case UDF_VTOP_TYPE_VIRT: /* nothing */ break; case UDF_VTOP_TYPE_PHYS: case UDF_VTOP_TYPE_SPAREABLE: case UDF_VTOP_TYPE_META: if (context.part_unalloc_bits[context.vtop[partnr]] == NULL) { context.part_free[partnr] = 0; break; } #ifdef DEBUG printf("marking %d+%d as used\n", start_lb, blocks); #endif dscr = (union dscrptr *) (context.part_unalloc_bits[partnr]); for (cnt = start_lb; cnt < start_lb + blocks; cnt++) { bpos = &dscr->sbd.data[cnt / 8]; bit = cnt % 8; /* only account for bits marked free */ if ((*bpos & (1 << bit))) context.part_free[partnr] -= 1; *bpos &= ~(1<< bit); } break; default: errx(1, "internal error: bad mapping type %d in %s", context.vtop_tp[partnr], __func__); } } void udf_advance_uniqueid(void) { /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */ context.unique_id++; if (context.unique_id < 0x10) context.unique_id = 0x10; } /* --------------------------------------------------------------------- */ /* XXX implement the using of the results */ int udf_surface_check(void) { uint32_t loc, block_bytes; uint32_t sector_size, blockingnr, bpos; uint8_t *buffer; int error, num_errors; if (mmc_discinfo.mmc_class == MMC_CLASS_DISC) return 0; sector_size = context.sector_size; blockingnr = layout.blockingnr; block_bytes = layout.blockingnr * sector_size; if ((buffer = malloc(block_bytes)) == NULL) return ENOMEM; /* set all one to not kill Flash memory? */ for (bpos = 0; bpos < block_bytes; bpos++) buffer[bpos] = 0x00; printf("\nChecking disc surface : phase 1 - writing\n"); num_errors = 0; loc = layout.first_lba; while (loc <= layout.last_lba) { /* write blockingnr sectors */ error = pwrite(dev_fd, buffer, block_bytes, (uint64_t) loc*sector_size); printf(" %08d + %d (%02d %%)\r", loc, blockingnr, (int)((100.0 * loc)/layout.last_lba)); fflush(stdout); if (error == -1) { /* block is bad */ printf("BAD block at %08d + %d \n", loc, layout.blockingnr); if ((error = udf_register_bad_block(loc))) { free(buffer); return error; } num_errors ++; } loc += layout.blockingnr; } printf("\nChecking disc surface : phase 2 - reading\n"); num_errors = 0; loc = layout.first_lba; while (loc <= layout.last_lba) { /* read blockingnr sectors */ error = pread(dev_fd, buffer, block_bytes, loc*sector_size); printf(" %08d + %d (%02d %%)\r", loc, blockingnr, (int)((100.0 * loc)/layout.last_lba)); fflush(stdout); if (error == -1) { /* block is bad */ printf("BAD block at %08d + %d \n", loc, layout.blockingnr); if ((error = udf_register_bad_block(loc))) { free(buffer); return error; } num_errors ++; } loc += layout.blockingnr; } printf("Scan complete : %d bad blocks found\n", num_errors); free(buffer); return 0; } /* --------------------------------------------------------------------- */ #define UDF_SYMLINKBUFLEN (64*1024) /* picked */ int udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target) { struct charspec osta_charspec; struct pathcomp pathcomp; char *pathbuf, *pathpos, *compnamepos; // char *mntonname; // int mntonnamelen; int pathlen, len, compnamelen; int error; /* process `target' to an UDF structure */ pathbuf = malloc(UDF_SYMLINKBUFLEN); assert(pathbuf); *pathbufp = NULL; *pathlenp = 0; pathpos = pathbuf; pathlen = 0; udf_osta_charset(&osta_charspec); if (*target == '/') { /* symlink starts from the root */ len = UDF_PATH_COMP_SIZE; memset(&pathcomp, 0, len); pathcomp.type = UDF_PATH_COMP_ROOT; #if 0 /* XXX how to check for in makefs? */ /* check if its mount-point relative! */ mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; mntonnamelen = strlen(mntonname); if (strlen(target) >= mntonnamelen) { if (strncmp(target, mntonname, mntonnamelen) == 0) { pathcomp.type = UDF_PATH_COMP_MOUNTROOT; target += mntonnamelen; } } else { target++; } #else target++; #endif memcpy(pathpos, &pathcomp, len); pathpos += len; pathlen += len; } error = 0; while (*target) { /* ignore multiple '/' */ while (*target == '/') { target++; } if (!*target) break; /* extract component name */ compnamelen = 0; compnamepos = target; while ((*target) && (*target != '/')) { target++; compnamelen++; } /* just trunc if too long ?? (security issue) */ if (compnamelen >= 127) { error = ENAMETOOLONG; break; } /* convert unix name to UDF name */ len = sizeof(struct pathcomp); memset(&pathcomp, 0, len); pathcomp.type = UDF_PATH_COMP_NAME; len = UDF_PATH_COMP_SIZE; if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0)) pathcomp.type = UDF_PATH_COMP_PARENTDIR; if ((compnamelen == 1) && (*compnamepos == '.')) pathcomp.type = UDF_PATH_COMP_CURDIR; if (pathcomp.type == UDF_PATH_COMP_NAME) { unix_to_udf_name( (char *) &pathcomp.ident, &pathcomp.l_ci, compnamepos, compnamelen, &osta_charspec); len = UDF_PATH_COMP_SIZE + pathcomp.l_ci; } if (pathlen + len >= UDF_SYMLINKBUFLEN) { error = ENAMETOOLONG; break; } memcpy(pathpos, &pathcomp, len); pathpos += len; pathlen += len; } if (error) { /* apparently too big */ free(pathbuf); return error; } /* return status of symlink contents writeout */ *pathbufp = (uint8_t *) pathbuf; *pathlenp = pathlen; return 0; } #undef UDF_SYMLINKBUFLEN /* * XXX note the different semantics from udfclient: for FIDs it still rounds * up to sectors. Use udf_fidsize() for a correct length. */ uint32_t udf_tagsize(union dscrptr *dscr, uint32_t lb_size) { uint32_t size, tag_id, num_lb, elmsz; tag_id = udf_rw16(dscr->tag.id); switch (tag_id) { case TAGID_LOGVOL : size = sizeof(struct logvol_desc) - 1; size += udf_rw32(dscr->lvd.mt_l); break; case TAGID_UNALLOC_SPACE : elmsz = sizeof(struct extent_ad); size = sizeof(struct unalloc_sp_desc) - elmsz; size += udf_rw32(dscr->usd.alloc_desc_num) * elmsz; break; case TAGID_FID : size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu); size = (size + 3) & ~3; break; case TAGID_LOGVOL_INTEGRITY : size = sizeof(struct logvol_int_desc) - sizeof(uint32_t); size += udf_rw32(dscr->lvid.l_iu); size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t)); break; case TAGID_SPACE_BITMAP : size = sizeof(struct space_bitmap_desc) - 1; size += udf_rw32(dscr->sbd.num_bytes); break; case TAGID_SPARING_TABLE : elmsz = sizeof(struct spare_map_entry); size = sizeof(struct udf_sparing_table) - elmsz; size += udf_rw16(dscr->spt.rt_l) * elmsz; break; case TAGID_FENTRY : size = sizeof(struct file_entry); size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1; break; case TAGID_EXTFENTRY : size = sizeof(struct extfile_entry); size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1; break; case TAGID_FSD : size = sizeof(struct fileset_desc); break; default : size = sizeof(union dscrptr); break; } if ((size == 0) || (lb_size == 0)) return 0; if (lb_size == 1) return size; /* round up in sectors */ num_lb = (size + lb_size -1) / lb_size; return num_lb * lb_size; } int udf_fidsize(struct fileid_desc *fid) { uint32_t size; if (udf_rw16(fid->tag.id) != TAGID_FID) errx(1, "internal error, bad tag in %s", __func__); size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu); size = (size + 3) & ~3; return size; } int udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent) { /* the size of an empty FID is 38 but needs to be a multiple of 4 */ int fidsize = 40; udf_inittag(&fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num)); fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR; fid->icb = *parent; fid->icb.longad_uniqueid = parent->longad_uniqueid; fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); /* we have to do the fid here explicitly for simplicity */ udf_validate_tag_and_crc_sums((union dscrptr *) fid); return fidsize; } void udf_create_fid(uint32_t diroff, struct fileid_desc *fid, char *name, int file_char, struct long_ad *ref) { struct charspec osta_charspec; uint32_t endfid; uint32_t fidsize, lb_rest; memset(fid, 0, sizeof(*fid)); udf_inittag(&fid->tag, TAGID_FID, udf_rw32(ref->loc.lb_num)); fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ fid->file_char = file_char; fid->l_iu = udf_rw16(0); fid->icb = *ref; fid->icb.longad_uniqueid = ref->longad_uniqueid; udf_osta_charset(&osta_charspec); unix_to_udf_name((char *) fid->data, &fid->l_fi, name, strlen(name), &osta_charspec); /* * OK, tricky part: we need to pad so the next descriptor header won't * cross the sector boundary */ endfid = diroff + udf_fidsize(fid); lb_rest = context.sector_size - (endfid % context.sector_size); if (lb_rest < sizeof(struct desc_tag)) { /* add at least 32 */ fid->l_iu = udf_rw16(32); udf_set_regid((struct regid *) fid->data, context.impl_name); udf_add_impl_regid((struct regid *) fid->data); unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu), &fid->l_fi, name, strlen(name), &osta_charspec); } fidsize = udf_fidsize(fid); fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); /* make sure the header sums stays correct */ udf_validate_tag_and_crc_sums((union dscrptr *)fid); } static void udf_append_parentfid(union dscrptr *dscr, struct long_ad *parent_icb) { struct file_entry *fe; struct extfile_entry *efe; struct fileid_desc *fid; uint32_t l_ea; uint32_t fidsize, crclen; uint8_t *bpos, *data; fe = NULL; efe = NULL; if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { fe = &dscr->fe; data = fe->data; l_ea = udf_rw32(fe->l_ea); } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { efe = &dscr->efe; data = efe->data; l_ea = udf_rw32(efe->l_ea); } else { errx(1, "internal error, bad tag in %s", __func__); } /* create '..' */ bpos = data + l_ea; fid = (struct fileid_desc *) bpos; fidsize = udf_create_parentfid(fid, parent_icb); /* record fidlength information */ if (fe) { fe->inf_len = udf_rw64(fidsize); fe->l_ad = udf_rw32(fidsize); fe->logblks_rec = udf_rw64(0); /* intern */ crclen = sizeof(struct file_entry); } else { efe->inf_len = udf_rw64(fidsize); efe->obj_size = udf_rw64(fidsize); efe->l_ad = udf_rw32(fidsize); efe->logblks_rec = udf_rw64(0); /* intern */ crclen = sizeof(struct extfile_entry); } crclen -= 1 + UDF_DESC_TAG_LENGTH; crclen += l_ea + fidsize; dscr->tag.desc_crc_len = udf_rw16(crclen); /* make sure the header sums stays correct */ udf_validate_tag_and_crc_sums(dscr); } /* --------------------------------------------------------------------- */ /* * Extended attribute support. UDF knows of 3 places for extended attributes: * * (a) inside the file's (e)fe in the length of the extended attribute area * before the allocation descriptors/filedata * * (b) in a file referenced by (e)fe->ext_attr_icb and * * (c) in the e(fe)'s associated stream directory that can hold various * sub-files. In the stream directory a few fixed named subfiles are reserved * for NT/Unix ACL's and OS/2 attributes. * * NOTE: Extended attributes are read randomly but always written * *atomically*. For ACL's this interface is probably different but not known * to me yet. * * Order of extended attributes in a space: * ECMA 167 EAs * Non block aligned Implementation Use EAs * Block aligned Implementation Use EAs * Application Use EAs */ int udf_impl_extattr_check(struct impl_extattr_entry *implext) { uint16_t *spos; if (strncmp((char *) implext->imp_id.id, "*UDF", 4) == 0) { /* checksum valid? */ spos = (uint16_t *) implext->data; if (udf_rw16(*spos) != udf_ea_cksum((uint8_t *) implext)) return EINVAL; } return 0; } void udf_calc_impl_extattr_checksum(struct impl_extattr_entry *implext) { uint16_t *spos; if (strncmp((char *) implext->imp_id.id, "*UDF", 4) == 0) { /* set checksum */ spos = (uint16_t *) implext->data; *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); } } int udf_extattr_search_intern(union dscrptr *dscr, uint32_t sattr, char const *sattrname, uint32_t *offsetp, uint32_t *lengthp) { struct extattrhdr_desc *eahdr; struct extattr_entry *attrhdr; struct impl_extattr_entry *implext; uint32_t offset, a_l, sector_size; uint32_t l_ea; uint8_t *pos; int tag_id, error; sector_size = context.sector_size; /* get information from fe/efe */ tag_id = udf_rw16(dscr->tag.id); if (tag_id == TAGID_FENTRY) { l_ea = udf_rw32(dscr->fe.l_ea); eahdr = (struct extattrhdr_desc *) dscr->fe.data; } else { assert(tag_id == TAGID_EXTFENTRY); l_ea = udf_rw32(dscr->efe.l_ea); eahdr = (struct extattrhdr_desc *) dscr->efe.data; } /* something recorded here? */ if (l_ea == 0) return ENOENT; /* check extended attribute tag; what to do if it fails? */ error = udf_check_tag(eahdr); if (error) return EINVAL; if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR) return EINVAL; error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc)); if (error) return EINVAL; /* looking for Ecma-167 attributes? */ offset = sizeof(struct extattrhdr_desc); /* looking for either implementation use or application use */ if (sattr == 2048) { /* [4/48.10.8] */ offset = udf_rw32(eahdr->impl_attr_loc); if (offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT) return ENOENT; } if (sattr == 65536) { /* [4/48.10.9] */ offset = udf_rw32(eahdr->appl_attr_loc); if (offset == UDF_APPL_ATTR_LOC_NOT_PRESENT) return ENOENT; } /* paranoia check offset and l_ea */ if (l_ea + offset >= sector_size - sizeof(struct extattr_entry)) return EINVAL; /* find our extended attribute */ l_ea -= offset; pos = (uint8_t *) eahdr + offset; while (l_ea >= sizeof(struct extattr_entry)) { attrhdr = (struct extattr_entry *) pos; implext = (struct impl_extattr_entry *) pos; /* get complete attribute length and check for roque values */ a_l = udf_rw32(attrhdr->a_l); if ((a_l == 0) || (a_l > l_ea)) return EINVAL; if (udf_rw32(attrhdr->type) != sattr) goto next_attribute; /* we might have found it! */ if (udf_rw32(attrhdr->type) < 2048) { /* Ecma-167 attribute */ *offsetp = offset; *lengthp = a_l; return 0; /* success */ } /* * Implementation use and application use extended attributes * have a name to identify. They share the same structure only * UDF implementation use extended attributes have a checksum * we need to check */ if (strcmp((char *) implext->imp_id.id, sattrname) == 0) { /* we have found our appl/implementation attribute */ *offsetp = offset; *lengthp = a_l; return 0; /* success */ } next_attribute: /* next attribute */ pos += a_l; l_ea -= a_l; offset += a_l; } /* not found */ return ENOENT; } static void udf_extattr_insert_internal(union dscrptr *dscr, struct extattr_entry *extattr) { struct file_entry *fe; struct extfile_entry *efe; struct extattrhdr_desc *extattrhdr; struct impl_extattr_entry *implext; uint32_t impl_attr_loc, appl_attr_loc, l_ea, l_ad, a_l; uint16_t *spos; uint8_t *bpos, *data; void *l_eap; if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { fe = &dscr->fe; data = fe->data; l_eap = &fe->l_ea; l_ad = udf_rw32(fe->l_ad); } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { efe = &dscr->efe; data = efe->data; l_eap = &efe->l_ea; l_ad = udf_rw32(efe->l_ad); } else { errx(1, "internal error, bad tag in %s", __func__); } /* should have a header! */ extattrhdr = (struct extattrhdr_desc *) data; memcpy(&l_ea, l_eap, sizeof(l_ea)); l_ea = udf_rw32(l_ea); if (l_ea == 0) { uint32_t exthdr_len; assert(l_ad == 0); /* create empty extended attribute header */ l_ea = sizeof(struct extattrhdr_desc); exthdr_len = udf_rw32(l_ea); udf_inittag(&extattrhdr->tag, TAGID_EXTATTR_HDR, /* loc */ 0); extattrhdr->impl_attr_loc = exthdr_len; extattrhdr->appl_attr_loc = exthdr_len; extattrhdr->tag.desc_crc_len = udf_rw16(8); /* record extended attribute header length */ memcpy(l_eap, &exthdr_len, sizeof(exthdr_len)); } /* extract locations */ impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc); appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc); if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) impl_attr_loc = l_ea; if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) appl_attr_loc = l_ea; /* Ecma 167 EAs */ if (udf_rw32(extattr->type) < 2048) { assert(impl_attr_loc == l_ea); assert(appl_attr_loc == l_ea); } /* implementation use extended attributes */ if (udf_rw32(extattr->type) == 2048) { assert(appl_attr_loc == l_ea); /* calculate and write extended attribute header checksum */ implext = (struct impl_extattr_entry *) extattr; assert(udf_rw32(implext->iu_l) == 4); /* [UDF 3.3.4.5] */ spos = (uint16_t *) implext->data; *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); } /* application use extended attributes */ assert(udf_rw32(extattr->type) != 65536); assert(appl_attr_loc == l_ea); /* append the attribute at the end of the current space */ bpos = data + l_ea; a_l = udf_rw32(extattr->a_l); /* update impl. attribute locations */ if (udf_rw32(extattr->type) < 2048) { impl_attr_loc = l_ea + a_l; appl_attr_loc = l_ea + a_l; } if (udf_rw32(extattr->type) == 2048) { appl_attr_loc = l_ea + a_l; } /* copy and advance */ memcpy(bpos, extattr, a_l); l_ea += a_l; l_ea = udf_rw32(l_ea); memcpy(l_eap, &l_ea, sizeof(l_ea)); /* do the `dance` again backwards */ if (context.dscrver != 2) { if (impl_attr_loc == l_ea) impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT; if (appl_attr_loc == l_ea) appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT; } /* store offsets */ extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc); extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc); /* make sure the header sums stays correct */ udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr); } /* --------------------------------------------------------------------- */ int udf_create_new_fe(struct file_entry **fep, int file_type, struct stat *st) { struct file_entry *fe; struct icb_tag *icb; struct timestamp birthtime; struct filetimes_extattr_entry *ft_extattr; uint32_t crclen; /* XXX: should be 16; need to detect overflow */ uint16_t icbflags; *fep = NULL; fe = calloc(1, context.sector_size); if (fe == NULL) return ENOMEM; udf_inittag(&fe->tag, TAGID_FENTRY, /* loc */ 0); icb = &fe->icbtag; /* * Always use strategy type 4 unless on WORM which we don't support * (yet). Fill in defaults and set for internal allocation of data. */ icb->strat_type = udf_rw16(4); icb->max_num_entries = udf_rw16(1); icb->file_type = file_type; /* 8 bit */ icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); fe->perm = udf_rw32(0x7fff); /* all is allowed */ fe->link_cnt = udf_rw16(0); /* explicit setting */ fe->ckpoint = udf_rw32(1); /* user supplied file version */ udf_set_timestamp_now(&birthtime); udf_set_timestamp_now(&fe->atime); udf_set_timestamp_now(&fe->attrtime); udf_set_timestamp_now(&fe->mtime); /* set attributes */ if (st) { #if !HAVE_NBTOOL_CONFIG_H udf_set_timestamp(&birthtime, st->st_birthtime); #else udf_set_timestamp(&birthtime, 0); #endif udf_set_timestamp(&fe->atime, st->st_atime); udf_set_timestamp(&fe->attrtime, st->st_ctime); udf_set_timestamp(&fe->mtime, st->st_mtime); fe->uid = udf_rw32(st->st_uid); fe->gid = udf_rw32(st->st_gid); fe->perm = udf_rw32(unix_mode_to_udf_perm(st->st_mode)); icbflags = udf_rw16(fe->icbtag.flags); icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; if (st->st_mode & S_ISUID) icbflags |= UDF_ICB_TAG_FLAGS_SETUID; if (st->st_mode & S_ISGID) icbflags |= UDF_ICB_TAG_FLAGS_SETGID; if (st->st_mode & S_ISVTX) icbflags |= UDF_ICB_TAG_FLAGS_STICKY; fe->icbtag.flags = udf_rw16(icbflags); } udf_set_regid(&fe->imp_id, context.impl_name); udf_add_impl_regid(&fe->imp_id); fe->unique_id = udf_rw64(context.unique_id); udf_advance_uniqueid(); fe->l_ea = udf_rw32(0); /* create extended attribute to record our creation time */ ft_extattr = calloc(1, UDF_FILETIMES_ATTR_SIZE(1)); ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO); ft_extattr->hdr.subtype = 1; /* [4/48.10.5] */ ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1)); ft_extattr->d_l = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */ ft_extattr->existence = UDF_FILETIMES_FILE_CREATION; ft_extattr->times[0] = birthtime; udf_extattr_insert_internal((union dscrptr *) fe, (struct extattr_entry *) ft_extattr); free(ft_extattr); /* record fidlength information */ fe->inf_len = udf_rw64(0); fe->l_ad = udf_rw32(0); fe->logblks_rec = udf_rw64(0); /* intern */ crclen = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; crclen += udf_rw32(fe->l_ea); /* make sure the header sums stays correct */ fe->tag.desc_crc_len = udf_rw16(crclen); udf_validate_tag_and_crc_sums((union dscrptr *) fe); *fep = fe; return 0; } int udf_create_new_efe(struct extfile_entry **efep, int file_type, struct stat *st) { struct extfile_entry *efe; struct icb_tag *icb; uint32_t crclen; /* XXX: should be 16; need to detect overflow */ uint16_t icbflags; *efep = NULL; efe = calloc(1, context.sector_size); if (efe == NULL) return ENOMEM; udf_inittag(&efe->tag, TAGID_EXTFENTRY, /* loc */ 0); icb = &efe->icbtag; /* * Always use strategy type 4 unless on WORM which we don't support * (yet). Fill in defaults and set for internal allocation of data. */ icb->strat_type = udf_rw16(4); icb->max_num_entries = udf_rw16(1); icb->file_type = file_type; /* 8 bit */ icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); efe->perm = udf_rw32(0x7fff); /* all is allowed */ efe->link_cnt = udf_rw16(0); /* explicit setting */ efe->ckpoint = udf_rw32(1); /* user supplied file version */ udf_set_timestamp_now(&efe->ctime); udf_set_timestamp_now(&efe->atime); udf_set_timestamp_now(&efe->attrtime); udf_set_timestamp_now(&efe->mtime); /* set attributes */ if (st) { #if !HAVE_NBTOOL_CONFIG_H udf_set_timestamp(&efe->ctime, st->st_birthtime); #else udf_set_timestamp(&efe->ctime, 0); #endif udf_set_timestamp(&efe->atime, st->st_atime); udf_set_timestamp(&efe->attrtime, st->st_ctime); udf_set_timestamp(&efe->mtime, st->st_mtime); efe->uid = udf_rw32(st->st_uid); efe->gid = udf_rw32(st->st_gid); efe->perm = udf_rw32(unix_mode_to_udf_perm(st->st_mode)); icbflags = udf_rw16(efe->icbtag.flags); icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; if (st->st_mode & S_ISUID) icbflags |= UDF_ICB_TAG_FLAGS_SETUID; if (st->st_mode & S_ISGID) icbflags |= UDF_ICB_TAG_FLAGS_SETGID; if (st->st_mode & S_ISVTX) icbflags |= UDF_ICB_TAG_FLAGS_STICKY; efe->icbtag.flags = udf_rw16(icbflags); } udf_set_regid(&efe->imp_id, context.impl_name); udf_add_impl_regid(&efe->imp_id); efe->unique_id = udf_rw64(context.unique_id); udf_advance_uniqueid(); /* record fidlength information */ efe->inf_len = udf_rw64(0); efe->obj_size = udf_rw64(0); efe->l_ad = udf_rw32(0); efe->logblks_rec = udf_rw64(0); crclen = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH; /* make sure the header sums stays correct */ efe->tag.desc_crc_len = udf_rw16(crclen); udf_validate_tag_and_crc_sums((union dscrptr *) efe); *efep = efe; return 0; } /* --------------------------------------------------------------------- */ /* for METADATA file appending only */ static void udf_append_meta_mapping_part_to_efe(struct extfile_entry *efe, struct short_ad *mapping) { struct icb_tag *icb; uint64_t inf_len, obj_size, logblks_rec; uint32_t l_ad, l_ea; uint16_t crclen; uintptr_t bpos; inf_len = udf_rw64(efe->inf_len); obj_size = udf_rw64(efe->obj_size); logblks_rec = udf_rw64(efe->logblks_rec); l_ad = udf_rw32(efe->l_ad); l_ea = udf_rw32(efe->l_ea); crclen = udf_rw16(efe->tag.desc_crc_len); icb = &efe->icbtag; /* set our allocation to shorts if not already done */ icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC); /* append short_ad */ bpos = (uintptr_t)efe->data + l_ea + l_ad; memcpy((void *)bpos, mapping, sizeof(struct short_ad)); l_ad += sizeof(struct short_ad); crclen += sizeof(struct short_ad); inf_len += UDF_EXT_LEN(udf_rw32(mapping->len)); obj_size += UDF_EXT_LEN(udf_rw32(mapping->len)); logblks_rec = UDF_ROUNDUP(inf_len, context.sector_size) / context.sector_size; efe->l_ad = udf_rw32(l_ad); efe->inf_len = udf_rw64(inf_len); efe->obj_size = udf_rw64(obj_size); efe->logblks_rec = udf_rw64(logblks_rec); efe->tag.desc_crc_len = udf_rw16(crclen); } /* for METADATA file appending only */ static void udf_append_meta_mapping_to_efe(struct extfile_entry *efe, uint16_t partnr, uint32_t lb_num, uint64_t len) { struct short_ad mapping; uint64_t max_len, part_len; /* calculate max length meta allocation sizes */ max_len = UDF_EXT_MAXLEN / context.sector_size; /* in sectors */ max_len = (max_len / layout.meta_blockingnr) * layout.meta_blockingnr; max_len = max_len * context.sector_size; memset(&mapping, 0, sizeof(mapping)); while (len) { part_len = MIN(len, max_len); mapping.lb_num = udf_rw32(lb_num); mapping.len = udf_rw32(part_len); udf_append_meta_mapping_part_to_efe(efe, &mapping); lb_num += part_len / context.sector_size; len -= part_len; } } int udf_create_meta_files(void) { struct extfile_entry *efe; struct long_ad meta_icb; uint64_t bytes; uint32_t sector_size; int filetype, error; sector_size = context.sector_size; memset(&meta_icb, 0, sizeof(meta_icb)); meta_icb.len = udf_rw32(sector_size); meta_icb.loc.part_num = udf_rw16(context.data_part); /* create metadata file */ meta_icb.loc.lb_num = udf_rw32(layout.meta_file); filetype = UDF_ICB_FILETYPE_META_MAIN; error = udf_create_new_efe(&efe, filetype, NULL); if (error) return error; context.meta_file = efe; context.meta_file->unique_id = udf_rw64(0); /* create metadata mirror file */ meta_icb.loc.lb_num = udf_rw32(layout.meta_mirror); filetype = UDF_ICB_FILETYPE_META_MIRROR; error = udf_create_new_efe(&efe, filetype, NULL); if (error) return error; context.meta_mirror = efe; context.meta_mirror->unique_id = udf_rw64(0); if (!(context.format_flags & FORMAT_READONLY)) { /* create metadata bitmap file */ meta_icb.loc.lb_num = udf_rw32(layout.meta_bitmap); filetype = UDF_ICB_FILETYPE_META_BITMAP; error = udf_create_new_efe(&efe, filetype, NULL); if (error) return error; context.meta_bitmap = efe; context.meta_bitmap->unique_id = udf_rw64(0); } /* restart unique id */ context.unique_id = 0x10; /* XXX no support for metadata mirroring yet */ /* insert extents */ efe = context.meta_file; udf_append_meta_mapping_to_efe(efe, context.data_part, layout.meta_part_start_lba, (uint64_t) layout.meta_part_size_lba * sector_size); efe = context.meta_mirror; udf_append_meta_mapping_to_efe(efe, context.data_part, layout.meta_part_start_lba, (uint64_t) layout.meta_part_size_lba * sector_size); if (context.meta_bitmap) { efe = context.meta_bitmap; bytes = udf_space_bitmap_len(layout.meta_part_size_lba); udf_append_meta_mapping_to_efe(efe, context.data_part, layout.meta_bitmap_space, bytes); } return 0; } /* --------------------------------------------------------------------- */ int udf_create_new_rootdir(union dscrptr **dscr) { struct file_entry *fe; struct extfile_entry *efe; struct long_ad root_icb; int filetype, error; memset(&root_icb, 0, sizeof(root_icb)); root_icb.len = udf_rw32(context.sector_size); root_icb.loc.lb_num = udf_rw32(layout.rootdir); root_icb.loc.part_num = udf_rw16(context.metadata_part); filetype = UDF_ICB_FILETYPE_DIRECTORY; if (context.dscrver == 2) { error = udf_create_new_fe(&fe, filetype, NULL); *dscr = (union dscrptr *) fe; } else { error = udf_create_new_efe(&efe, filetype, NULL); *dscr = (union dscrptr *) efe; } if (error) return error; /* append '..' */ udf_append_parentfid(*dscr, &root_icb); /* rootdir has explicit only one link on creation; '..' is no link */ if (context.dscrver == 2) { fe->link_cnt = udf_rw16(1); } else { efe->link_cnt = udf_rw16(1); } context.num_directories++; assert(context.num_directories == 1); return 0; } void udf_prepend_VAT_file(void) { /* old style VAT has no prepend */ if (context.dscrver == 2) { context.vat_start = 0; context.vat_size = 0; return; } context.vat_start = offsetof(struct udf_vat, data); context.vat_size = offsetof(struct udf_vat, data); } void udf_vat_update(uint32_t virt, uint32_t phys) { uint32_t *vatpos; uint32_t new_size; if (context.vtop_tp[context.metadata_part] != UDF_VTOP_TYPE_VIRT) return; new_size = MAX(context.vat_size, (context.vat_start + (virt+1)*sizeof(uint32_t))); if (new_size > context.vat_allocated) { context.vat_allocated = UDF_ROUNDUP(new_size, context.sector_size); context.vat_contents = realloc(context.vat_contents, context.vat_allocated); assert(context.vat_contents); /* XXX could also report error */ } vatpos = (uint32_t *) (context.vat_contents + context.vat_start); vatpos[virt] = udf_rw32(phys); context.vat_size = MAX(context.vat_size, (context.vat_start + (virt+1)*sizeof(uint32_t))); } int udf_append_VAT_file(void) { struct udf_oldvat_tail *oldvat_tail; struct udf_vat *vathdr; int32_t len_diff; /* new style VAT has VAT LVInt analog in front */ if (context.dscrver == 3) { /* set up VATv2 descriptor */ vathdr = (struct udf_vat *) context.vat_contents; vathdr->header_len = udf_rw16(sizeof(struct udf_vat) - 1); vathdr->impl_use_len = udf_rw16(0); memcpy(vathdr->logvol_id, context.logical_vol->logvol_id, 128); vathdr->prev_vat = udf_rw32(UDF_NO_PREV_VAT); vathdr->num_files = udf_rw32(context.num_files); vathdr->num_directories = udf_rw32(context.num_directories); vathdr->min_udf_readver = udf_rw16(context.min_udf); vathdr->min_udf_writever = udf_rw16(context.min_udf); vathdr->max_udf_writever = udf_rw16(context.max_udf); return 0; } /* old style VAT has identifier appended */ /* append "*UDF Virtual Alloc Tbl" id and prev. VAT location */ len_diff = context.vat_allocated - context.vat_size; assert(len_diff >= 0); if (len_diff < (int32_t) sizeof(struct udf_oldvat_tail)) { context.vat_allocated += context.sector_size; context.vat_contents = realloc(context.vat_contents, context.vat_allocated); assert(context.vat_contents); /* XXX could also report error */ } oldvat_tail = (struct udf_oldvat_tail *) (context.vat_contents + context.vat_size); udf_set_regid(&oldvat_tail->id, "*UDF Virtual Alloc Tbl"); udf_add_udf_regid(&oldvat_tail->id); oldvat_tail->prev_vat = udf_rw32(UDF_NO_PREV_VAT); context.vat_size += sizeof(struct udf_oldvat_tail); return 0; } int udf_create_VAT(union dscrptr **vat_dscr, struct long_ad *vatdata_loc) { struct impl_extattr_entry *implext; struct vatlvext_extattr_entry *vatlvext; struct long_ad *allocpos; uint8_t *bpos, *extattr; uint32_t ea_len, inf_len, vat_len, blks; int filetype; int error; assert((layout.rootdir < 2) && (layout.fsd < 2)); if (context.dscrver == 2) { struct file_entry *fe; /* old style VAT */ filetype = UDF_ICB_FILETYPE_UNKNOWN; error = udf_create_new_fe(&fe, filetype, NULL); if (error) return error; /* append VAT LVExtension attribute */ ea_len = sizeof(struct impl_extattr_entry) - 2 + 4 + sizeof(struct vatlvext_extattr_entry); extattr = calloc(1, ea_len); implext = (struct impl_extattr_entry *) extattr; implext->hdr.type = udf_rw32(2048); /* [4/48.10.8] */ implext->hdr.subtype = 1; /* [4/48.10.8.2] */ implext->hdr.a_l = udf_rw32(ea_len); /* VAT LVext EA size */ /* use 4 bytes of imp use for UDF checksum [UDF 3.3.4.5] */ implext->iu_l = udf_rw32(4); udf_set_regid(&implext->imp_id, "*UDF VAT LVExtension"); udf_add_udf_regid(&implext->imp_id); /* VAT LVExtension data follows UDF IU space */ bpos = ((uint8_t *) implext->data) + 4; vatlvext = (struct vatlvext_extattr_entry *) bpos; vatlvext->unique_id_chk = fe->unique_id; vatlvext->num_files = udf_rw32(context.num_files); vatlvext->num_directories = udf_rw32(context.num_directories); memcpy(vatlvext->logvol_id, context.logical_vol->logvol_id,128); udf_extattr_insert_internal((union dscrptr *) fe, (struct extattr_entry *) extattr); free(extattr); fe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); allocpos = (struct long_ad *) (fe->data + udf_rw32(fe->l_ea)); *allocpos = *vatdata_loc; /* set length */ inf_len = context.vat_size; fe->inf_len = udf_rw64(inf_len); allocpos->len = udf_rw32(inf_len); fe->l_ad = udf_rw32(sizeof(struct long_ad)); blks = UDF_ROUNDUP(inf_len, context.sector_size) / context.sector_size; fe->logblks_rec = udf_rw64(blks); /* update vat descriptor's CRC length */ vat_len = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; vat_len += udf_rw32(fe->l_ad) + udf_rw32(fe->l_ea); fe->tag.desc_crc_len = udf_rw16(vat_len); *vat_dscr = (union dscrptr *) fe; } else { /* the choice is between an EFE or an FE as VAT */ #if 1 struct extfile_entry *efe; /* new style VAT on FE */ filetype = UDF_ICB_FILETYPE_VAT; error = udf_create_new_efe(&efe, filetype, NULL); if (error) return error; efe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); allocpos = (struct long_ad *) efe->data; *allocpos = *vatdata_loc; /* set length */ inf_len = context.vat_size; efe->inf_len = udf_rw64(inf_len); allocpos->len = udf_rw32(inf_len); efe->obj_size = udf_rw64(inf_len); efe->l_ad = udf_rw32(sizeof(struct long_ad)); blks = UDF_ROUNDUP(inf_len, context.sector_size) / context.sector_size; efe->logblks_rec = udf_rw64(blks); vat_len = sizeof(struct extfile_entry)-1 - UDF_DESC_TAG_LENGTH; vat_len += udf_rw32(efe->l_ad); efe->tag.desc_crc_len = udf_rw16(vat_len); *vat_dscr = (union dscrptr *) efe; #else struct file_entry *fe; uint32_t l_ea; /* new style VAT on EFE */ filetype = UDF_ICB_FILETYPE_VAT; error = udf_create_new_fe(&fe, filetype, NULL); if (error) return error; fe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); l_ea = udf_rw32(fe->l_ea); allocpos = (struct long_ad *) (fe->data + l_ea); *allocpos = *vatdata_loc; /* set length */ inf_len = context.vat_size; fe->inf_len = udf_rw64(inf_len); allocpos->len = udf_rw32(inf_len); fe->l_ad = udf_rw32(sizeof(struct long_ad)); blks = UDF_ROUNDUP(inf_len, context.sector_size) / context.sector_size; fe->logblks_rec = udf_rw64(blks); vat_len = sizeof(struct file_entry)-1 - UDF_DESC_TAG_LENGTH; vat_len += udf_rw32(fe->l_ad) + udf_rw32(fe->l_ea); fe->tag.desc_crc_len = udf_rw16(vat_len); *vat_dscr = (union dscrptr *) fe; #endif } return 0; } int udf_writeout_VAT(void) { union dscrptr *vat_dscr; struct long_ad vatdata; uint32_t loc, phys, ext, sects; int rel_block, rest_block, error; vat_dscr = NULL; /* update lvint to reflect the newest values (no writeout) */ udf_update_lvintd(UDF_INTEGRITY_CLOSED); error = udf_append_VAT_file(); if (error) return error; /* write out VAT data */ sects = UDF_ROUNDUP(context.vat_size, context.sector_size) / context.sector_size; layout.vat = context.alloc_pos[context.data_part]; udf_data_alloc(sects, &vatdata); //printf("layout.vat %d\n", layout.vat + udf_rw32(context.partitions[context.data_part]->start_loc)); loc = udf_rw32(vatdata.loc.lb_num); udf_translate_vtop(loc, context.data_part, &phys, &ext); error = udf_write_phys(context.vat_contents, phys, sects); if (error) return error; loc += sects; /* create new VAT descriptor */ error = udf_create_VAT(&vat_dscr, &vatdata); if (error) return error; //printf("VAT data at %d\n", vatdata.loc.lb_num); //printf("VAT itself at %d\n", loc + udf_rw32(context.partitions[context.data_part]->start_loc)); /* at least one */ error = udf_write_dscr_virt(vat_dscr, loc, context.data_part, 1); loc++; error = udf_translate_vtop(loc, context.data_part, &phys, &ext); assert(!error); rel_block = phys - (UDF_ROUNDDOWN(phys, layout.blockingnr) + wrtrack_skew); rest_block = layout.blockingnr - rel_block; for (int i = 0; i < rest_block; i++) { error = udf_write_dscr_virt(vat_dscr, loc, context.data_part, 1); loc++; } free(vat_dscr); return error; } /* --------------------------------------------------------------------- */ /* * mmc_discinfo and mmc_trackinfo readers modified from original in udf main * code in sys/fs/udf/ */ void udf_dump_discinfo(struct mmc_discinfo *di) { #ifdef DEBUG char bits[128]; printf("Device/media info :\n"); printf("\tMMC profile 0x%02x\n", di->mmc_profile); printf("\tderived class %d\n", di->mmc_class); printf("\tsector size %d\n", di->sector_size); printf("\tdisc state %d\n", di->disc_state); printf("\tlast ses state %d\n", di->last_session_state); printf("\tbg format state %d\n", di->bg_format_state); printf("\tfrst track %d\n", di->first_track); printf("\tfst on last ses %d\n", di->first_track_last_session); printf("\tlst on last ses %d\n", di->last_track_last_session); printf("\tlink block penalty %d\n", di->link_block_penalty); snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, (uint64_t) di->disc_flags); printf("\tdisc flags %s\n", bits); printf("\tdisc id %x\n", di->disc_id); printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); printf("\tnum sessions %d\n", di->num_sessions); printf("\tnum tracks %d\n", di->num_tracks); snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); printf("\tcapabilities cur %s\n", bits); snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); printf("\tcapabilities cap %s\n", bits); printf("\n"); printf("\tlast_possible_lba %d\n", di->last_possible_lba); printf("\n"); #endif } void udf_synchronise_caches(void) { #if !HAVE_NBTOOL_CONFIG_H struct mmc_op mmc_op; bzero(&mmc_op, sizeof(struct mmc_op)); mmc_op.operation = MMC_OP_SYNCHRONISECACHE; /* this device might not know this ioct, so just be ignorant */ (void) ioctl(dev_fd, MMCOP, &mmc_op); #endif } /* * General Idea: * * stat the dev_fd * * If a S_ISREG(), we emulate using the emul_* settings. * * If its a device : * try the MMCGETDISCINFO ioctl() and be done. * * If that fails, its a regular disc and set the type to disc media. * */ int udf_update_discinfo(void) { off_t size, last_sector, secsize; int error; memset(&mmc_discinfo, 0, sizeof(struct mmc_discinfo)); #if !HAVE_NBTOOL_CONFIG_H /* check if we're on a MMC capable device, i.e. CD/DVD */ error = ioctl(dev_fd, MMCGETDISCINFO, &mmc_discinfo); if (error == 0) { if ((emul_mmc_profile != -1) && (emul_mmc_profile != mmc_discinfo.mmc_profile)) { errno = EINVAL; perror("media and specified disc type mismatch"); return errno; } emul_size = 0; return 0; } #endif if (S_ISREG(dev_fd_stat.st_mode)) { /* file support; we pick the minimum sector size allowed */ if (emul_mmc_profile < 0) emul_mmc_profile = 0x01; if (emul_size == 0) emul_size = dev_fd_stat.st_size; size = emul_size; secsize = emul_sectorsize; last_sector = (size / secsize) - 1; if (ftruncate(dev_fd, size)) { perror("can't resize file"); return EXIT_FAILURE; } } else { #if !HAVE_NBTOOL_CONFIG_H struct disk_geom geo; struct dkwedge_info dkw; /* sanity */ if (emul_mmc_profile <= 0) emul_mmc_profile = 0x01; if (emul_mmc_profile != 0x01) { warnx("format incompatible with disc partition"); return EXIT_FAILURE; } /* get our disc info */ error = getdiskinfo(dev_name, dev_fd, NULL, &geo, &dkw); if (error) { warn("retrieving disc info failed"); return EXIT_FAILURE; } secsize = emul_sectorsize; last_sector = (dkw.dkw_size - 1) * geo.dg_secsize / secsize; #else warnx("disk partitions only usable outside tools"); return EIO; #endif } /* commons */ mmc_discinfo.mmc_profile = emul_mmc_profile; mmc_discinfo.disc_state = MMC_STATE_CLOSED; mmc_discinfo.last_session_state = MMC_STATE_CLOSED; mmc_discinfo.bg_format_state = MMC_BGFSTATE_COMPLETED; mmc_discinfo.link_block_penalty = 0; mmc_discinfo.disc_flags = MMC_DFLAGS_UNRESTRICTED; mmc_discinfo.last_possible_lba = last_sector; mmc_discinfo.sector_size = secsize; mmc_discinfo.num_sessions = 1; mmc_discinfo.num_tracks = 1; mmc_discinfo.first_track = 1; mmc_discinfo.first_track_last_session = mmc_discinfo.last_track_last_session = 1; mmc_discinfo.mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_ZEROLINKBLK; switch (emul_mmc_profile) { case 0x00: /* unknown, treat as CDROM */ case 0x08: /* CDROM */ case 0x10: /* DVDROM */ case 0x40: /* BDROM */ /* FALLTHROUGH */ case 0x01: /* disc */ /* set up a disc info profile for partitions/files */ mmc_discinfo.mmc_class = MMC_CLASS_DISC; mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; break; case 0x09: /* CD-R */ mmc_discinfo.mmc_class = MMC_CLASS_CD; mmc_discinfo.mmc_cur |= MMC_CAP_SEQUENTIAL; mmc_discinfo.disc_state = MMC_STATE_EMPTY; break; case 0x0a: /* CD-RW + CD-MRW (regretably) */ mmc_discinfo.mmc_class = MMC_CLASS_CD; mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE; break; case 0x13: /* DVD-RW */ case 0x1a: /* DVD+RW */ mmc_discinfo.mmc_class = MMC_CLASS_DVD; mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE; break; case 0x11: /* DVD-R */ case 0x14: /* DVD-RW sequential */ case 0x1b: /* DVD+R */ case 0x2b: /* DVD+R DL */ case 0x51: /* HD DVD-R */ mmc_discinfo.mmc_class = MMC_CLASS_DVD; mmc_discinfo.mmc_cur |= MMC_CAP_SEQUENTIAL; mmc_discinfo.disc_state = MMC_STATE_EMPTY; break; case 0x41: /* BD-R */ mmc_discinfo.mmc_class = MMC_CLASS_BD; mmc_discinfo.mmc_cur |= MMC_CAP_SEQUENTIAL | MMC_CAP_HW_DEFECTFREE; mmc_discinfo.disc_state = MMC_STATE_EMPTY; break; case 0x43: /* BD-RE */ mmc_discinfo.mmc_class = MMC_CLASS_BD; mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; break; default: errno = EINVAL; perror("unknown or unimplemented device type"); return errno; } mmc_discinfo.mmc_cap = mmc_discinfo.mmc_cur; return 0; } int udf_update_trackinfo(struct mmc_trackinfo *ti) { int error, class; #if !HAVE_NBTOOL_CONFIG_H class = mmc_discinfo.mmc_class; if (class != MMC_CLASS_DISC) { /* tracknr specified in struct ti */ error = ioctl(dev_fd, MMCGETTRACKINFO, ti); if (!error) return 0; } #endif /* discs partition support */ if (ti->tracknr != 1) return EIO; /* create fake ti (TODO check for resized vnds) */ ti->sessionnr = 1; ti->track_mode = 0; /* XXX */ ti->data_mode = 0; /* XXX */ ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; ti->track_start = 0; ti->packet_size = emul_packetsize; /* TODO support for resizable vnd */ ti->track_size = mmc_discinfo.last_possible_lba; ti->next_writable = mmc_discinfo.last_possible_lba + 1; //0; ti->last_recorded = ti->next_writable; ti->free_blocks = 0; return 0; } int udf_opendisc(const char *device, int open_flags) { /* set global variable to the passed name */ dev_name = strdup(device); /* open device */ if (open_flags & O_RDONLY) { dev_fd_rdonly = 1; if ((dev_fd = open(dev_name, O_RDONLY, 0)) == -1) { warn("device/image not found"); return EXIT_FAILURE; } } else { dev_fd_rdonly = 0; if ((dev_fd = open(dev_name, O_RDWR, 0)) == -1) { /* check if we need to create a file */ dev_fd = open(dev_name, O_RDONLY, 0); if (dev_fd > 0) { warn("device is there but can't be opened for " "read/write"); return EXIT_FAILURE; } if ((open_flags & O_CREAT) == 0) { warnx("device/image not found"); return EXIT_FAILURE; } /* need to create a file */ dev_fd = open(dev_name, O_RDWR | O_CREAT | O_TRUNC, 0666); if (dev_fd == -1) { warn("can't create image file"); return EXIT_FAILURE; } } } /* stat the device/image */ if (fstat(dev_fd, &dev_fd_stat) != 0) { warn("can't stat the disc image"); return EXIT_FAILURE; } /* sanity check and resizing of file */ if (S_ISREG(dev_fd_stat.st_mode)) { if (emul_size == 0) emul_size = dev_fd_stat.st_size; /* sanitise arguments */ emul_sectorsize &= ~511; if (emul_size & (emul_sectorsize-1)) { warnx("size of file is not a multiple of sector size, " "shrinking"); emul_size -= emul_size & (emul_sectorsize-1); } /* grow the image */ if (ftruncate(dev_fd, emul_size)) { warn("can't resize file"); return EXIT_FAILURE; } /* restat the device/image */ if (fstat(dev_fd, &dev_fd_stat) != 0) { warn("can't re-stat the disc image"); return EXIT_FAILURE; } } else { if (!S_ISCHR(dev_fd_stat.st_mode)) { warnx("%s is not a raw device", dev_name); return EXIT_FAILURE; } } /* just in case something went wrong, synchronise the drive's cache */ udf_synchronise_caches(); if (udf_update_discinfo()) { warnx("update discinfo failed"); return EXIT_FAILURE; } /* honour minimum sector size of the device */ if (mmc_discinfo.sector_size > context.sector_size) context.sector_size = mmc_discinfo.sector_size; if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) udf_init_writequeue(UDF_WRITE_SEQUENTIAL); else { udf_init_writequeue(UDF_WRITE_PACKET); } return 0; } void udf_closedisc(void) { if (!write_queue_suspend) { udf_writeout_writequeue(true); assert(write_queuelen == 0); } udf_synchronise_caches(); if (dev_fd) close(dev_fd); } /* --------------------------------------------------------------------- */ static int udf_setup_writeparams(void) { #if !HAVE_NBTOOL_CONFIG_H struct mmc_writeparams mmc_writeparams; int error; if (mmc_discinfo.mmc_class == MMC_CLASS_DISC) return 0; if (S_ISREG(dev_fd_stat.st_mode)) return 0; /* * only CD burning normally needs setting up, but other disc types * might need other settings to be made. The MMC framework will set up * the necessary recording parameters according to the disc * characteristics read in. Modifications can be made in the discinfo * structure passed to change the nature of the disc. */ memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams)); mmc_writeparams.mmc_class = mmc_discinfo.mmc_class; mmc_writeparams.mmc_cur = mmc_discinfo.mmc_cur; /* * UDF dictates first track to determine track mode for the whole * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1] * To prevent problems with a `reserved' track in front we start with * the 2nd track and if that is not valid, go for the 1st. */ mmc_writeparams.tracknr = 2; mmc_writeparams.data_mode = MMC_DATAMODE_DEFAULT; /* XA disc */ mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT; /* data */ error = ioctl(dev_fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); if (error) { mmc_writeparams.tracknr = 1; error = ioctl(dev_fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); } return error; #else return 0; #endif } /* * On sequential recordable media, we might need to close the last session to * be able to write new anchors/new fs. */ static int udf_open_new_session(void) { #if !HAVE_NBTOOL_CONFIG_H struct mmc_trackinfo ti; struct mmc_op op; int tracknr, error; /* if the drive is not sequential, we're done */ if ((mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) == 0) return 0; /* close the last session if its still open */ if (mmc_discinfo.last_session_state == MMC_STATE_INCOMPLETE) { /* * Leave the disc alone if force format is not set, it will * error out later */ if (!context.create_new_session) return 0; // printf("Closing last open session if present\n"); /* close all associated tracks */ tracknr = mmc_discinfo.first_track_last_session; while (tracknr <= mmc_discinfo.last_track_last_session) { ti.tracknr = tracknr; error = udf_update_trackinfo(&ti); if (error) return error; // printf("\tClosing open track %d\n", tracknr); memset(&op, 0, sizeof(op)); op.operation = MMC_OP_CLOSETRACK; op.mmc_profile = mmc_discinfo.mmc_profile; op.tracknr = tracknr; error = ioctl(dev_fd, MMCOP, &op); if (error) return error; tracknr ++; } // printf("Closing session\n"); memset(&op, 0, sizeof(op)); op.operation = MMC_OP_CLOSESESSION; op.mmc_profile = mmc_discinfo.mmc_profile; op.sessionnr = mmc_discinfo.num_sessions; error = ioctl(dev_fd, MMCOP, &op); if (error) return error; /* update discinfo since it changed by the operations */ error = udf_update_discinfo(); if (error) return error; } #endif return 0; } /* bit paranoid but tracks may need repair before they can be written to */ static void udf_repair_tracks(void) { #if !HAVE_NBTOOL_CONFIG_H struct mmc_trackinfo ti; struct mmc_op op; int tracknr, error; tracknr = mmc_discinfo.first_track_last_session; while (tracknr <= mmc_discinfo.last_track_last_session) { ti.tracknr = tracknr; error = udf_update_trackinfo(&ti); if (error) { warnx("error updating track information for track %d", tracknr); /* resume */ tracknr++; continue; } if (ti.flags & MMC_TRACKINFO_DAMAGED) { /* * Need to repair last track before anything can be done. * this is an optional command, so ignore its error but report * warning. */ memset(&op, 0, sizeof(op)); op.operation = MMC_OP_REPAIRTRACK; op.mmc_profile = mmc_discinfo.mmc_profile; op.tracknr = ti.tracknr; error = ioctl(dev_fd, MMCOP, &op); if (error) warnx("drive notifies it can't explicitly repair " "damaged track, but it might autorepair\n"); } tracknr++; } /* tracks (if any) might not be damaged now, operations are ok now */ #endif } int udf_prepare_disc(void) { #if !HAVE_NBTOOL_CONFIG_H int error; /* setup write parameters from discinfo */ error = udf_setup_writeparams(); if (error) return error; udf_repair_tracks(); /* open new session if needed */ return udf_open_new_session(); #endif return 0; } /* --------------------------------------------------------------------- */ /* * write queue implementation */ void udf_suspend_writing(void) { write_queue_suspend = 1; } void udf_allow_writing(void) { write_queue_suspend = 0; } static void udf_init_writequeue(int write_strategy) { context.write_strategy = write_strategy; write_queue_suspend = 0; /* setup sector writeout queue's */ TAILQ_INIT(&write_queue); write_queuelen = 0; } int udf_write_sector(void *sector, uint64_t location) { struct wrpacket *packet, *found_packet; uint64_t rel_loc; uint64_t blockingnr = layout.blockingnr; int error; assert(!dev_fd_rdonly); assert(blockingnr >= 1); assert(blockingnr <= 64); /* * We have a write strategy but in practice packet writing is * preferable for all media types. */ again: /* search location */ found_packet = NULL; TAILQ_FOREACH_REVERSE(packet, &write_queue, wrpacket_list, next) { if (packet->start_sectornr <= location) { found_packet = packet; break; } } /* are we in a current packet? */ if (found_packet) { uint64_t base = found_packet->start_sectornr; if ((location >= base) && (location -base < blockingnr)) { /* fill in existing packet */ rel_loc = location - base; memcpy(found_packet->packet_data + rel_loc * context.sector_size, sector, context.sector_size); found_packet->present |= ((uint64_t) 1 << rel_loc); return 0; } } if ((write_queuelen > UDF_MAX_QUEUELEN) && !write_queue_suspend) { /* we purge the queue and reset found_packet! */ error = udf_writeout_writequeue(false); if (error) return error; goto again; } /* create new packet */ packet = calloc(1, sizeof(struct wrpacket)); if (packet == NULL) return errno; packet->packet_data = calloc(1, context.sector_size * blockingnr); if (packet->packet_data == NULL) { free(packet); return errno; } packet->start_sectornr = UDF_ROUNDDOWN(location, blockingnr) + wrtrack_skew; rel_loc = location - packet->start_sectornr; memcpy(packet->packet_data + rel_loc * context.sector_size, sector, context.sector_size); packet->present = ((uint64_t) 1 << rel_loc); if (found_packet) { TAILQ_INSERT_AFTER(&write_queue, found_packet, packet, next); } else { TAILQ_INSERT_HEAD(&write_queue, packet, next); } write_queuelen++; return 0; } int udf_read_sector(void *sector, uint64_t location) { struct wrpacket *packet, *found_packet; ssize_t ret; uint64_t rpos, rel_loc; uint64_t blockingnr = layout.blockingnr; rpos = (uint64_t) location * context.sector_size; /* search location */ found_packet = NULL; TAILQ_FOREACH_REVERSE(packet, &write_queue, wrpacket_list, next) { if (packet->start_sectornr <= location) { found_packet = packet; break; } } /* are we in a current packet? */ if (found_packet) { uint64_t base = found_packet->start_sectornr; if ((location >= base) && (location -base < blockingnr)) { /* fill in existing packet */ rel_loc = location - base; if (found_packet->present & ((uint64_t) 1 << rel_loc)) { memcpy(sector, found_packet->packet_data + rel_loc * context.sector_size, context.sector_size); } else { ret = pread(dev_fd, sector, context.sector_size, rpos); if (ret == -1) return errno; if (ret < (int) context.sector_size) return EIO; memcpy(found_packet->packet_data + rel_loc * context.sector_size, sector, context.sector_size); found_packet->present |= ((uint64_t) 1 << rel_loc); return 0; } } } /* don't create a packet just for we read something */ ret = pread(dev_fd, sector, context.sector_size, rpos); if (ret == -1) return errno; if (ret < (int) context.sector_size) return EIO; return 0; } /* * Now all write requests are queued in the TAILQ, write them out to the * disc/file image. Special care needs to be taken for devices that are only * strict overwritable i.e. only in packet size chunks * * XXX support for growing vnd? */ static int udf_writeout_writequeue(bool complete) { struct wrpacket *packet, *next_packet; int blockingnr = layout.blockingnr; int linesize, offset, ret; uint8_t *linebuf; int32_t wsects; uint64_t present, all_present = -1; uint64_t rpos, wpos; static int t = 0; if (write_queuelen == 0) return 0; if (blockingnr < 64) all_present = ((uint64_t) 1 << blockingnr) -1; linesize = blockingnr * context.sector_size; linebuf = calloc(1, linesize); assert(linebuf); /* fill in blanks if needed */ if (complete && (context.write_strategy != UDF_WRITE_SEQUENTIAL)) { TAILQ_FOREACH(packet, &write_queue, next) { present = packet->present; if (present != all_present) { printf("%c", "\\|/-"[t++ % 4]); fflush(stdout);fflush(stderr); //printf("%16lu : readin %08lx\n", packet->start_sectornr, packet->present ^ all_present); rpos = (uint64_t) packet->start_sectornr * context.sector_size; ret = pread(dev_fd, linebuf, linesize, rpos); if (ret == -1) { printf("\b"); warn("error reading in blanks, " "could indicate bad disc"); printf(" "); } for (int i = 0; i < blockingnr; i++) { //printf("present %08lx, testing bit %08lx, value %08lx\n", present, ((uint64_t) 1 << i), (present & ((uint64_t) 1 << i))); if ((present & ((uint64_t) 1 << i)) > 0) continue; //printf("NOT PRESENT\n"); offset = i * context.sector_size; memcpy(packet->packet_data + offset, linebuf + offset, context.sector_size); packet->present |= ((uint64_t) 1<present == all_present); } } /* writeout */ TAILQ_FOREACH(packet, &write_queue, next) { if (complete || (packet->present == all_present)) { printf("%c", "\\|/-"[t++ % 4]); fflush(stdout);fflush(stderr); //printf("write %lu + %d\n", packet->start_sectornr, linesize / context.sector_size); /* don't write past last possible lba */ wsects = (mmc_discinfo.last_possible_lba + 1 - packet->start_sectornr); assert(wsects >= 0); wsects = MIN(wsects, blockingnr); wpos = (uint64_t) packet->start_sectornr * context.sector_size; ret = pwrite(dev_fd, packet->packet_data, wsects * context.sector_size, wpos); printf("\b"); if (ret == -1) warn("error writing packet, " "could indicate bad disc"); } } /* removing completed packets */ TAILQ_FOREACH_SAFE(packet, &write_queue, next, next_packet) { if (complete || (packet->present == all_present)) { TAILQ_REMOVE(&write_queue, packet, next); free(packet->packet_data); free(packet); write_queuelen--; } } if (complete) { assert(TAILQ_EMPTY(&write_queue)); write_queuelen = 0; } free(linebuf); return 0; } /* --------------------------------------------------------------------- */ /* simplified version of kernel routine */ int udf_translate_vtop(uint32_t lb_num, uint16_t vpart, uint32_t *lb_numres, uint32_t *extres) { struct part_desc *pdesc; struct spare_map_entry *sme; struct short_ad *short_ad; struct extfile_entry *efe; uint32_t ext, len, lb_rel, lb_packet, vat_off; uint32_t start_lb, lb_offset, end_lb_offset; uint32_t udf_rw32_lbmap; uint32_t flags; uint8_t *vat_pos, *data_pos; int dscr_size, l_ea, l_ad, icbflags, addr_type; int rel, part; if (vpart > UDF_VTOP_RAWPART) return EINVAL; ext = INT_MAX; translate_again: part = context.vtop[vpart]; pdesc = context.partitions[part]; switch (context.vtop_tp[vpart]) { case UDF_VTOP_TYPE_RAW : /* 1:1 to the end of the device */ *lb_numres = lb_num; *extres = MIN(ext, INT_MAX); return 0; case UDF_VTOP_TYPE_PHYS : /* transform into its disc logical block */ if (lb_num > udf_rw32(pdesc->part_len)) return EINVAL; *lb_numres = lb_num + udf_rw32(pdesc->start_loc); /* extent from here to the end of the partition */ *extres = MIN(ext, udf_rw32(pdesc->part_len) - lb_num); if (*extres == 0) return EINVAL; return 0; case UDF_VTOP_TYPE_VIRT : /* only maps one logical block, lookup in VAT */ if (lb_num * 4 >= context.vat_size) return EINVAL; vat_off = context.vat_start + lb_num * 4; vat_pos = context.vat_contents + vat_off; udf_rw32_lbmap = *((uint32_t *) vat_pos); if (vat_off >= context.vat_size) /* XXX > or >= ? */ return EINVAL; lb_num = udf_rw32(udf_rw32_lbmap); /* transform into its disc logical block */ if (lb_num > udf_rw32(pdesc->part_len)) return EINVAL; *lb_numres = lb_num + udf_rw32(pdesc->start_loc); /* just one logical block */ *extres = 1; return 0; case UDF_VTOP_TYPE_SPAREABLE : /* check if the packet containing the lb_num is remapped */ lb_packet = lb_num / layout.spareable_blockingnr; lb_rel = lb_num % layout.spareable_blockingnr; for (rel = 0; rel < udf_rw16(context.sparing_table->rt_l); rel++) { sme = &context.sparing_table->entries[rel]; if (lb_packet == udf_rw32(sme->org)) { /* NOTE maps to absolute disc logical block! */ *lb_numres = udf_rw32(sme->map) + lb_rel; *extres = layout.spareable_blockingnr - lb_rel; return 0; } } /* transform into its disc logical block */ if (lb_num > udf_rw32(pdesc->part_len)) return EINVAL; *lb_numres = lb_num + udf_rw32(pdesc->start_loc); /* rest of block */ *extres = MIN(ext, layout.spareable_blockingnr - lb_rel); return 0; case UDF_VTOP_TYPE_META : /* we have to look into the file's allocation descriptors */ /* get first overlapping extent */ efe = context.meta_file; dscr_size = sizeof(struct extfile_entry) - 1; l_ea = udf_rw32(efe->l_ea); l_ad = udf_rw32(efe->l_ad); icbflags = udf_rw16(efe->icbtag.flags); addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; if (addr_type != UDF_ICB_SHORT_ALLOC) { warnx("specification violation: metafile not using" "short allocs"); return EINVAL; } data_pos = (uint8_t *) context.meta_file + dscr_size + l_ea; short_ad = (struct short_ad *) data_pos; lb_offset = 0; while (l_ad > 0) { len = udf_rw32(short_ad->len); start_lb = udf_rw32(short_ad->lb_num); flags = UDF_EXT_FLAGS(len); len = UDF_EXT_LEN(len); if (flags == UDF_EXT_REDIRECT) { warnx("implementation limit: no support for " "extent redirection in metadata file"); return EINVAL; } end_lb_offset = lb_offset + len / context.sector_size; /* overlap? */ if (end_lb_offset > lb_num) break; short_ad++; lb_offset = end_lb_offset; l_ad -= sizeof(struct short_ad); } if (l_ad <= 0) { warnx("looking up outside metadata partition!"); return EINVAL; } lb_num = start_lb + (lb_num - lb_offset); vpart = part; ext = end_lb_offset - lb_num; /* * vpart and lb_num are updated, translate again since we * might be mapped on spareable media */ goto translate_again; default: printf("UDF vtop translation scheme %d unimplemented yet\n", context.vtop_tp[vpart]); } return EINVAL; } /* --------------------------------------------------------------------- */ int udf_read_phys(void *blob, uint32_t location, uint32_t sects) { uint32_t phys, cnt; uint8_t *bpos; int error; for (cnt = 0; cnt < sects; cnt++) { bpos = (uint8_t *) blob; bpos += context.sector_size * cnt; phys = location + cnt; error = udf_read_sector(bpos, phys); if (error) return error; } return 0; } int udf_write_phys(void *blob, uint32_t location, uint32_t sects) { uint32_t phys, cnt; uint8_t *bpos; int error; for (cnt = 0; cnt < sects; cnt++) { bpos = (uint8_t *) blob; bpos += context.sector_size * cnt; phys = location + cnt; error = udf_write_sector(bpos, phys); if (error) return error; } return 0; } int udf_read_virt(void *blob, uint32_t location, uint16_t vpart, uint32_t sectors) { uint32_t phys, ext; uint8_t *data; int error; /* determine physical location */ data = (uint8_t *) blob; while (sectors) { if (udf_translate_vtop(location, vpart, &phys, &ext)) { // warnx("internal error: bad translation"); return EINVAL; } ext = MIN(sectors, ext); error = udf_read_phys(data, phys, ext); if (error) return error; location += ext; data += ext * context.sector_size; sectors -= ext; } return 0; } int udf_write_virt(void *blob, uint32_t location, uint16_t vpart, uint32_t sectors) { uint32_t phys, ext, alloc_pos; uint8_t *data; int error; /* determine physical location */ if (context.vtop_tp[vpart] == UDF_VTOP_TYPE_VIRT) { assert(sectors == 1); alloc_pos = context.alloc_pos[context.data_part]; udf_vat_update(location, alloc_pos); udf_translate_vtop(alloc_pos, context.vtop[vpart], &phys, &ext); context.alloc_pos[context.data_part]++; return udf_write_phys(blob, phys, sectors); } data = (uint8_t *) blob; while (sectors) { if (udf_translate_vtop(location, vpart, &phys, &ext)) { warnx("internal error: bad translation"); return EINVAL; } ext = MIN(sectors, ext); error = udf_write_phys(data, phys, ext); if (error) return error; location += ext; data += ext * context.sector_size; sectors -= ext; } return 0; } int udf_read_dscr_phys(uint32_t sector, union dscrptr **dstp) { union dscrptr *dst, *new_dst; uint8_t *pos; uint32_t sectors, dscrlen, sector_size; int error; sector_size = context.sector_size; *dstp = dst = NULL; dscrlen = sector_size; /* read initial piece */ dst = malloc(sector_size); assert(dst); error = udf_read_sector(dst, sector); // if (error) // warn("read error"); if (!error) { /* check if its an empty block */ if (is_zero(dst, sector_size)) { /* return no error but with no dscrptr */ /* dispose first block */ free(dst); return 0; } /* check if its a valid tag */ error = udf_check_tag(dst); if (error) { free(dst); return 0; } /* calculate descriptor size */ dscrlen = udf_tagsize(dst, sector_size); } if (!error && (dscrlen > sector_size)) { /* read the rest of descriptor */ new_dst = realloc(dst, dscrlen); if (new_dst == NULL) { free(dst); return ENOMEM; } dst = new_dst; sectors = dscrlen / sector_size; pos = (uint8_t *) dst + sector_size; error = udf_read_phys(pos, sector + 1, sectors-1); if (error) warnx("read error"); } if (!error) error = udf_check_tag_payload(dst, dscrlen); if (error && dst) { free(dst); dst = NULL; } *dstp = dst; return error; } int udf_write_dscr_phys(union dscrptr *dscr, uint32_t location, uint32_t sectors) { dscr->tag.tag_loc = udf_rw32(location); (void) udf_validate_tag_and_crc_sums(dscr); assert(sectors == udf_tagsize(dscr, context.sector_size) / context.sector_size); return udf_write_phys(dscr, location, sectors); } int udf_read_dscr_virt(uint32_t sector, uint16_t vpart, union dscrptr **dstp) { union dscrptr *dst, *new_dst; uint8_t *pos; uint32_t sectors, dscrlen, sector_size; int error; sector_size = context.sector_size; *dstp = dst = NULL; dscrlen = sector_size; /* read initial piece */ dst = calloc(1, sector_size); assert(dst); error = udf_read_virt(dst, sector, vpart, 1); if (error) return error; if (!error) { /* check if its a valid tag */ error = udf_check_tag(dst); if (error) { /* check if its an empty block */ if (is_zero(dst, sector_size)) { /* return no error but with no dscrptr */ /* dispose first block */ free(dst); return 0; } } /* calculate descriptor size */ dscrlen = udf_tagsize(dst, sector_size); } if (!error && (dscrlen > sector_size)) { /* read the rest of descriptor */ new_dst = realloc(dst, dscrlen); if (new_dst == NULL) { free(dst); return ENOMEM; } dst = new_dst; sectors = dscrlen / sector_size; pos = (uint8_t *) dst + sector_size; error = udf_read_virt(pos, sector + 1, vpart, sectors-1); if (error) warn("read error"); } if (!error) error = udf_check_tag_payload(dst, dscrlen); if (error && dst) { free(dst); dst = NULL; } *dstp = dst; return error; } int udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint16_t vpart, uint32_t sectors) { struct file_entry *fe; struct extfile_entry *efe; struct extattrhdr_desc *extattrhdr; extattrhdr = NULL; if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { fe = (struct file_entry *) dscr; if (udf_rw32(fe->l_ea) > 0) extattrhdr = (struct extattrhdr_desc *) fe->data; } if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { efe = (struct extfile_entry *) dscr; if (udf_rw32(efe->l_ea) > 0) extattrhdr = (struct extattrhdr_desc *) efe->data; } if (extattrhdr) { extattrhdr->tag.tag_loc = udf_rw32(location); udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr); } dscr->tag.tag_loc = udf_rw32(location); udf_validate_tag_and_crc_sums(dscr); assert(sectors >= (udf_tagsize(dscr, context.sector_size) / context.sector_size)); return udf_write_virt(dscr, location, vpart, sectors); } int is_zero(void *blob, int size) { uint8_t *p = blob; for (int i = 0; i < size; i++, p++) if (*p) return 0; return 1; } /* --------------------------------------------------------------------- */ static void udf_partition_alloc(int nblk, int vpart, struct long_ad *pos) { memset(pos, 0, sizeof(*pos)); pos->len = udf_rw32(nblk * context.sector_size); pos->loc.lb_num = udf_rw32(context.alloc_pos[vpart]); pos->loc.part_num = udf_rw16(vpart); udf_mark_allocated(context.alloc_pos[vpart], vpart, nblk); context.alloc_pos[vpart] += nblk; } void udf_metadata_alloc(int nblk, struct long_ad *pos) { udf_partition_alloc(nblk, context.metadata_part, pos); } void udf_data_alloc(int nblk, struct long_ad *pos) { udf_partition_alloc(nblk, context.data_part, pos); } void udf_fids_alloc(int nblk, struct long_ad *pos) { udf_partition_alloc(nblk, context.fids_part, pos); } /* --------------------------------------------------------------------- */ /* * udf_derive_format derives the format_flags from the disc's mmc_discinfo. * The resulting flags uniquely define a disc format. Note there are at least * 7 distinct format types defined in UDF. */ #define UDF_VERSION(a) \ (((a) == 0x102) || ((a) == 0x150) || ((a) == 0x200) || \ ((a) == 0x201) || ((a) == 0x250) || ((a) == 0x260)) int udf_derive_format(int req_enable, int req_disable) { int format_flags; int media_accesstype; /* disc writability, formatted, appendable */ if ((mmc_discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) { warnx("can't newfs readonly device"); return EROFS; } if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { /* sequentials need sessions appended */ if (mmc_discinfo.disc_state == MMC_STATE_CLOSED) { warnx("can't append session to a closed disc"); return EROFS; } if ((mmc_discinfo.disc_state != MMC_STATE_EMPTY) && !context.create_new_session) { warnx("disc not empty! Use -F to force " "initialisation"); return EROFS; } } else { /* check if disc (being) formatted or has been started on */ if (mmc_discinfo.disc_state == MMC_STATE_EMPTY) { warnx("disc is not formatted"); return EROFS; } } /* determine UDF format */ format_flags = 0; if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) { /* all rewritable media */ format_flags |= FORMAT_REWRITABLE; if (context.min_udf >= 0x0250) { /* standard dictates meta as default */ format_flags |= FORMAT_META; } if ((mmc_discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) == 0) { /* spareables for defect management */ if (context.min_udf >= 0x150) format_flags |= FORMAT_SPAREABLE; } } else { /* all once recordable media */ format_flags |= FORMAT_WRITEONCE; if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { format_flags |= FORMAT_SEQUENTIAL; if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) { /* logical overwritable */ format_flags |= FORMAT_LOW; } else { /* have to use VAT for overwriting */ format_flags |= FORMAT_VAT; } } else { /* rare WORM devices, but BluRay has one, strat4096 */ format_flags |= FORMAT_WORM; } } /* enable/disable requests */ if (req_disable & FORMAT_META) { format_flags &= ~(FORMAT_META | FORMAT_LOW); req_disable &= ~FORMAT_META; } if ((format_flags & FORMAT_VAT) & UDF_512_TRACK) format_flags |= FORMAT_TRACK512; if (req_enable & FORMAT_READONLY) { format_flags |= FORMAT_READONLY; } /* determine partition/media access type */ media_accesstype = UDF_ACCESSTYPE_NOT_SPECIFIED; if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) { media_accesstype = UDF_ACCESSTYPE_OVERWRITABLE; if (mmc_discinfo.mmc_cur & MMC_CAP_ERASABLE) media_accesstype = UDF_ACCESSTYPE_REWRITEABLE; } else { /* all once recordable media */ media_accesstype = UDF_ACCESSTYPE_WRITE_ONCE; } if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) media_accesstype = UDF_ACCESSTYPE_PSEUDO_OVERWITE; /* patch up media accesstype */ if (req_enable & FORMAT_READONLY) { /* better now */ media_accesstype = UDF_ACCESSTYPE_READ_ONLY; } /* adjust minimum version limits */ if (format_flags & FORMAT_VAT) context.min_udf = MAX(context.min_udf, 0x0150); if (format_flags & FORMAT_SPAREABLE) context.min_udf = MAX(context.min_udf, 0x0150); if (format_flags & FORMAT_META) context.min_udf = MAX(context.min_udf, 0x0250); if (format_flags & FORMAT_LOW) context.min_udf = MAX(context.min_udf, 0x0260); /* adjust maximum version limits not to tease or break things */ if (!(format_flags & (FORMAT_META | FORMAT_LOW | FORMAT_VAT)) && (context.max_udf > 0x200)) context.max_udf = 0x201; if ((format_flags & (FORMAT_VAT | FORMAT_SPAREABLE)) == 0) if (context.max_udf <= 0x150) context.min_udf = 0x102; /* limit Ecma 167 descriptor if possible/needed */ context.dscrver = 3; if ((context.min_udf < 0x200) || (context.max_udf < 0x200)) { context.dscrver = 2; context.max_udf = 0x150; /* last version < 0x200 */ } /* is it possible ? */ if (context.min_udf > context.max_udf) { warnx("initialisation prohibited by specified maximum " "UDF version 0x%04x. Minimum version required 0x%04x", context.max_udf, context.min_udf); return EPERM; } if (!UDF_VERSION(context.min_udf) || !UDF_VERSION(context.max_udf)) { warnx("internal error, invalid min/max udf versionsi in %s", __func__); return EPERM; } context.format_flags = format_flags; context.media_accesstype = media_accesstype; return 0; } #undef UDF_VERSION /* --------------------------------------------------------------------- */ int udf_proces_names(void) { struct timeval time_of_day; uint32_t primary_nr; uint64_t volset_nr; if (context.logvol_name == NULL) context.logvol_name = strdup("anonymous"); if (context.primary_name == NULL) { if (mmc_discinfo.disc_flags & MMC_DFLAGS_DISCIDVALID) { primary_nr = mmc_discinfo.disc_id; } else { primary_nr = (uint32_t) random(); } context.primary_name = calloc(32, 1); sprintf(context.primary_name, "%08"PRIx32, primary_nr); } if (context.volset_name == NULL) { if (mmc_discinfo.disc_flags & MMC_DFLAGS_BARCODEVALID) { volset_nr = mmc_discinfo.disc_barcode; } else { (void)gettimeofday(&time_of_day, NULL); volset_nr = (uint64_t) random(); volset_nr |= ((uint64_t) time_of_day.tv_sec) << 32; } context.volset_name = calloc(128,1); sprintf(context.volset_name, "%016"PRIx64, volset_nr); } if (context.fileset_name == NULL) context.fileset_name = strdup("anonymous"); /* check passed/created identifiers */ if (strlen(context.logvol_name) > 128) { warnx("logical volume name too long"); return EINVAL; } if (strlen(context.primary_name) > 32) { warnx("primary volume name too long"); return EINVAL; } if (strlen(context.volset_name) > 128) { warnx("volume set name too long"); return EINVAL; } if (strlen(context.fileset_name) > 32) { warnx("fileset name too long"); return EINVAL; } /* signal all OK */ return 0; } /* --------------------------------------------------------------------- */ int udf_write_iso9660_vrs(void) { struct vrs_desc *iso9660_vrs_desc; uint32_t pos; int error, cnt, dpos; /* create ISO/Ecma-167 identification descriptors */ if ((iso9660_vrs_desc = calloc(1, context.sector_size)) == NULL) return ENOMEM; /* * All UDF formats should have their ISO/Ecma-167 descriptors written * except when not possible due to track reservation in the case of * VAT */ if ((context.format_flags & FORMAT_TRACK512) == 0) { dpos = (2048 + context.sector_size - 1) / context.sector_size; /* wipe at least 6 times 2048 byte `sectors' */ for (cnt = 0; cnt < 6 *dpos; cnt++) { pos = layout.iso9660_vrs + cnt; if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { free(iso9660_vrs_desc); return error; } } /* common VRS fields in all written out ISO descriptors */ iso9660_vrs_desc->struct_type = 0; iso9660_vrs_desc->version = 1; pos = layout.iso9660_vrs; /* BEA01, NSR[23], TEA01 */ memcpy(iso9660_vrs_desc->identifier, "BEA01", 5); if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { free(iso9660_vrs_desc); return error; } pos += dpos; if (context.dscrver == 2) memcpy(iso9660_vrs_desc->identifier, "NSR02", 5); else memcpy(iso9660_vrs_desc->identifier, "NSR03", 5); ; if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { free(iso9660_vrs_desc); return error; } pos += dpos; memcpy(iso9660_vrs_desc->identifier, "TEA01", 5); if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { free(iso9660_vrs_desc); return error; } } free(iso9660_vrs_desc); /* return success */ return 0; } /* --------------------------------------------------------------------- */ int udf_get_blockingnr(struct mmc_trackinfo *ti) { int blockingnr; /* determine blockingnr */ blockingnr = ti->packet_size; if (blockingnr <= 1) { /* paranoia on blockingnr */ switch (mmc_discinfo.mmc_profile) { case 0x01 : /* DISC */ blockingnr = 64; break; case 0x08 : /* CDROM */ case 0x09 : /* CD-R */ case 0x0a : /* CD-RW */ blockingnr = 32; /* UDF requirement */ break; case 0x10 : /* DVDROM */ case 0x11 : /* DVD-R (DL) */ case 0x12 : /* DVD-RAM */ case 0x1b : /* DVD+R */ case 0x2b : /* DVD+R Dual layer */ case 0x13 : /* DVD-RW restricted overwrite */ case 0x14 : /* DVD-RW sequential */ case 0x1a : /* DVD+RW */ blockingnr = 16; /* SCSI definition */ break; case 0x40 : /* BDROM */ case 0x41 : /* BD-R Sequential recording (SRM) */ case 0x42 : /* BD-R Random recording (RRM) */ case 0x43 : /* BD-RE */ case 0x51 : /* HD DVD-R */ case 0x52 : /* HD DVD-RW */ blockingnr = 32; /* SCSI definition */ break; default: break; } } return blockingnr; } int udf_spareable_blocks(void) { if (mmc_discinfo.mmc_class == MMC_CLASS_CD) { /* not too much for CD-RW, still 20MiB */ return 32; } else { /* take a value for DVD*RW mainly, BD is `defect free' */ return 512; } } int udf_spareable_blockingnr(void) { struct mmc_trackinfo ti; int spareable_blockingnr; int error; /* determine span/size */ ti.tracknr = mmc_discinfo.first_track_last_session; error = udf_update_trackinfo(&ti); spareable_blockingnr = udf_get_blockingnr(&ti); if (error) spareable_blockingnr = 32; /* * Note that for (bug) compatibility with version UDF 2.00 * (fixed in 2.01 and higher) the blocking size needs to be 32 * sectors otherwise the drive's blockingnr. */ if (context.min_udf <= 0x200) spareable_blockingnr = 32; return spareable_blockingnr; } /* * Main function that creates and writes out disc contents based on the * format_flags's that uniquely define the type of disc to create. */ int udf_do_newfs_prefix(void) { union dscrptr *zero_dscr; union dscrptr *dscr; struct mmc_trackinfo ti; uint32_t blockingnr; uint32_t cnt, loc, len; int sectcopy; int error, integrity_type; int data_part, metadata_part; int format_flags; /* init */ format_flags = context.format_flags; /* determine span/size */ ti.tracknr = mmc_discinfo.first_track_last_session; error = udf_update_trackinfo(&ti); if (error) return error; if (mmc_discinfo.sector_size > context.sector_size) { warnx("impossible to format: " "sector size %d too small for media sector size %d", context.sector_size, mmc_discinfo.sector_size); return EIO; } /* determine blockingnr */ blockingnr = udf_get_blockingnr(&ti); if (blockingnr <= 0) { warnx("can't fixup blockingnumber for device " "type %d", mmc_discinfo.mmc_profile); warnx("device is not returning valid blocking" " number and media type is unknown"); return EINVAL; } wrtrack_skew = 0; if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) wrtrack_skew = ti.next_writable % blockingnr; /* get layout */ error = udf_calculate_disc_layout(context.min_udf, ti.track_start, mmc_discinfo.last_possible_lba, context.sector_size, blockingnr); /* cache partition for we need it often */ data_part = context.data_part; metadata_part = context.metadata_part; /* Create sparing table descriptor if applicable */ if (format_flags & FORMAT_SPAREABLE) { if ((error = udf_create_sparing_tabled())) return error; if (context.check_surface) { if ((error = udf_surface_check())) return error; } } /* Create a generic terminator descriptor (later reused) */ terminator_dscr = calloc(1, context.sector_size); if (terminator_dscr == NULL) return ENOMEM; udf_create_terminator(terminator_dscr, 0); /* * Create the two Volume Descriptor Sets (VDS) each containing the * following descriptors : primary volume, partition space, * unallocated space, logical volume, implementation use and the * terminator */ /* start of volume recognition sequence building */ context.vds_seq = 0; /* Create primary volume descriptor */ if ((error = udf_create_primaryd())) return error; /* Create partition descriptor */ if ((error = udf_create_partitiond(context.data_part))) return error; /* Create unallocated space descriptor */ if ((error = udf_create_unalloc_spaced())) return error; /* Create logical volume descriptor */ if ((error = udf_create_logical_dscr())) return error; /* Create implementation use descriptor */ /* TODO input of fields 1,2,3 and passing them */ if ((error = udf_create_impvold(NULL, NULL, NULL))) return error; /* Create anchors */ for (cnt = 0; cnt < 3; cnt++) { if ((error = udf_create_anchor(cnt))) { return error; } } /* * Write out what we've created so far. * * Start with wipeout of VRS1 upto start of partition. This allows * formatting for sequentials with the track reservation and it * cleans old rubbish on rewritables. For sequentials without the * track reservation all is wiped from track start. */ if ((zero_dscr = calloc(1, context.sector_size)) == NULL) return ENOMEM; loc = (format_flags & FORMAT_TRACK512) ? layout.vds1 : ti.track_start; for (; loc < layout.part_start_lba; loc++) { if ((error = udf_write_sector(zero_dscr, loc))) { free(zero_dscr); return error; } } free(zero_dscr); /* writeout iso9660 vrs */ if ((error = udf_write_iso9660_vrs())) return error; /* Writeout anchors */ for (cnt = 0; cnt < 3; cnt++) { dscr = (union dscrptr *) context.anchors[cnt]; loc = layout.anchors[cnt]; if ((error = udf_write_dscr_phys(dscr, loc, 1))) { err(1, "ERR!"); return error; } /* sequential media has only one anchor */ if (format_flags & FORMAT_SEQUENTIAL) break; } /* write out main and secondary VRS */ for (sectcopy = 1; sectcopy <= 2; sectcopy++) { loc = (sectcopy == 1) ? layout.vds1 : layout.vds2; /* primary volume descriptor */ dscr = (union dscrptr *) context.primary_vol; error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; /* partition descriptor(s) */ for (cnt = 0; cnt < UDF_PARTITIONS; cnt++) { dscr = (union dscrptr *) context.partitions[cnt]; if (dscr) { error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; } } /* unallocated space descriptor */ dscr = (union dscrptr *) context.unallocated; error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; /* logical volume descriptor */ dscr = (union dscrptr *) context.logical_vol; error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; /* implementation use descriptor */ dscr = (union dscrptr *) context.implementation; error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; /* terminator descriptor */ error = udf_write_dscr_phys(terminator_dscr, loc, 1); if (error) return error; loc++; } /* writeout the two spareable table descriptors (if needed) */ if (format_flags & FORMAT_SPAREABLE) { for (sectcopy = 1; sectcopy <= 2; sectcopy++) { loc = (sectcopy == 1) ? layout.spt_1 : layout.spt_2; dscr = (union dscrptr *) context.sparing_table; len = udf_tagsize(dscr, context.sector_size) / context.sector_size; /* writeout */ error = udf_write_dscr_phys(dscr, loc, len); if (error) return error; } } /* * Create unallocated space bitmap descriptor. Sequential recorded * media report their own free/used space; no free/used space tables * should be recorded for these. */ if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { error = udf_create_space_bitmap( layout.alloc_bitmap_dscr_size, layout.part_size_lba, &context.part_unalloc_bits[data_part]); if (error) return error; /* TODO: freed space bitmap if applicable */ /* mark space allocated for the unallocated space bitmap */ udf_mark_allocated(layout.unalloc_space, data_part, layout.alloc_bitmap_dscr_size); } /* * Create metadata partition file entries and allocate and init their * space and free space maps. */ if (format_flags & FORMAT_META) { error = udf_create_meta_files(); if (error) return error; /* mark space allocated for meta partition and its bitmap */ udf_mark_allocated(layout.meta_file, data_part, 1); udf_mark_allocated(layout.meta_mirror, data_part, 1); udf_mark_allocated(layout.meta_part_start_lba, data_part, layout.meta_part_size_lba); if (context.meta_bitmap) { /* metadata bitmap creation and accounting */ error = udf_create_space_bitmap( layout.meta_bitmap_dscr_size, layout.meta_part_size_lba, &context.part_unalloc_bits[metadata_part]); if (error) return error; udf_mark_allocated(layout.meta_bitmap, data_part, 1); /* mark space allocated for the unallocated space bitmap */ udf_mark_allocated(layout.meta_bitmap_space, data_part, layout.meta_bitmap_dscr_size); } } /* create logical volume integrity descriptor */ context.num_files = 0; context.num_directories = 0; integrity_type = UDF_INTEGRITY_OPEN; if ((error = udf_create_lvintd(integrity_type))) return error; /* writeout initial open integrity sequence + terminator */ loc = layout.lvis; dscr = (union dscrptr *) context.logvol_integrity; error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; error = udf_write_dscr_phys(terminator_dscr, loc, 1); if (error) return error; /* create VAT if needed */ if (format_flags & FORMAT_VAT) { context.vat_allocated = context.sector_size; context.vat_contents = malloc(context.vat_allocated); assert(context.vat_contents); udf_prepend_VAT_file(); } /* create FSD and writeout */ if ((error = udf_create_fsd())) return error; udf_mark_allocated(layout.fsd, metadata_part, 1); dscr = (union dscrptr *) context.fileset_desc; error = udf_write_dscr_virt(dscr, layout.fsd, metadata_part, 1); return error; } /* specific routine for newfs to create empty rootdirectory */ int udf_do_rootdir(void) { union dscrptr *root_dscr; int error; /* create root directory and write out */ assert(context.unique_id == 0x10); context.unique_id = 0; if ((error = udf_create_new_rootdir(&root_dscr))) return error; udf_mark_allocated(layout.rootdir, context.metadata_part, 1); error = udf_write_dscr_virt(root_dscr, layout.rootdir, context.metadata_part, 1); free(root_dscr); return error; } int udf_do_newfs_postfix(void) { union dscrptr *dscr; uint32_t loc, len; int data_part, metadata_part; int format_flags = context.format_flags; int error; /* cache partition for we need it often */ data_part = context.data_part; metadata_part = context.metadata_part; if ((format_flags & FORMAT_SEQUENTIAL) == 0) { /* update lvint and mark it closed */ udf_update_lvintd(UDF_INTEGRITY_CLOSED); /* overwrite initial terminator */ loc = layout.lvis+1; dscr = (union dscrptr *) context.logvol_integrity; error = udf_write_dscr_phys(dscr, loc, 1); if (error) return error; loc++; /* mark end of integrity descriptor sequence again */ error = udf_write_dscr_phys(terminator_dscr, loc, 1); if (error) return error; } /* write out unallocated space bitmap on non sequential media */ if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { /* writeout unallocated space bitmap */ loc = layout.unalloc_space; dscr = (union dscrptr *) (context.part_unalloc_bits[data_part]); len = layout.alloc_bitmap_dscr_size; error = udf_write_dscr_virt(dscr, loc, data_part, len); if (error) return error; } if (format_flags & FORMAT_META) { loc = layout.meta_file; dscr = (union dscrptr *) context.meta_file; error = udf_write_dscr_virt(dscr, loc, data_part, 1); if (error) return error; loc = layout.meta_mirror; dscr = (union dscrptr *) context.meta_mirror; error = udf_write_dscr_virt(dscr, loc, data_part, 1); if (error) return error; if (context.meta_bitmap) { loc = layout.meta_bitmap; dscr = (union dscrptr *) context.meta_bitmap; error = udf_write_dscr_virt(dscr, loc, data_part, 1); if (error) return error; /* writeout unallocated space bitmap */ loc = layout.meta_bitmap_space; dscr = (union dscrptr *) (context.part_unalloc_bits[metadata_part]); len = layout.meta_bitmap_dscr_size; error = udf_write_dscr_virt(dscr, loc, data_part, len); if (error) return error; } } /* create and writeout a VAT */ if (format_flags & FORMAT_VAT) udf_writeout_VAT(); /* done */ return 0; }