NetBSD/sbin/newfs_udf/udf_core.c

5113 lines
132 KiB
C

/* $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 <sys/cdefs.h>
__RCSID("$NetBSD: udf_core.c,v 1.14 2024/02/05 21:46:05 andvar Exp $");
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <util.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#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 <scottl@freebsd.org>
*/
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(&timespec->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<<i);
}
printf("\b");
}
assert(packet->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;
}