diff options
Diffstat (limited to 'udfinfo/readdisc.c')
-rw-r--r-- | udfinfo/readdisc.c | 635 |
1 files changed, 500 insertions, 135 deletions
diff --git a/udfinfo/readdisc.c b/udfinfo/readdisc.c index 076723d..a82f383 100644 --- a/udfinfo/readdisc.c +++ b/udfinfo/readdisc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Pali Rohár <pali.rohar@gmail.com> + * Copyright (C) 2017-2018 Pali Rohár <pali.rohar@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,12 +34,12 @@ #include "libudffs.h" #include "readdisc.h" -static int read_offset(int fd, struct udf_disc *disc, void *buf, size_t offset, size_t count, int warn_beyond) +static int read_offset(int fd, struct udf_disc *disc, void *buf, off_t offset, size_t count, int warn_beyond) { off_t off; ssize_t ret; - if (offset + count > (size_t)disc->blocks * disc->blocksize) + if (offset + (off_t)count > (off_t)disc->blocks * disc->blocksize) { if (warn_beyond) fprintf(stderr, "%s: Warning: Trying to read beyond end of disk\n", appname); @@ -47,7 +47,7 @@ static int read_offset(int fd, struct udf_disc *disc, void *buf, size_t offset, } off = lseek(fd, offset, SEEK_SET); - if (off != (off_t)-1 && (size_t)off != offset) + if (off != (off_t)-1 && off != offset) { errno = EIO; off = (off_t)-1; @@ -201,7 +201,7 @@ static int read_anchor_i(int fd, struct udf_disc *disc, int i, uint32_t location struct anchorVolDescPtr avdp; struct udf_extent *ext; - if (read_offset(fd, disc, &avdp, (size_t)location * disc->blocksize, sizeof(avdp), 1) < 0) + if (read_offset(fd, disc, &avdp, (off_t)location * disc->blocksize, sizeof(avdp), 1) < 0) return -2; if (le32_to_cpu(avdp.descTag.tagLocation) != location) @@ -233,14 +233,14 @@ static int read_anchor_second(int fd, struct udf_disc *disc) { int ret1, ret2; - if (disc->blocks > 257 && (size_t)(disc->blocks - 257) * disc->blocksize > (size_t)32768 + disc->blocksize && disc->blocks - 257 != 256) + if (disc->blocks > 257 && (off_t)(disc->blocks - 257) * disc->blocksize > (off_t)32768 + disc->blocksize && disc->blocks - 257 != 256) ret1 = read_anchor_i(fd, disc, 1, disc->blocks - 257); else ret1 = -2; if (ret1 == -1) return -1; - if ((size_t)(disc->blocks - 1) * disc->blocksize > (size_t)32768 + disc->blocksize && disc->blocks - 1 != 256) + if ((off_t)(disc->blocks - 1) * disc->blocksize > (off_t)32768 + disc->blocksize && disc->blocks - 1 != 256) ret2 = read_anchor_i(fd, disc, 2, disc->blocks - 1); else ret2 = -2; @@ -505,10 +505,11 @@ static int choose_anchor(struct udf_disc *disc) static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) { uint32_t location, length, count, i; - uint32_t next_vdp_num, next_location, next_length, next_count; + uint32_t next_location, next_length, next_count; uint16_t type, udf_rev_le16; size_t gd_length; struct genericDesc *gd_ptr; + struct partitionDesc *pd_ptr; struct udf_extent *ext; struct volDescPtr *vdp; struct logicalVolDesc *lvd; @@ -516,6 +517,7 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) unsigned char buffer[512]; int id, anchor; int nested; + int done; anchor = choose_anchor(disc); if (anchor == -1) @@ -532,6 +534,18 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) next_location = le32_to_cpu(disc->udf_anchor[anchor]->reserveVolDescSeqExt.extLocation); next_length = le32_to_cpu(disc->udf_anchor[anchor]->reserveVolDescSeqExt.extLength) & EXT_LENGTH_MASK; id = 1; + if (next_location == le32_to_cpu(disc->udf_anchor[anchor]->mainVolDescSeqExt.extLocation)) + { + fprintf(stderr, "%s: Warning: Reserve Volume Descriptor Sequence is on same location as Main\n", appname); + disc->udf_pvd[1] = disc->udf_pvd[0]; + disc->udf_lvd[1] = disc->udf_lvd[0]; + disc->udf_pd[1] = disc->udf_pd[0]; + disc->udf_pd2[1] = disc->udf_pd2[0]; + disc->udf_usd[1] = disc->udf_usd[0]; + disc->udf_iuvd[1] = disc->udf_iuvd[0]; + disc->udf_td[1] = disc->udf_td[0]; + return 0; + } } else { @@ -545,7 +559,6 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) return -2; nested = 0; - next_vdp_num = 0; while (next_location && next_count) { @@ -567,6 +580,8 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) if (count > 256) length = 256 * disc->blocksize; + done = 0; + for (i = 0; i < count; ++i) { if (count >= 256) @@ -575,7 +590,7 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) break; } - if (read_offset(fd, disc, &buffer, ((size_t)location+i) * disc->blocksize, sizeof(buffer), 1) < 0) + if (read_offset(fd, disc, &buffer, ((off_t)location+i) * disc->blocksize, sizeof(buffer), 1) < 0) return -3; gd_ptr = (struct genericDesc *)&buffer; @@ -606,19 +621,44 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) memcpy(gd_ptr, &buffer, sizeof(buffer)); set_desc(ext, type, i, sizeof(buffer), alloc_data(gd_ptr, sizeof(buffer))); - if (type == TAG_IDENT_PVD && (!disc->udf_pvd[id] || le32_to_cpu(disc->udf_pvd[id]->volDescSeqNum) < le32_to_cpu(gd_ptr->volDescSeqNum))) - disc->udf_pvd[id] = (struct primaryVolDesc *)gd_ptr; - else if (type == TAG_IDENT_PD && (!disc->udf_pd[id] || le32_to_cpu(disc->udf_pd[id]->volDescSeqNum) < le32_to_cpu(gd_ptr->volDescSeqNum))) - disc->udf_pd[id] = (struct partitionDesc *)gd_ptr; - else if (type == TAG_IDENT_IUVD && (!disc->udf_iuvd[id] || le32_to_cpu(disc->udf_iuvd[id]->volDescSeqNum) < le32_to_cpu(gd_ptr->volDescSeqNum))) - disc->udf_iuvd[id] = (struct impUseVolDesc *)gd_ptr; - else if (type == TAG_IDENT_TD && !disc->udf_td[id]) - disc->udf_td[id] = (struct terminatingDesc *)gd_ptr; - else if (type == TAG_IDENT_VDP) + switch (type) { - vdp = (struct volDescPtr *)gd_ptr; - if (next_vdp_num < le32_to_cpu(vdp->volDescSeqNum)) - { + case TAG_IDENT_PVD: + if (!disc->udf_pvd[id] || le32_to_cpu(disc->udf_pvd[id]->volDescSeqNum) < le32_to_cpu(gd_ptr->volDescSeqNum)) + disc->udf_pvd[id] = (struct primaryVolDesc *)gd_ptr; + break; + + case TAG_IDENT_PD: + pd_ptr = (struct partitionDesc *)gd_ptr; + if (!disc->udf_pd[id] || le16_to_cpu(disc->udf_pd[id]->partitionNumber) == le16_to_cpu(pd_ptr->partitionNumber)) + { + if (!disc->udf_pd[id] || le32_to_cpu(disc->udf_pd[id]->volDescSeqNum) < le32_to_cpu(pd_ptr->volDescSeqNum)) + disc->udf_pd[id] = pd_ptr; + } + else if (!disc->udf_pd2[id] || le16_to_cpu(disc->udf_pd2[id]->partitionNumber) == le16_to_cpu(pd_ptr->partitionNumber)) + { + if (!disc->udf_pd2[id] || le32_to_cpu(disc->udf_pd2[id]->volDescSeqNum) < le32_to_cpu(pd_ptr->volDescSeqNum)) + disc->udf_pd2[id] = pd_ptr; + } + else + { + fprintf(stderr, "%s: Warning: More then two Partition Descriptors are present, ignoring others\n", appname); + } + break; + + case TAG_IDENT_IUVD: + if (!disc->udf_iuvd[id] || le32_to_cpu(disc->udf_iuvd[id]->volDescSeqNum) < le32_to_cpu(gd_ptr->volDescSeqNum)) + disc->udf_iuvd[id] = (struct impUseVolDesc *)gd_ptr; + break; + + case TAG_IDENT_TD: + if (!disc->udf_td[id]) + disc->udf_td[id] = (struct terminatingDesc *)gd_ptr; + done = 1; + break; + + case TAG_IDENT_VDP: + vdp = (struct volDescPtr *)gd_ptr; next_location = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); if (next_location <= location) { @@ -627,14 +667,16 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) } else { - next_vdp_num = le32_to_cpu(vdp->volDescSeqNum); next_length = le32_to_cpu(vdp->nextVolDescSeqExt.extLength) & EXT_LENGTH_MASK; next_count = next_length / disc->blocksize; } - } + done = 1; + break; + + default: + fprintf(stderr, "%s: Warning: Unknown descriptor in Volume Descriptor Sequence\n", appname); + break; } - else - fprintf(stderr, "%s: Warning: Unknown descriptor in Volume Descriptor Sequence\n", appname); break; case TAG_IDENT_LVD: @@ -733,7 +775,7 @@ static int scan_vds(int fd, struct udf_disc *disc, enum udf_space_type vds_type) break; } - if (type == TAG_IDENT_TD) + if (done) break; } } @@ -796,7 +838,7 @@ static void scan_lvis(int fd, struct udf_disc *disc) break; } - if (read_offset(fd, disc, &buffer, (size_t)location * disc->blocksize, sizeof(buffer), 1) < 0) + if (read_offset(fd, disc, &buffer, (off_t)location * disc->blocksize, sizeof(buffer), 1) < 0) return; descTag = (tag *)&buffer; @@ -907,10 +949,15 @@ static void parse_lvidiu(struct udf_disc *disc) } lvidiu = (struct logicalVolIntegrityDescImpUse *)&(disc->udf_lvid->impUse[le32_to_cpu(disc->udf_lvid->numOfPartitions) * 2 * sizeof(uint32_t)]); - disc->udf_rev = le16_to_cpu(lvidiu->minUDFReadRev); - disc->udf_write_rev = le16_to_cpu(lvidiu->minUDFWriteRev); disc->num_files = le32_to_cpu(lvidiu->numFiles); disc->num_dirs = le32_to_cpu(lvidiu->numDirs); + + /* Fields minUDFReadRev and minUDFWriteRev are defined since UDF 1.02 */ + if (disc->udf_rev >= 0x0102) + { + disc->udf_rev = le16_to_cpu(lvidiu->minUDFReadRev); + disc->udf_write_rev = le16_to_cpu(lvidiu->minUDFWriteRev); + } } static struct genericPartitionMap *find_partition(struct udf_disc *disc, uint8_t type, const char *ident) @@ -971,25 +1018,61 @@ static struct genericPartitionMap *get_partition(struct udf_disc *disc, int id, return pmap; } -static uint32_t find_block_position(struct udf_disc *disc, struct genericPartitionMap *pmap, uint32_t block) +static struct partitionDesc *find_partition_descriptor(struct udf_disc *disc, uint16_t partition) +{ + int id; + + if (disc->udf_pd[0]) + id = 0; + else if (disc->udf_pd[1]) + id = 1; + else + return NULL; + + if (le16_to_cpu(disc->udf_pd[id]->partitionNumber) == partition) + return disc->udf_pd[id]; + + if (disc->udf_pd2[0]) + id = 0; + else if (disc->udf_pd2[1]) + id = 1; + else + return NULL; + + if (le16_to_cpu(disc->udf_pd2[id]->partitionNumber) == partition) + return disc->udf_pd2[id]; + + return NULL; +} + +static uint32_t find_block_position(struct udf_disc *disc, struct genericPartitionMap *pmap, uint32_t block, uint16_t *partition) { + struct genericPartitionMap1 *pm1; struct udfPartitionMap2 *upm2; + struct virtualPartitionMap *vpm; struct sparablePartitionMap *spm; uint8_t count, i; uint16_t packet_len, num, j; uint32_t location, packet, offset; if (pmap->partitionMapType == GP_PARTITION_MAP_TYPE_1) + { + pm1 = (struct genericPartitionMap1 *)pmap; + *partition = le16_to_cpu(pm1->partitionNum); return block; + } else if (pmap->partitionMapType == GP_PARTITION_MAP_TYPE_2) { upm2 = (struct udfPartitionMap2 *)pmap; if (strncmp((char *)upm2->partIdent.ident, UDF_ID_VIRTUAL, sizeof(upm2->partIdent.ident)) == 0) { + vpm = (struct virtualPartitionMap *)upm2; + *partition = le16_to_cpu(vpm->partitionNum); + if (!disc->vat) return UINT32_MAX; else if (block < disc->vat_entries) - return disc->vat[block]; + return le32_to_cpu(disc->vat[block]); else return block; } @@ -998,9 +1081,10 @@ static uint32_t find_block_position(struct udf_disc *disc, struct genericPartiti spm = (struct sparablePartitionMap *)upm2; count = spm->numSparingTables; packet_len = le16_to_cpu(spm->packetLength); + *partition = le16_to_cpu(spm->partitionNum); - packet = block & ~(packet_len-1); - offset = block & (packet_len-1); + offset = block % packet_len; + packet = block - offset; for (i = 0; i < count; ++i) { @@ -1070,7 +1154,7 @@ static void read_stable(int fd, struct udf_disc *disc) { location = le32_to_cpu(spm->locSparingTable[i]); - if (read_offset(fd, disc, &buffer, (size_t)location * disc->blocksize, sizeof(buffer), 1) < 0) + if (read_offset(fd, disc, &buffer, (off_t)location * disc->blocksize, sizeof(buffer), 1) < 0) return; st = (struct sparingTable *)&buffer; @@ -1135,28 +1219,47 @@ static void read_stable(int fd, struct udf_disc *disc) static void read_vat(int fd, struct udf_disc *disc) { long last; + struct partitionDesc *pd; + struct virtualPartitionMap *vpm; + long_ad *lad; + short_ad *sad; + uint32_t j, count; + uint32_t ext_length, ext_position, ext_location; + uint16_t ext_partition; + uint64_t vat_length, vat_offset; uint32_t i, vat_block; uint32_t length, offset, location; + uint32_t ea_length, ea_offset; + uint32_t ea_attr_length, ea_attr_offset; + uint64_t unique_id; struct stat st; struct fileEntry *fe; struct extendedFileEntry *efe; struct virtualAllocationTable15 *vat15; struct virtualAllocationTable20 *vat20; + struct extendedAttrHeaderDesc ea_hdr; + struct impUseExtAttr ea_attr; + struct LVExtensionEA ea_lv; unsigned char *vat; + unsigned char *descs; unsigned char buffer[512]; - int id; - if (disc->udf_pd[0]) - id = 0; - else if (disc->udf_pd[1]) - id = 1; - else + vpm = (struct virtualPartitionMap *)find_partition(disc, GP_PARTITION_MAP_TYPE_2, UDF_ID_VIRTUAL); + if (!vpm) + { + if (disc->vat_block) + fprintf(stderr, "%s: Error: Virtual Partition Map not found, but --vatblock specified\n", appname); return; + } - location = le32_to_cpu(disc->udf_pd[id]->partitionStartingLocation); - - if (!find_partition(disc, GP_PARTITION_MAP_TYPE_2, UDF_ID_VIRTUAL)) + pd = find_partition_descriptor(disc, le16_to_cpu(vpm->partitionNum)); + if (!pd) + { + fprintf(stderr, "%s: Error: Virtual Partition Map found, but corresponding Partition Descriptor not found\n", appname); return; + } + + location = le32_to_cpu(pd->partitionStartingLocation); if (disc->vat_block) vat_block = disc->vat_block; @@ -1167,7 +1270,7 @@ static void read_vat(int fd, struct udf_disc *disc) for (i = vat_block + 3; i > 0 && i > vat_block - 32; --i) { - if (read_offset(fd, disc, &buffer, (size_t)i * disc->blocksize, sizeof(buffer), 0) < 0) + if (read_offset(fd, disc, &buffer, (off_t)i * disc->blocksize, sizeof(buffer), 0) < 0) continue; fe = (struct fileEntry *)&buffer; @@ -1184,62 +1287,203 @@ static void read_vat(int fd, struct udf_disc *disc) continue; } - if ((le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK) != ICBTAG_FLAG_AD_IN_ICB) - { - fprintf(stderr, "%s: Warning: Reading Virtual Allocation Table outside of Information Control Block is not supported yet\n", appname); - break; - } - if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE) { - offset = sizeof(*fe) + le32_to_cpu(fe->lengthExtendedAttr); + ea_offset = sizeof(*fe); + ea_length = le32_to_cpu(fe->lengthExtendedAttr); + offset = ea_offset + ea_length; length = le32_to_cpu(fe->lengthAllocDescs); + unique_id = le64_to_cpu(fe->uniqueID); } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE) { efe = (struct extendedFileEntry *)&buffer; - offset = sizeof(*efe) + le32_to_cpu(efe->lengthExtendedAttr); + ea_offset = sizeof(*efe); + ea_length = le32_to_cpu(efe->lengthExtendedAttr); + offset = ea_offset + ea_length; length = le32_to_cpu(efe->lengthAllocDescs); + unique_id = le64_to_cpu(efe->uniqueID); } else continue; - if (le64_to_cpu(fe->informationLength) > length) + if (length == 0) { - fprintf(stderr, "%s: Warning: Virtual Allocation Table inside of Information Control Block is larger then allocated block\n", appname); + fprintf(stderr, "%s: Warning: Information Control Block for Virtual Allocation Table is empty\n", appname); break; } - length = le64_to_cpu(fe->informationLength); - - if (length < 36) + if (ea_length > disc->blocksize || offset > disc->blocksize || length > disc->blocksize || offset + length > disc->blocksize) { - fprintf(stderr, "%s: Warning: Virtual Allocation Table is too small\n", appname); + fprintf(stderr, "%s: Warning: Information Control Block for Virtual Allocation Table is larger then block size\n", appname); break; } - if (offset+length > disc->blocksize) + descs = malloc(length); + if (!descs) { - fprintf(stderr, "%s: Warning: Virtual Allocation Table inside of Information Control Block is larger then block size\n", appname); + fprintf(stderr, "%s: Error: malloc failed: %s\n", appname, strerror(errno)); break; } - vat = malloc(length); - if (!vat) + if (read_offset(fd, disc, descs, (off_t)i * disc->blocksize + offset, length, 1) < 0) { - fprintf(stderr, "%s: Error: malloc failed: %s\n", appname, strerror(errno)); + free(descs); break; } - if (read_offset(fd, disc, vat, (size_t)i * disc->blocksize + offset, length, 1) < 0) + if ((le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK) == ICBTAG_FLAG_AD_IN_ICB) { - free(vat); - break; + if (le64_to_cpu(fe->informationLength) > length) + { + fprintf(stderr, "%s: Warning: Virtual Allocation Table inside of Information Control Block is larger then allocated block\n", appname); + free(descs); + break; + } + vat = descs; + vat_length = le64_to_cpu(fe->informationLength); + } + else + { + sad = NULL; + lad = NULL; + if ((le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK) == ICBTAG_FLAG_AD_SHORT) + { + sad = (short_ad *)descs; + count = length / sizeof(short_ad); + vat_length = 0; + for (j = 0; j < count; ++j) + { + if ((le32_to_cpu(sad[j].extLength) & EXT_LENGTH_MASK) >= UINT64_MAX - vat_length) + { + vat_length = UINT64_MAX; + break; + } + vat_length += le32_to_cpu(sad[j].extLength) & EXT_LENGTH_MASK; + } + } + else if ((le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK) == ICBTAG_FLAG_AD_LONG) + { + lad = (long_ad *)descs; + count = length / sizeof(long_ad); + vat_length = 0; + for (j = 0; j < count; ++j) + { + if ((le32_to_cpu(lad[j].extLength) & EXT_LENGTH_MASK) >= UINT64_MAX - vat_length) + { + vat_length = UINT64_MAX; + break; + } + vat_length += le32_to_cpu(lad[j].extLength) & EXT_LENGTH_MASK; + } + } + else + { + fprintf(stderr, "%s: Error: Information Control Block for Virtual Allocation Table has unknown Allocation Descriptors type\n", appname); + free(descs); + break; + } + + if (vat_length == 0) + { + fprintf(stderr, "%s: Warning: Virtual Allocation Table is empty\n", appname); + free(descs); + break; + } + else if (vat_length > (uint64_t)256 * disc->blocksize) + { + fprintf(stderr, "%s: Warning: Virtual Allocation Table is too big\n", appname); + free(descs); + break; + } + + /* Prefer non-virtual partition if exists */ + if (disc->udf_pd[0] && le16_to_cpu(disc->udf_pd[0]->partitionNumber) != le16_to_cpu(vpm->partitionNum)) + { + ext_location = le32_to_cpu(disc->udf_pd[0]->partitionStartingLocation); + ext_partition = le16_to_cpu(disc->udf_pd[0]->partitionNumber); + } + else if (disc->udf_pd[1] && le16_to_cpu(disc->udf_pd[1]->partitionNumber) != le16_to_cpu(vpm->partitionNum)) + { + ext_location = le32_to_cpu(disc->udf_pd[1]->partitionStartingLocation); + ext_partition = le16_to_cpu(disc->udf_pd[1]->partitionNumber); + } + else if (disc->udf_pd2[0] && le16_to_cpu(disc->udf_pd2[0]->partitionNumber) != le16_to_cpu(vpm->partitionNum)) + { + ext_location = le32_to_cpu(disc->udf_pd2[0]->partitionStartingLocation); + ext_partition = le16_to_cpu(disc->udf_pd2[0]->partitionNumber); + } + else if (disc->udf_pd2[1] && le16_to_cpu(disc->udf_pd2[1]->partitionNumber) != le16_to_cpu(vpm->partitionNum)) + { + ext_location = le32_to_cpu(disc->udf_pd2[1]->partitionStartingLocation); + ext_partition = le16_to_cpu(disc->udf_pd2[1]->partitionNumber); + } + else + { + ext_location = location; + ext_partition = le16_to_cpu(vpm->partitionNum); + } + + vat = malloc(vat_length); + if (!vat) + { + fprintf(stderr, "%s: Error: malloc failed: %s\n", appname, strerror(errno)); + free(descs); + break; + } + + vat_offset = 0; + for (j = 0; j < count; ++j) + { + if ((le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK) == ICBTAG_FLAG_AD_SHORT) + { + ext_length = le32_to_cpu(sad[j].extLength) & EXT_LENGTH_MASK; + ext_position = le32_to_cpu(sad[j].extPosition); + if (ext_length == 0) + continue; + } + else + { + ext_length = le32_to_cpu(lad[j].extLength) & EXT_LENGTH_MASK; + ext_position = le32_to_cpu(lad[j].extLocation.logicalBlockNum); + if (ext_length == 0) + continue; + if (le32_to_cpu(lad[j].extLocation.partitionReferenceNum) != ext_partition) + { + fprintf(stderr, "%s: Error: Virtual Allocation Table is stored on different partition\n", appname); + count = 0; + break; + } + } + + if (read_offset(fd, disc, vat + vat_offset, (off_t)(ext_location + ext_position) * disc->blocksize, ext_length, 1) != 0) + { + fprintf(stderr, "%s: Error: Virtual Allocation Table is damaged\n", appname); + count = 0; + break; + } + + vat_offset += ext_length; + } + + free(descs); + + if (count == 0) + { + free(vat); + break; + } } if (fe->icbTag.fileType == ICBTAG_FILE_TYPE_UNDEF) { - vat15 = (struct virtualAllocationTable15 *)(vat + ((length - 36) / 4) * 4); + if (vat_length < 36) + { + fprintf(stderr, "%s: Warning: Virtual Allocation Table is too small\n", appname); + free(vat); + break; + } + vat15 = (struct virtualAllocationTable15 *)(vat + ((vat_length - 36) / 4) * 4); if (strncmp((const char *)vat15->vatIdent.ident, UDF_ID_ALLOC, sizeof(vat15->vatIdent.ident)) != 0) { fprintf(stderr, "%s: Warning: Virtual Allocation Table is damaged\n", appname); @@ -1247,12 +1491,71 @@ static void read_vat(int fd, struct udf_disc *disc) break; } disc->vat = (uint32_t *)vat; - disc->vat_entries = (length - 36) / 4; + disc->vat_entries = (vat_length - 36) / 4; + if (ea_length) + { + if (sizeof(ea_hdr) > ea_length) + fprintf(stderr, "%s: Warning: Extended Attributes for Virtual Allocation Table are damaged\n", appname); + else if (read_offset(fd, disc, &ea_hdr, (off_t)i * disc->blocksize + ea_offset, sizeof(ea_hdr), 1) != 0) + fprintf(stderr, "%s: Warning: Extended Attributes for Virtual Allocation Table are damaged\n", appname); + else + { + /* UDF 1.50 3.3.4.1: if attribute does not exist then location point to byte after the EA space */ + ea_attr_offset = le32_to_cpu(ea_hdr.impAttrLocation); + while (ea_attr_offset < ea_length) + { + if (read_offset(fd, disc, &ea_attr, (off_t)i * disc->blocksize + ea_offset + ea_attr_offset, sizeof(ea_attr), 1) != 0) + { + fprintf(stderr, "%s: Warning: Extended Attributes for Virtual Allocation Table are damaged\n", appname); + break; + } + ea_attr_length = le32_to_cpu(ea_attr.attrLength); + if (ea_attr_length == 0) + break; + if (ea_attr_offset + ea_attr_length > ea_length || sizeof(ea_attr) + le32_to_cpu(ea_attr.impUseLength) > ea_attr_length) + { + fprintf(stderr, "%s: Warning: Extended Attributes for Virtual Allocation Table are damaged\n", appname); + break; + } + if (le32_to_cpu(ea_attr.attrType) == EXTATTR_IMP_USE && strncmp((const char *)ea_attr.impIdent.ident, UDF_ID_VAT_LVEXTENSION, sizeof(ea_attr.impIdent.ident)) == 0) + { + if (ea_attr_length < sizeof(ea_attr) + sizeof(ea_lv) || le32_to_cpu(ea_attr.impUseLength) < sizeof(ea_lv)) + { + fprintf(stderr, "%s: Warning: Logical Volume Extended Information for Virtual Allocation Table is damaged\n", appname); + break; + } + if (read_offset(fd, disc, &ea_lv, (off_t)i * disc->blocksize + ea_offset + ea_attr_offset + sizeof(ea_attr), sizeof(ea_lv), 1) != 0) + { + fprintf(stderr, "%s: Warning: Logical Volume Extended Information for Virtual Allocation Table is damaged\n", appname); + break; + } + if (le64_to_cpu(ea_lv.verificationID) != unique_id) + { + fprintf(stderr, "%s: Warning: Logical Volume Extended Information for Virtual Allocation Table is damaged\n", appname); + } + else + { + if (disc->udf_lvd[0]) + memcpy(disc->udf_lvd[0]->logicalVolIdent, ea_lv.logicalVolIdent, sizeof(ea_lv.logicalVolIdent)); + if (disc->udf_lvd[1]) + memcpy(disc->udf_lvd[1]->logicalVolIdent, ea_lv.logicalVolIdent, sizeof(ea_lv.logicalVolIdent)); + if (disc->udf_lvid) + { + disc->num_files = le32_to_cpu(ea_lv.numFiles); + disc->num_dirs = le32_to_cpu(ea_lv.numDirs); + } + break; + } + } + ea_attr_offset += ea_attr_length; + } + } + } } else if (fe->icbTag.fileType == ICBTAG_FILE_TYPE_VAT20) { vat20 = (struct virtualAllocationTable20 *)vat; - if (vat20->lengthHeader < sizeof(*vat20) || vat20->lengthHeader != sizeof(*vat20) + vat20->lengthImpUse || vat20->lengthHeader > length) + if (le16_to_cpu(vat20->lengthHeader) < sizeof(*vat20) || le16_to_cpu(vat20->lengthHeader) != sizeof(*vat20) + le16_to_cpu(vat20->lengthImpUse) || le16_to_cpu(vat20->lengthHeader) > vat_length) { fprintf(stderr, "%s: Warning: Virtual Allocation Table is damaged\n", appname); free(vat); @@ -1269,13 +1572,13 @@ static void read_vat(int fd, struct udf_disc *disc) disc->num_files = le32_to_cpu(vat20->numFiles); disc->num_dirs = le32_to_cpu(vat20->numDirs); } - disc->vat = (uint32_t *)(vat + vat20->lengthHeader); - disc->vat_entries = (length - vat20->lengthHeader) / 4; + disc->vat = (uint32_t *)(vat + le16_to_cpu(vat20->lengthHeader)); + disc->vat_entries = (vat_length - le16_to_cpu(vat20->lengthHeader)) / 4; } else { - free(vat); - continue; + fprintf(stderr, "%s: Error: Wrong file type\n", appname); + exit(1); } disc->vat_block = i; @@ -1292,40 +1595,53 @@ static void read_vat(int fd, struct udf_disc *disc) fprintf(stderr, "%s: Error: Virtual Allocation Table not found, maybe wrong --vatblock?\n", appname); } -static void setup_pspace(struct udf_disc *disc) +static void setup_pspace(struct udf_disc *disc, int second) { + struct partitionDesc *pd; struct udf_extent *ext, *new_ext; uint32_t location, blocks; - int id; - if (disc->udf_pd[0]) - id = 0; - else if (disc->udf_pd[1]) - id = 1; + if (!second) + { + if (disc->udf_pd[0]) + pd = disc->udf_pd[0]; + else if (disc->udf_pd[1]) + pd = disc->udf_pd[1]; + else + { + fprintf(stderr, "%s: Warning: Partition Space not found\n", appname); + return; + } + } else { - fprintf(stderr, "%s: Warning: Partition Space not found\n", appname); - return; + if (disc->udf_pd2[0]) + pd = disc->udf_pd2[0]; + else if (disc->udf_pd2[1]) + pd = disc->udf_pd2[1]; + else + return; } - location = le32_to_cpu(disc->udf_pd[id]->partitionStartingLocation); - blocks = le32_to_cpu(disc->udf_pd[id]->partitionLength); - if (!location || !blocks) + location = le32_to_cpu(pd->partitionStartingLocation); + + blocks = le32_to_cpu(pd->partitionLength); + if (!blocks) { - fprintf(stderr, "%s: Warning: Partition Space not found\n", appname); + fprintf(stderr, "%s: Warning: %sPartition Space not found\n", appname, second ? "Second " : ""); return; } if (location + blocks > disc->blocks && !find_partition(disc, GP_PARTITION_MAP_TYPE_2, UDF_ID_VIRTUAL)) - fprintf(stderr, "%s: Warning: Partition Space is beyond end of disk\n", appname); + fprintf(stderr, "%s: Warning: %sPartition Space is beyond end of disk\n", appname, second ? "Second " : ""); ext = find_extent(disc, location); if (ext->space_type != USPACE) { - fprintf(stderr, "%s: Warning: Partition Space overlaps with other blocks\n", appname); - ext = ext->next; - if (ext->space_type == USPACE) + fprintf(stderr, "%s: Warning: %sPartition Space overlaps with other blocks\n", appname, second ? "Second " : ""); + if (ext->next && ext->next->space_type == USPACE) { + ext = ext->next; if (blocks > ext->start - location && blocks - (ext->start - location) < ext->blocks) ext = set_extent(disc, PSPACE, ext->start, blocks - (ext->start - location)); else @@ -1336,14 +1652,20 @@ static void setup_pspace(struct udf_disc *disc) else { new_ext = malloc(sizeof(struct udf_extent)); + if (!new_ext) + { + fprintf(stderr, "%s: Error: malloc failed: %s\n", appname, strerror(errno)); + return; + } new_ext->space_type = PSPACE; new_ext->start = location; new_ext->blocks = blocks; new_ext->head = new_ext->tail = NULL; - new_ext->prev = ext->prev; - new_ext->next = ext; + new_ext->prev = ext; + new_ext->next = ext->next; new_ext->prev->next = new_ext; - new_ext->next->prev = new_ext; + if (new_ext->next) + new_ext->next->prev = new_ext; } } else @@ -1351,7 +1673,7 @@ static void setup_pspace(struct udf_disc *disc) if ((location == ext->start || (location > ext->start && location + blocks > ext->start + ext->blocks)) && blocks > ext->blocks) { if (ext != disc->tail) - fprintf(stderr, "%s: Warning: Partition Space overlaps with other blocks\n", appname); + fprintf(stderr, "%s: Warning: %sPartition Space overlaps with other blocks\n", appname, second ? "Second " : ""); ext = set_extent(disc, PSPACE, location, ext->blocks); ext->blocks = blocks; } @@ -1364,24 +1686,21 @@ static void read_fsd(int fd, struct udf_disc *disc) { long_ad *ad; uint16_t partition; - uint32_t block_num, location, position, length; + uint32_t block, location, position, length; struct genericPartitionMap *pmap; struct udf_extent *ext; + struct partitionDesc *pd; int id; - if (disc->udf_lvd[0] && disc->udf_pd[0]) - id = 0; - else if (disc->udf_lvd[1] && disc->udf_pd[1]) - id = 1; - else if (disc->udf_lvd[0] && disc->udf_pd[1]) + if (disc->udf_lvd[0]) id = 0; - else if (disc->udf_lvd[1] && disc->udf_pd[0]) + else if (disc->udf_lvd[1]) id = 1; else return; ad = (long_ad *)disc->udf_lvd[id]->logicalVolContentsUse; - block_num = le32_to_cpu(ad->extLocation.logicalBlockNum); + block = le32_to_cpu(ad->extLocation.logicalBlockNum); partition = le16_to_cpu(ad->extLocation.partitionReferenceNum); length = le32_to_cpu(ad->extLength) & EXT_LENGTH_MASK; @@ -1392,19 +1711,21 @@ static void read_fsd(int fd, struct udf_disc *disc) return; } - position = find_block_position(disc, pmap, block_num); + position = find_block_position(disc, pmap, block, &partition); if (position == UINT32_MAX) { fprintf(stderr, "%s: Warning: File Set Descriptor cannot be read\n", appname); return; } - if (id == 0 && !disc->udf_pd[0]) - id = 1; - else if (id == 1 && !disc->udf_pd[1]) - id = 0; + pd = find_partition_descriptor(disc, partition); + if (!pd) + { + fprintf(stderr, "%s: Warning: File Set Descriptor cannot be read\n", appname); + return; + } - location = le32_to_cpu(disc->udf_pd[id]->partitionStartingLocation) + position; + location = le32_to_cpu(pd->partitionStartingLocation) + position; if (sizeof(*disc->udf_fsd) > length) { @@ -1419,14 +1740,14 @@ static void read_fsd(int fd, struct udf_disc *disc) return; } - if (read_offset(fd, disc, disc->udf_fsd, (size_t)location * disc->blocksize, length, 1) < 0) + if (read_offset(fd, disc, disc->udf_fsd, (off_t)location * disc->blocksize, length, 1) < 0) { free(disc->udf_fsd); disc->udf_fsd = NULL; return; } - if (le32_to_cpu(disc->udf_fsd->descTag.tagLocation) != position) + if (le32_to_cpu(disc->udf_fsd->descTag.tagLocation) != block) { fprintf(stderr, "%s: Warning: Incorrect Logical Volume Integrity Descriptor\n", appname); free(disc->udf_fsd); @@ -1450,6 +1771,7 @@ static void read_fsd(int fd, struct udf_disc *disc) static void setup_total_space_blocks(struct udf_disc *disc) { int id; + int warn_beyond; if (disc->udf_pd[0]) id = 0; @@ -1463,14 +1785,35 @@ static void setup_total_space_blocks(struct udf_disc *disc) disc->total_space_blocks = le32_to_cpu(disc->udf_pd[id]->partitionLength); - if (disc->total_space_blocks + le32_to_cpu(disc->udf_pd[id]->partitionStartingLocation) > disc->blocks && !find_partition(disc, GP_PARTITION_MAP_TYPE_2, UDF_ID_VIRTUAL)) + warn_beyond = !find_partition(disc, GP_PARTITION_MAP_TYPE_2, UDF_ID_VIRTUAL); + + if (warn_beyond && disc->total_space_blocks + le32_to_cpu(disc->udf_pd[id]->partitionStartingLocation) > disc->blocks) + { fprintf(stderr, "%s: Warning: Some space blocks are beyond end of disk\n", appname); + warn_beyond = 0; + } + + if (disc->udf_pd2[0]) + id = 0; + else if (disc->udf_pd2[1]) + id = 1; + else + return; + + if (warn_beyond && le32_to_cpu(disc->udf_pd2[id]->partitionLength) + le32_to_cpu(disc->udf_pd2[id]->partitionStartingLocation) > disc->blocks) + fprintf(stderr, "%s: Warning: Some space blocks are beyond end of disk\n", appname); + + disc->total_space_blocks += le32_to_cpu(disc->udf_pd2[id]->partitionLength); } -static uint32_t count_bitmap_blocks(int fd, struct udf_disc *disc, uint32_t location, uint32_t length) +static uint32_t count_bitmap_blocks(int fd, struct udf_disc *disc, struct genericPartitionMap *pmap, uint32_t block, uint32_t length) { unsigned long int buffer[512/sizeof(unsigned long int)]; unsigned long int val; + uint32_t location; + uint32_t position; + uint16_t partition; + struct partitionDesc *pd; struct spaceBitmapDesc sbd; uint32_t bits; uint32_t bytes; @@ -1483,7 +1826,17 @@ static uint32_t count_bitmap_blocks(int fd, struct udf_disc *disc, uint32_t loca return 0; } - if (read_offset(fd, disc, &sbd, (size_t)location * disc->blocksize, sizeof(sbd), 1) < 0) + position = find_block_position(disc, pmap, block, &partition); + if (position == UINT32_MAX) + return 0; + + pd = find_partition_descriptor(disc, partition); + if (!pd) + return 0; + + location = le32_to_cpu(pd->partitionStartingLocation) + position; + + if (read_offset(fd, disc, &sbd, (off_t)location * disc->blocksize, sizeof(sbd), 1) < 0) return 0; bits = le32_to_cpu(sbd.numOfBits); @@ -1543,9 +1896,13 @@ static uint32_t count_bitmap_blocks(int fd, struct udf_disc *disc, uint32_t loca return blocks; } -static uint32_t count_table_blocks(int fd, struct udf_disc *disc, uint32_t location, uint32_t length) +static uint32_t count_table_blocks(int fd, struct udf_disc *disc, struct genericPartitionMap *pmap, uint32_t block, uint32_t length) { unsigned char buffer[512]; + uint32_t location; + uint32_t position; + uint16_t partition; + struct partitionDesc *pd; struct unallocSpaceEntry *use; size_t use_len; uint64_t space, blocks; @@ -1559,7 +1916,17 @@ static uint32_t count_table_blocks(int fd, struct udf_disc *disc, uint32_t locat return 0; } - if (read_offset(fd, disc, &buffer, (size_t)location * disc->blocksize, sizeof(buffer), 1) < 0) + position = find_block_position(disc, pmap, block, &partition); + if (position == UINT32_MAX) + return 0; + + pd = find_partition_descriptor(disc, partition); + if (!pd) + return 0; + + location = le32_to_cpu(pd->partitionStartingLocation) + position; + + if (read_offset(fd, disc, &buffer, (off_t)location * disc->blocksize, sizeof(buffer), 1) < 0) return 0; use = (struct unallocSpaceEntry *)&buffer; @@ -1642,9 +2009,10 @@ static void scan_free_space_blocks(int fd, struct udf_disc *disc) { long_ad *ad; uint16_t partition; - uint32_t blocks, position, location, length; + uint32_t blocks, location, length; struct genericPartitionMap *pmap; struct partitionHeaderDesc *phd; + struct partitionDesc *pd; char *ident; int id; @@ -1675,29 +2043,29 @@ static void scan_free_space_blocks(int fd, struct udf_disc *disc) if (!pmap) return; - if (find_block_position(disc, pmap, 0) == UINT32_MAX) + if (find_block_position(disc, pmap, 0, &partition) == UINT32_MAX) { fprintf(stderr, "%s: Warning: Determining free space blocks is not possible\n", appname); return; } - if (disc->udf_pd[0]) - id = 0; - else if (disc->udf_pd[1]) - id = 1; - else + pd = find_partition_descriptor(disc, partition); + if (!pd) + { + fprintf(stderr, "%s: Warning: Determining free space blocks is not possible\n", appname); return; + } - ident = (char *)disc->udf_pd[id]->partitionContents.ident; - length = sizeof(disc->udf_pd[id]->partitionContents.ident); + ident = (char *)pd->partitionContents.ident; + length = sizeof(pd->partitionContents.ident); if (strncmp(ident, PD_PARTITION_CONTENTS_NSR02, length) != 0 && strncmp(ident, PD_PARTITION_CONTENTS_NSR03, length) != 0) { fprintf(stderr, "%s: Warning: Unknown Partition Descriptor Content, determining free space blocks is not possible\n", appname); return; } - location = le32_to_cpu(disc->udf_pd[id]->partitionStartingLocation); - phd = (struct partitionHeaderDesc *)disc->udf_pd[id]->partitionContentsUse; + location = le32_to_cpu(pd->partitionStartingLocation); + phd = (struct partitionHeaderDesc *)pd->partitionContentsUse; if (disc->vat) { @@ -1708,8 +2076,7 @@ static void scan_free_space_blocks(int fd, struct udf_disc *disc) length = le32_to_cpu(phd->unallocSpaceBitmap.extLength) & EXT_LENGTH_MASK; if (length) { - position = find_block_position(disc, pmap, le32_to_cpu(phd->unallocSpaceBitmap.extPosition)); - blocks = count_bitmap_blocks(fd, disc, location + position, length); + blocks = count_bitmap_blocks(fd, disc, pmap, le32_to_cpu(phd->unallocSpaceBitmap.extPosition), length); if (blocks) { disc->free_space_blocks = blocks; @@ -1720,8 +2087,7 @@ static void scan_free_space_blocks(int fd, struct udf_disc *disc) length = le32_to_cpu(phd->freedSpaceBitmap.extLength) & EXT_LENGTH_MASK; if (length) { - position = find_block_position(disc, pmap, le32_to_cpu(phd->freedSpaceBitmap.extPosition)); - blocks = count_bitmap_blocks(fd, disc, location + position, length); + blocks = count_bitmap_blocks(fd, disc, pmap, le32_to_cpu(phd->freedSpaceBitmap.extPosition), length); if (blocks) { disc->free_space_blocks = blocks; @@ -1732,8 +2098,7 @@ static void scan_free_space_blocks(int fd, struct udf_disc *disc) length = le32_to_cpu(phd->unallocSpaceTable.extLength) & EXT_LENGTH_MASK; if (length) { - position = find_block_position(disc, pmap, le32_to_cpu(phd->unallocSpaceTable.extPosition)); - blocks = count_table_blocks(fd, disc, location + position, length); + blocks = count_table_blocks(fd, disc, pmap, le32_to_cpu(phd->unallocSpaceTable.extPosition), length); if (blocks) { disc->free_space_blocks = blocks; @@ -1744,8 +2109,7 @@ static void scan_free_space_blocks(int fd, struct udf_disc *disc) length = le32_to_cpu(phd->freedSpaceTable.extLength) & EXT_LENGTH_MASK; if (length) { - position = find_block_position(disc, pmap, le32_to_cpu(phd->freedSpaceTable.extPosition)); - blocks = count_table_blocks(fd, disc, location + position, length); + blocks = count_table_blocks(fd, disc, pmap, le32_to_cpu(phd->freedSpaceTable.extPosition), length); if (blocks) { disc->free_space_blocks = blocks; @@ -1791,7 +2155,8 @@ int read_disc(int fd, struct udf_disc *disc) parse_lvidiu(disc); read_stable(fd, disc); read_vat(fd, disc); - setup_pspace(disc); + setup_pspace(disc, 0); + setup_pspace(disc, 1); /* TODO: setup USPACE extents */ |