diff options
Diffstat (limited to 'libblkid/iso9660.c')
-rw-r--r-- | libblkid/iso9660.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/libblkid/iso9660.c b/libblkid/iso9660.c new file mode 100644 index 000000000..148587b3b --- /dev/null +++ b/libblkid/iso9660.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired also by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include <ctype.h> + +#include "superblocks.h" + +struct iso9660_date { + unsigned char year[4]; + unsigned char month[2]; + unsigned char day[2]; + unsigned char hour[2]; + unsigned char minute[2]; + unsigned char second[2]; + unsigned char hundredth[2]; + unsigned char offset; +} __attribute__ ((packed)); + +/* PVD - Primary volume descriptor */ +struct iso_volume_descriptor { + unsigned char vd_type; + unsigned char vd_id[5]; + unsigned char vd_version; + unsigned char flags; + unsigned char system_id[32]; + unsigned char volume_id[32]; + unsigned char unused[8]; + unsigned char space_size[8]; + unsigned char escape_sequences[8]; + unsigned char unused1[222]; + unsigned char publisher_id[128]; + unsigned char unused2[128]; + unsigned char application_id[128]; + unsigned char unused3[111]; + struct iso9660_date created; + struct iso9660_date modified; +} __attribute__((packed)); + +/* Boot Record */ +struct boot_record { + unsigned char vd_type; + unsigned char vd_id[5]; + unsigned char vd_version; + unsigned char boot_system_id[32]; + unsigned char boot_id[32]; + unsigned char unused[1]; +} __attribute__((packed)); + +#define ISO_SUPERBLOCK_OFFSET 0x8000 +#define ISO_SECTOR_SIZE 0x800 +#define ISO_VD_OFFSET (ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE) +#define ISO_VD_BOOT_RECORD 0x0 +#define ISO_VD_SUPPLEMENTARY 0x2 +#define ISO_VD_END 0xff +#define ISO_VD_MAX 16 + +struct high_sierra_volume_descriptor { + unsigned char foo[8]; + unsigned char type; + unsigned char id[5]; + unsigned char version; + unsigned char unused1; + unsigned char system_id[32]; + unsigned char volume_id[32]; +} __attribute__((packed)); + +/* returns 1 if the begin of @ascii is equal to @utf16 string. + */ +static int ascii_eq_utf16be(unsigned char *ascii, + unsigned char *utf16, size_t len) +{ + size_t a, u; + + for (a = 0, u = 0; u < len; a++, u += 2) { + if (utf16[u] != 0x0 || ascii[a] != utf16[u + 1]) + return 0; + } + return 1; +} + +/* old High Sierra format */ +static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct high_sierra_volume_descriptor *iso; + + iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor); + if (!iso) + return -1; + + blkid_probe_set_version(pr, "High Sierra"); + blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id)); + return 0; +} + +static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date) +{ + unsigned char buffer[16]; + unsigned int i, zeros = 0; + + buffer[0] = date->year[0]; + buffer[1] = date->year[1]; + buffer[2] = date->year[2]; + buffer[3] = date->year[3]; + buffer[4] = date->month[0]; + buffer[5] = date->month[1]; + buffer[6] = date->day[0]; + buffer[7] = date->day[1]; + buffer[8] = date->hour[0]; + buffer[9] = date->hour[1]; + buffer[10] = date->minute[0]; + buffer[11] = date->minute[1]; + buffer[12] = date->second[0]; + buffer[13] = date->second[1]; + buffer[14] = date->hundredth[0]; + buffer[15] = date->hundredth[1]; + + /* count the number of zeros ('0') in the date buffer */ + for (i = 0, zeros = 0; i < sizeof(buffer); i++) + if (buffer[i] == '0') + zeros++; + + /* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */ + if (zeros == sizeof(buffer) && date->offset == 0) + return 0; + + /* generate an UUID using this date and return success */ + blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer), + "%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c", + buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], + buffer[6], buffer[7], + buffer[8], buffer[9], + buffer[10], buffer[11], + buffer[12], buffer[13], + buffer[14], buffer[15]); + + return 1; +} + +static int is_str_empty(const unsigned char *str, size_t len) +{ + size_t i; + + if (!str || !*str) + return 1; + + for (i = 0; i < len; i++) + if (!isspace(str[i])) + return 0; + return 1; +} + +/* iso9660 [+ Microsoft Joliet Extension] */ +int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct iso_volume_descriptor *iso; + unsigned char label[32]; + int i; + int off; + + if (strcmp(mag->magic, "CDROM") == 0) + return probe_iso9660_hsfs(pr, mag); + + iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor); + if (!iso) + return -1; + + memcpy(label, iso->volume_id, sizeof(label)); + + if (!is_str_empty(iso->system_id, sizeof(iso->system_id))) + blkid_probe_set_id_label(pr, "SYSTEM_ID", + iso->system_id, sizeof(iso->system_id)); + + if (!is_str_empty(iso->publisher_id, sizeof(iso->publisher_id))) + blkid_probe_set_id_label(pr, "PUBLISHER_ID", + iso->publisher_id, sizeof(iso->publisher_id)); + + if (!is_str_empty(iso->application_id, sizeof(iso->application_id))) + blkid_probe_set_id_label(pr, "APPLICATION_ID", + iso->application_id, sizeof(iso->application_id)); + + /* create an UUID using the modified/created date */ + if (! probe_iso9660_set_uuid(pr, &iso->modified)) + probe_iso9660_set_uuid(pr, &iso->created); + + /* Joliet Extension and Boot Record */ + off = ISO_VD_OFFSET; + for (i = 0; i < ISO_VD_MAX; i++) { + struct boot_record *boot= (struct boot_record *) + blkid_probe_get_buffer(pr, + off, + max(sizeof(struct boot_record), + sizeof(struct iso_volume_descriptor))); + + if (boot == NULL || boot->vd_type == ISO_VD_END) + break; + + if (boot->vd_type == ISO_VD_BOOT_RECORD) { + if (!is_str_empty(boot->boot_system_id, + sizeof(boot->boot_system_id))) + blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID", + boot->boot_system_id, + sizeof(boot->boot_system_id)); + off += ISO_SECTOR_SIZE; + continue; + } + + /* Not a Boot record, lets see if its supplemntary volume descriptor */ + iso = (struct iso_volume_descriptor *) boot; + + if (iso->vd_type != ISO_VD_SUPPLEMENTARY) { + off += ISO_SECTOR_SIZE; + continue; + } + + if (memcmp(iso->escape_sequences, "%/@", 3) == 0 || + memcmp(iso->escape_sequences, "%/C", 3) == 0 || + memcmp(iso->escape_sequences, "%/E", 3) == 0) { + + blkid_probe_set_version(pr, "Joliet Extension"); + + /* Is the Joliet (UTF16BE) label equal to the label in + * the PVD? If yes, use PVD label. The Jolied version + * of the label could be trimed (because UTF16..). + */ + if (ascii_eq_utf16be(label, iso->volume_id, 32)) + break; + + blkid_probe_set_utf8label(pr, + iso->volume_id, + sizeof(iso->volume_id), + BLKID_ENC_UTF16BE); + goto has_label; + } + off += ISO_SECTOR_SIZE; + } + + /* Joliet not found, let use standard iso label */ + blkid_probe_set_label(pr, label, sizeof(label)); + +has_label: + return 0; +} + +const struct blkid_idinfo iso9660_idinfo = +{ + .name = "iso9660", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_iso9660, + .flags = BLKID_IDINFO_TOLERANT, + .magics = + { + { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 }, + { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9 }, + { NULL } + } +}; + |