summaryrefslogtreecommitdiff
path: root/mkudffs/mkudffs.c
diff options
context:
space:
mode:
Diffstat (limited to 'mkudffs/mkudffs.c')
-rw-r--r--mkudffs/mkudffs.c212
1 files changed, 143 insertions, 69 deletions
diff --git a/mkudffs/mkudffs.c b/mkudffs/mkudffs.c
index 9ad3500..59b529c 100644
--- a/mkudffs/mkudffs.c
+++ b/mkudffs/mkudffs.c
@@ -1,8 +1,8 @@
/*
* mkudffs.c
*
- * Copyright (c) 2001-2002 Ben Fennema <bfennema@falcon.csc.calpoly.edu>
- * Copyright (c) 2014-2017 Pali Rohár <pali.rohar@gmail.com>
+ * Copyright (c) 2001-2002 Ben Fennema
+ * Copyright (c) 2014-2018 Pali Rohár <pali.rohar@gmail.com>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -30,6 +30,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -50,9 +51,8 @@ void udf_init_disc(struct udf_disc *disc)
struct timeval tv;
struct tm *tm;
int altzone;
- unsigned long int rnd;
-
- srand(time(NULL));
+ uint32_t uuid_time;
+ char uuid[17];
memset(disc, 0x00, sizeof(*disc));
@@ -62,31 +62,50 @@ void udf_init_disc(struct udf_disc *disc)
disc->blkssz = 512;
disc->mode = 0755;
- gettimeofday(&tv, NULL);
- tm = localtime(&tv.tv_sec);
- altzone = timezone - 3600;
- if (daylight)
- ts.typeAndTimezone = cpu_to_le16(((-altzone/60) & 0x0FFF) | 0x1000);
+ if (gettimeofday(&tv, NULL) != 0 || tv.tv_sec == (time_t)-1 || (tm = localtime(&tv.tv_sec)) == NULL || tm->tm_year < 1-1900 || tm->tm_year > 9999-1900)
+ {
+ /* fallback to 1.1.1980 00:00:00 */
+ ts.typeAndTimezone = cpu_to_le16(0x1000);
+ ts.year = cpu_to_le16(1980);
+ ts.month = 1;
+ ts.day = 1;
+ ts.hour = 0;
+ ts.minute = 0;
+ ts.second = 0;
+ ts.centiseconds = 0;
+ ts.hundredsOfMicroseconds = 0;
+ ts.microseconds = 0;
+ /* and for uuid use random bytes */
+ uuid_time = randu32();
+ }
else
- ts.typeAndTimezone = cpu_to_le16(((-timezone/60) & 0x0FFF) | 0x1000);
- ts.year = cpu_to_le16(1900 + tm->tm_year);
- ts.month = 1 + tm->tm_mon;
- ts.day = tm->tm_mday;
- ts.hour = tm->tm_hour;
- ts.minute = tm->tm_min;
- ts.second = tm->tm_sec;
- ts.centiseconds = tv.tv_usec / 10000;
- ts.hundredsOfMicroseconds = (tv.tv_usec - ts.centiseconds * 10000) / 100;
- ts.microseconds = tv.tv_usec - ts.centiseconds * 10000 - ts.hundredsOfMicroseconds * 100;
-
- get_random_bytes(&rnd, sizeof(rnd));
+ {
+ altzone = timezone - 3600;
+ if (daylight)
+ ts.typeAndTimezone = cpu_to_le16(((-altzone/60) & 0x0FFF) | 0x1000);
+ else
+ ts.typeAndTimezone = cpu_to_le16(((-timezone/60) & 0x0FFF) | 0x1000);
+ ts.year = cpu_to_le16(1900 + tm->tm_year);
+ ts.month = 1 + tm->tm_mon;
+ ts.day = tm->tm_mday;
+ ts.hour = tm->tm_hour;
+ ts.minute = tm->tm_min;
+ ts.second = tm->tm_sec;
+ ts.centiseconds = tv.tv_usec / 10000;
+ ts.hundredsOfMicroseconds = (tv.tv_usec - ts.centiseconds * 10000) / 100;
+ ts.microseconds = tv.tv_usec - ts.centiseconds * 10000 - ts.hundredsOfMicroseconds * 100;
+ if (tv.tv_sec < 0)
+ uuid_time = randu32();
+ else
+ uuid_time = tv.tv_sec & 0xFFFFFFFF;
+ }
/* Allocate/Initialize Descriptors */
disc->udf_pvd[0] = malloc(sizeof(struct primaryVolDesc));
memcpy(disc->udf_pvd[0], &default_pvd, sizeof(struct primaryVolDesc));
memcpy(&disc->udf_pvd[0]->recordingDateAndTime, &ts, sizeof(timestamp));
- sprintf((char *)&disc->udf_pvd[0]->volSetIdent[1], "%08lx%08lx%s",
- ((unsigned long int)mktime(tm))%0xFFFFFFFF, rnd%0xFFFFFFFF, &disc->udf_pvd[0]->volSetIdent[17]);
+ snprintf(uuid, sizeof(uuid), "%08lx%08lx", (unsigned long int)uuid_time, (unsigned long int)randu32());
+ memcpy(&disc->udf_pvd[0]->volSetIdent[1], uuid, 16);
disc->udf_pvd[0]->volIdent[31] = strlen((char *)disc->udf_pvd[0]->volIdent);
disc->udf_pvd[0]->volSetIdent[127] = strlen((char *)disc->udf_pvd[0]->volSetIdent);
@@ -142,14 +161,16 @@ void udf_init_disc(struct udf_disc *disc)
disc->head->tail = NULL;
}
-int udf_set_version(struct udf_disc *disc, int udf_rev)
+int udf_set_version(struct udf_disc *disc, uint16_t udf_rev)
{
struct logicalVolIntegrityDescImpUse *lvidiu;
uint16_t udf_rev_le16;
if (disc->udf_rev == udf_rev)
return 0;
- else if (udf_rev != 0x0102 &&
+ else if (
+ udf_rev != 0x0101 &&
+ udf_rev != 0x0102 &&
udf_rev != 0x0150 &&
udf_rev != 0x0200 &&
udf_rev != 0x0201 &&
@@ -172,7 +193,7 @@ int udf_set_version(struct udf_disc *disc, int udf_rev)
disc->flags &= ~FLAG_EFE;
strcpy((char *)disc->udf_pd[0]->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02);
}
- else // 0x0102
+ else // 0x0102 and older
{
disc->flags &= ~FLAG_VAT;
disc->flags &= ~FLAG_EFE;
@@ -185,33 +206,24 @@ int udf_set_version(struct udf_disc *disc, int udf_rev)
memcpy(disc->udf_iuvd[0]->impIdent.identSuffix, &udf_rev_le16, sizeof(udf_rev_le16));
memcpy(disc->udf_stable[0]->sparingIdent.identSuffix, &udf_rev_le16, sizeof(udf_rev_le16));
lvidiu = query_lvidiu(disc);
- if (udf_rev == 0x0260)
- lvidiu->minUDFReadRev = cpu_to_le16(0x0250);
+ if (udf_rev < 0x0102)
+ {
+ /* The ImplementationUse area for the Logical Volume Integrity Descriptor
+ * prior to UDF 1.02 does not define UDF revisions, so clear these fields */
+ lvidiu->minUDFReadRev = cpu_to_le16(0);
+ lvidiu->minUDFWriteRev = cpu_to_le16(0);
+ lvidiu->maxUDFWriteRev = cpu_to_le16(0);
+ }
else
- lvidiu->minUDFReadRev = cpu_to_le16(udf_rev);
- lvidiu->minUDFWriteRev = cpu_to_le16(udf_rev);
- lvidiu->maxUDFWriteRev = cpu_to_le16(udf_rev);
- return 0;
-}
-
-void get_random_bytes(void *buffer, size_t count)
-{
- int fd;
- size_t i;
-
- fd = open("/dev/urandom", O_RDONLY);
- if (fd >= 0)
{
- if (read(fd, buffer, count) == (ssize_t)count)
- {
- close(fd);
- return;
- }
- close(fd);
+ if (udf_rev == 0x0260)
+ lvidiu->minUDFReadRev = cpu_to_le16(0x0250);
+ else
+ lvidiu->minUDFReadRev = cpu_to_le16(udf_rev);
+ lvidiu->minUDFWriteRev = cpu_to_le16(udf_rev);
+ lvidiu->maxUDFWriteRev = cpu_to_le16(udf_rev);
}
-
- for (i = 0; i < count; ++i)
- ((uint8_t *)buffer)[i] = rand() % 0xFF;
+ return 0;
}
void split_space(struct udf_disc *disc)
@@ -276,9 +288,12 @@ void split_space(struct udf_disc *disc)
}
accessType = le32_to_cpu(disc->udf_pd[0]->accessType);
- if ((accessType == PD_ACCESS_TYPE_OVERWRITABLE || accessType == PD_ACCESS_TYPE_REWRITABLE) && sizes[LVID_SIZE] * (size_t)disc->blocksize < 8192)
+ if ((accessType == PD_ACCESS_TYPE_OVERWRITABLE || accessType == PD_ACCESS_TYPE_REWRITABLE) && (uint64_t)sizes[LVID_SIZE] * disc->blocksize < 8192 && blocks > 257)
sizes[LVID_SIZE] = (8192 + disc->blocksize-1) / disc->blocksize;
+ if (sizes[VDS_SIZE] > 6 && blocks <= 257)
+ sizes[VDS_SIZE] = 6;
+
if (!(disc->flags & FLAG_VAT) && blocks < 770)
start = 0;
else
@@ -301,7 +316,7 @@ void split_space(struct udf_disc *disc)
}
set_extent(disc, RVDS, start, sizes[VDS_SIZE]);
}
- else
+ else if (blocks > 257)
{
if (blocks >= 3072)
start = find_next_extent_size(disc, (blocks-257+97)/32*32, USPACE, sizes[VDS_SIZE], offsets[VDS_SIZE]);
@@ -502,7 +517,7 @@ static void fill_mbr(struct udf_disc *disc, struct mbr *mbr, uint32_t start)
}
if (!mbr->disk_signature)
- get_random_bytes(&mbr->disk_signature, sizeof(mbr->disk_signature));
+ mbr->disk_signature = le32_to_cpu(randu32());
lba_blocks = ((uint64_t)disc->blocks * disc->blocksize + disc->blkssz - 1) / disc->blkssz;
@@ -608,13 +623,21 @@ void setup_anchor(struct udf_disc *disc)
mlen = ext->blocks * disc->blocksize;
ext = next_extent(disc->head, RVDS);
- if (!ext)
+ if (!ext && disc->blocks > 257)
{
fprintf(stderr, "%s: Error: Not enough blocks on device\n", appname);
exit(1);
}
- rloc = ext->start;
- rlen = ext->blocks * disc->blocksize;
+ if (disc->blocks > 257)
+ {
+ rloc = ext->start;
+ rlen = ext->blocks * disc->blocksize;
+ }
+ else
+ {
+ rloc = mloc;
+ rlen = mlen;
+ }
ext = next_extent(disc->head, ANCHOR);
if (!ext)
@@ -720,7 +743,7 @@ int setup_space(struct udf_disc *disc, struct udf_extent *pspace, uint32_t offse
if (pspace->blocks%8)
sbd->bitmap[nBytes-1] = 0xFF >> (8-(pspace->blocks%8));
clear_bits(sbd->bitmap, offset, (length + disc->blocksize - 1) / disc->blocksize);
- sbd->descTag = udf_query_tag(disc, TAG_IDENT_SBD, 1, desc->offset, desc->data, sizeof(struct spaceBitmapDesc));
+ sbd->descTag = udf_query_tag(disc, TAG_IDENT_SBD, 1, desc->offset, desc->data, 0, sizeof(struct spaceBitmapDesc));
}
else if (disc->flags & FLAG_SPACE_TABLE)
{
@@ -776,7 +799,7 @@ int setup_space(struct udf_disc *disc, struct udf_extent *pspace, uint32_t offse
use->icbTag.parentICBLocation.partitionReferenceNum = cpu_to_le16(0);
use->icbTag.fileType = ICBTAG_FILE_TYPE_USE;
use->icbTag.flags = cpu_to_le16(ICBTAG_FLAG_AD_SHORT);
- use->descTag = udf_query_tag(disc, TAG_IDENT_USE, 1, desc->offset, desc->data, sizeof(struct unallocSpaceEntry) + le32_to_cpu(use->lengthAllocDescs));
+ use->descTag = udf_query_tag(disc, TAG_IDENT_USE, 1, desc->offset, desc->data, 0, sizeof(struct unallocSpaceEntry) + le32_to_cpu(use->lengthAllocDescs));
if (disc->flags & FLAG_STRATEGY4096)
{
@@ -971,7 +994,7 @@ void setup_vds(struct udf_disc *disc)
stable[0] = next_extent(disc->head, STABLE);
sspace = next_extent(disc->head, SSPACE);
- if (!mvds || !rvds || !lvid)
+ if (!mvds || (!rvds && disc->blocks > 257) || !lvid)
{
fprintf(stderr, "%s: Error: Not enough blocks on device\n", appname);
exit(1);
@@ -993,8 +1016,7 @@ void setup_vds(struct udf_disc *disc)
setup_pd(disc, mvds, rvds, 2);
setup_usd(disc, mvds, rvds, 3);
setup_iuvd(disc, mvds, rvds, 4);
- if (mvds->blocks > 5)
- setup_td(disc, mvds, rvds, 5);
+ setup_td(disc, mvds, rvds, 5);
}
void setup_pvd(struct udf_disc *disc, struct udf_extent *mvds, struct udf_extent *rvds, uint32_t offset)
@@ -1007,6 +1029,8 @@ void setup_pvd(struct udf_disc *disc, struct udf_extent *mvds, struct udf_extent
desc->data->buffer = disc->udf_pvd[0];
disc->udf_pvd[0]->descTag = query_tag(disc, mvds, desc, 1);
+ if (!rvds)
+ return;
desc = set_desc(rvds, TAG_IDENT_PVD, offset, length, NULL);
memcpy(disc->udf_pvd[1] = desc->data->buffer, disc->udf_pvd[0], length);
disc->udf_pvd[1]->descTag = query_tag(disc, rvds, desc, 1);
@@ -1026,6 +1050,8 @@ void setup_lvd(struct udf_disc *disc, struct udf_extent *mvds, struct udf_extent
desc->data->buffer = disc->udf_lvd[0];
disc->udf_lvd[0]->descTag = query_tag(disc, mvds, desc, 1);
+ if (!rvds)
+ return;
desc = set_desc(rvds, TAG_IDENT_LVD, offset, length, NULL);
memcpy(disc->udf_lvd[1] = desc->data->buffer, disc->udf_lvd[0], length);
disc->udf_lvd[1]->descTag = query_tag(disc, rvds, desc, 1);
@@ -1057,6 +1083,8 @@ void setup_pd(struct udf_disc *disc, struct udf_extent *mvds, struct udf_extent
desc->data->buffer = disc->udf_pd[0];
disc->udf_pd[0]->descTag = query_tag(disc, mvds, desc, 1);
+ if (!rvds)
+ return;
desc = set_desc(rvds, TAG_IDENT_PD, offset, length, NULL);
memcpy(disc->udf_pd[1] = desc->data->buffer, disc->udf_pd[0], length);
disc->udf_pd[1]->descTag = query_tag(disc, rvds, desc, 1);
@@ -1086,6 +1114,8 @@ void setup_usd(struct udf_disc *disc, struct udf_extent *mvds, struct udf_extent
desc->data->buffer = disc->udf_usd[0];
disc->udf_usd[0]->descTag = query_tag(disc, mvds, desc, 1);
+ if (!rvds)
+ return;
desc = set_desc(rvds, TAG_IDENT_USD, offset, length, NULL);
memcpy(disc->udf_usd[1] = desc->data->buffer, disc->udf_usd[0], length);
disc->udf_usd[1]->descTag = query_tag(disc, rvds, desc, 1);
@@ -1103,6 +1133,8 @@ void setup_iuvd(struct udf_disc *disc, struct udf_extent *mvds, struct udf_exten
desc->data->buffer = disc->udf_iuvd[0];
disc->udf_iuvd[0]->descTag = query_tag(disc, mvds, desc, 1);
+ if (!rvds)
+ return;
desc = set_desc(rvds, TAG_IDENT_IUVD, offset, length, NULL);
memcpy(disc->udf_iuvd[1] = desc->data->buffer, disc->udf_iuvd[0], length);
disc->udf_iuvd[1]->descTag = query_tag(disc, rvds, desc, 1);
@@ -1118,6 +1150,8 @@ void setup_td(struct udf_disc *disc, struct udf_extent *mvds, struct udf_extent
desc->data->buffer = disc->udf_td[0];
disc->udf_td[0]->descTag = query_tag(disc, mvds, desc, 1);
+ if (!rvds)
+ return;
desc = set_desc(rvds, TAG_IDENT_TD, offset, length, NULL);
memcpy(disc->udf_td[1] = desc->data->buffer, disc->udf_td[0], length);
disc->udf_td[1]->descTag = query_tag(disc, rvds, desc, 1);
@@ -1211,21 +1245,32 @@ void setup_stable(struct udf_disc *disc, struct udf_extent *stable[4], struct ud
void setup_vat(struct udf_disc *disc, struct udf_extent *pspace)
{
- uint32_t offset = 0;
+ uint32_t offset;
struct udf_extent *anchor;
struct udf_desc *vtable;
struct udf_data *data;
- uint32_t len;
+ uint32_t len, i;
struct virtualAllocationTable15 *vat15;
struct virtualAllocationTable20 *vat20;
+ struct impUseExtAttr *ea_attr;
+ struct LVExtensionEA *ea_lv;
+ uint8_t buffer[(sizeof(*ea_attr)+sizeof(*ea_lv)+3)/4*4];
+ uint16_t checksum;
uint16_t udf_rev_le16;
+ uint32_t min_blocks;
+ uint32_t align;
+
+ /* Put VAT to the last sector correctly aligned */
+ align = disc->sizing[PSPACE_SIZE].align;
+ offset = pspace->tail->offset + (pspace->tail->length + disc->blocksize-1) / disc->blocksize;
+ offset = (offset + align) / align * align - 1;
if (disc->flags & FLAG_MIN_300_BLOCKS)
{
- // On optical discs one track has minimal size of 300 sectors, so put VAT to the last sector
- offset = pspace->tail->offset + (pspace->tail->length + disc->blocksize-1) / disc->blocksize;
- if (pspace->start + offset < 299)
- offset = 299 - pspace->start;
+ // On optical TAO discs one track has minimal size of 300 sectors
+ min_blocks = (300 + align) / align * align - 1;
+ if (pspace->start + offset < min_blocks)
+ offset = min_blocks - pspace->start;
}
if (disc->flags & FLAG_CLOSED)
@@ -1260,12 +1305,41 @@ void setup_vat(struct udf_disc *disc, struct udf_extent *pspace)
{
vtable = udf_create(disc, pspace, (const dchars *)"\x08" UDF_ID_ALLOC, strlen(UDF_ID_ALLOC)+1, offset, NULL, FID_FILE_CHAR_HIDDEN, ICBTAG_FILE_TYPE_UNDEF, 0);
disc->vat_entries--; // Remove VAT file itself from VAT table
+ udf_rev_le16 = cpu_to_le16(disc->udf_rev);
+ memset(&buffer, 0, sizeof(buffer));
+ ea_attr = (struct impUseExtAttr *)buffer;
+ ea_attr->attrType = cpu_to_le32(EXTATTR_IMP_USE);
+ ea_attr->attrSubtype = EXTATTR_SUBTYPE;
+ ea_attr->attrLength = cpu_to_le32(sizeof(buffer));
+ ea_attr->impUseLength = cpu_to_le32(sizeof(*ea_lv));
+ ea_attr->impIdent.identSuffix[2] = UDF_OS_CLASS_UNIX;
+ ea_attr->impIdent.identSuffix[3] = UDF_OS_ID_LINUX;
+ memcpy(ea_attr->impIdent.identSuffix, &udf_rev_le16, sizeof(udf_rev_le16));
+ strcpy((char *)ea_attr->impIdent.ident, UDF_ID_VAT_LVEXTENSION);
+ ea_lv = (struct LVExtensionEA *)&ea_attr->impUse[0];
+ checksum = 0;
+ for (i = 0; i < sizeof(*ea_attr); ++i)
+ checksum += ((uint8_t *)ea_attr)[i];
+ ea_lv->headerChecksum = cpu_to_le16(checksum);
+ if (disc->flags & FLAG_EFE)
+ {
+ struct extendedFileEntry *efe = (struct extendedFileEntry *)vtable->data->buffer;
+ ea_lv->verificationID = efe->uniqueID;
+ }
+ else
+ {
+ struct fileEntry *fe = (struct fileEntry *)vtable->data->buffer;
+ ea_lv->verificationID = fe->uniqueID;
+ }
+ ea_lv->numFiles = query_lvidiu(disc)->numFiles;
+ ea_lv->numDirs = query_lvidiu(disc)->numDirs;
+ memcpy(ea_lv->logicalVolIdent, disc->udf_lvd[0]->logicalVolIdent, 128);
+ insert_ea(disc, vtable, (struct genericFormat *)buffer, sizeof(buffer));
len = sizeof(struct virtualAllocationTable15);
data = alloc_data(disc->vat, disc->vat_entries * sizeof(uint32_t));
insert_data(disc, pspace, vtable, data);
data = alloc_data(&default_vat15, len);
vat15 = data->buffer;
- udf_rev_le16 = cpu_to_le16(disc->udf_rev);
memcpy(vat15->vatIdent.identSuffix, &udf_rev_le16, sizeof(udf_rev_le16));
insert_data(disc, pspace, vtable, data);
}