bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 1999 by Andries Brouwer |
| 3 | * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o |
| 4 | * Copyright (C) 2001 by Andreas Dilger |
| 5 | * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> |
| 6 | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
| 7 | * |
| 8 | * Inspired also by libvolume_id by |
| 9 | * Kay Sievers <kay.sievers@vrfy.org> |
| 10 | * |
| 11 | * This file may be redistributed under the terms of the |
| 12 | * GNU Lesser General Public License. |
| 13 | */ |
| 14 | #include <stdio.h> |
| 15 | #include <stdlib.h> |
| 16 | #include <unistd.h> |
| 17 | #include <string.h> |
| 18 | #include <stdint.h> |
| 19 | #include <ctype.h> |
| 20 | |
| 21 | #include "superblocks.h" |
| 22 | |
| 23 | struct iso9660_date { |
| 24 | unsigned char year[4]; |
| 25 | unsigned char month[2]; |
| 26 | unsigned char day[2]; |
| 27 | unsigned char hour[2]; |
| 28 | unsigned char minute[2]; |
| 29 | unsigned char second[2]; |
| 30 | unsigned char hundredth[2]; |
| 31 | unsigned char offset; |
| 32 | } __attribute__ ((packed)); |
| 33 | |
| 34 | /* PVD - Primary volume descriptor */ |
| 35 | struct iso_volume_descriptor { |
| 36 | unsigned char vd_type; |
| 37 | unsigned char vd_id[5]; |
| 38 | unsigned char vd_version; |
| 39 | unsigned char flags; |
| 40 | unsigned char system_id[32]; |
| 41 | unsigned char volume_id[32]; |
| 42 | unsigned char unused[8]; |
| 43 | unsigned char space_size[8]; |
| 44 | unsigned char escape_sequences[8]; |
| 45 | unsigned char unused1[222]; |
| 46 | unsigned char publisher_id[128]; |
| 47 | unsigned char unused2[128]; |
| 48 | unsigned char application_id[128]; |
| 49 | unsigned char unused3[111]; |
| 50 | struct iso9660_date created; |
| 51 | struct iso9660_date modified; |
| 52 | } __attribute__((packed)); |
| 53 | |
| 54 | /* Boot Record */ |
| 55 | struct boot_record { |
| 56 | unsigned char vd_type; |
| 57 | unsigned char vd_id[5]; |
| 58 | unsigned char vd_version; |
| 59 | unsigned char boot_system_id[32]; |
| 60 | unsigned char boot_id[32]; |
| 61 | unsigned char unused[1]; |
| 62 | } __attribute__((packed)); |
| 63 | |
| 64 | #define ISO_SUPERBLOCK_OFFSET 0x8000 |
| 65 | #define ISO_SECTOR_SIZE 0x800 |
| 66 | #define ISO_VD_OFFSET (ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE) |
| 67 | #define ISO_VD_BOOT_RECORD 0x0 |
| 68 | #define ISO_VD_SUPPLEMENTARY 0x2 |
| 69 | #define ISO_VD_END 0xff |
| 70 | #define ISO_VD_MAX 16 |
| 71 | |
| 72 | struct high_sierra_volume_descriptor { |
| 73 | unsigned char foo[8]; |
| 74 | unsigned char type; |
| 75 | unsigned char id[5]; |
| 76 | unsigned char version; |
| 77 | unsigned char unused1; |
| 78 | unsigned char system_id[32]; |
| 79 | unsigned char volume_id[32]; |
| 80 | } __attribute__((packed)); |
| 81 | |
| 82 | /* returns 1 if the begin of @ascii is equal to @utf16 string. |
| 83 | */ |
| 84 | static int ascii_eq_utf16be(unsigned char *ascii, |
| 85 | unsigned char *utf16, size_t len) |
| 86 | { |
| 87 | size_t a, u; |
| 88 | |
| 89 | for (a = 0, u = 0; u < len; a++, u += 2) { |
| 90 | if (utf16[u] != 0x0 || ascii[a] != utf16[u + 1]) |
| 91 | return 0; |
| 92 | } |
| 93 | return 1; |
| 94 | } |
| 95 | |
| 96 | /* old High Sierra format */ |
| 97 | static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag) |
| 98 | { |
| 99 | struct high_sierra_volume_descriptor *iso; |
| 100 | |
| 101 | iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor); |
| 102 | if (!iso) |
| 103 | return -1; |
| 104 | |
| 105 | blkid_probe_set_version(pr, "High Sierra"); |
| 106 | blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id)); |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date) |
| 111 | { |
| 112 | unsigned char buffer[16]; |
| 113 | unsigned int i, zeros = 0; |
| 114 | |
| 115 | buffer[0] = date->year[0]; |
| 116 | buffer[1] = date->year[1]; |
| 117 | buffer[2] = date->year[2]; |
| 118 | buffer[3] = date->year[3]; |
| 119 | buffer[4] = date->month[0]; |
| 120 | buffer[5] = date->month[1]; |
| 121 | buffer[6] = date->day[0]; |
| 122 | buffer[7] = date->day[1]; |
| 123 | buffer[8] = date->hour[0]; |
| 124 | buffer[9] = date->hour[1]; |
| 125 | buffer[10] = date->minute[0]; |
| 126 | buffer[11] = date->minute[1]; |
| 127 | buffer[12] = date->second[0]; |
| 128 | buffer[13] = date->second[1]; |
| 129 | buffer[14] = date->hundredth[0]; |
| 130 | buffer[15] = date->hundredth[1]; |
| 131 | |
| 132 | /* count the number of zeros ('0') in the date buffer */ |
| 133 | for (i = 0, zeros = 0; i < sizeof(buffer); i++) |
| 134 | if (buffer[i] == '0') |
| 135 | zeros++; |
| 136 | |
| 137 | /* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */ |
| 138 | if (zeros == sizeof(buffer) && date->offset == 0) |
| 139 | return 0; |
| 140 | |
| 141 | /* generate an UUID using this date and return success */ |
| 142 | blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer), |
| 143 | "%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c", |
| 144 | buffer[0], buffer[1], buffer[2], buffer[3], |
| 145 | buffer[4], buffer[5], |
| 146 | buffer[6], buffer[7], |
| 147 | buffer[8], buffer[9], |
| 148 | buffer[10], buffer[11], |
| 149 | buffer[12], buffer[13], |
| 150 | buffer[14], buffer[15]); |
| 151 | |
| 152 | return 1; |
| 153 | } |
| 154 | |
| 155 | static int is_str_empty(const unsigned char *str, size_t len) |
| 156 | { |
| 157 | size_t i; |
| 158 | |
| 159 | if (!str || !*str) |
| 160 | return 1; |
| 161 | |
| 162 | for (i = 0; i < len; i++) |
| 163 | if (!isspace(str[i])) |
| 164 | return 0; |
| 165 | return 1; |
| 166 | } |
| 167 | |
| 168 | /* iso9660 [+ Microsoft Joliet Extension] */ |
| 169 | int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) |
| 170 | { |
| 171 | struct iso_volume_descriptor *iso; |
| 172 | unsigned char label[32]; |
| 173 | int i; |
| 174 | int off; |
| 175 | |
| 176 | if (strcmp(mag->magic, "CDROM") == 0) |
| 177 | return probe_iso9660_hsfs(pr, mag); |
| 178 | |
| 179 | iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor); |
| 180 | if (!iso) |
| 181 | return -1; |
| 182 | |
| 183 | memcpy(label, iso->volume_id, sizeof(label)); |
| 184 | |
| 185 | if (!is_str_empty(iso->system_id, sizeof(iso->system_id))) |
| 186 | blkid_probe_set_id_label(pr, "SYSTEM_ID", |
| 187 | iso->system_id, sizeof(iso->system_id)); |
| 188 | |
| 189 | if (!is_str_empty(iso->publisher_id, sizeof(iso->publisher_id))) |
| 190 | blkid_probe_set_id_label(pr, "PUBLISHER_ID", |
| 191 | iso->publisher_id, sizeof(iso->publisher_id)); |
| 192 | |
| 193 | if (!is_str_empty(iso->application_id, sizeof(iso->application_id))) |
| 194 | blkid_probe_set_id_label(pr, "APPLICATION_ID", |
| 195 | iso->application_id, sizeof(iso->application_id)); |
| 196 | |
| 197 | /* create an UUID using the modified/created date */ |
| 198 | if (! probe_iso9660_set_uuid(pr, &iso->modified)) |
| 199 | probe_iso9660_set_uuid(pr, &iso->created); |
| 200 | |
| 201 | /* Joliet Extension and Boot Record */ |
| 202 | off = ISO_VD_OFFSET; |
| 203 | for (i = 0; i < ISO_VD_MAX; i++) { |
| 204 | struct boot_record *boot= (struct boot_record *) |
| 205 | blkid_probe_get_buffer(pr, |
| 206 | off, |
| 207 | max(sizeof(struct boot_record), |
| 208 | sizeof(struct iso_volume_descriptor))); |
| 209 | |
| 210 | if (boot == NULL || boot->vd_type == ISO_VD_END) |
| 211 | break; |
| 212 | |
| 213 | if (boot->vd_type == ISO_VD_BOOT_RECORD) { |
| 214 | if (!is_str_empty(boot->boot_system_id, |
| 215 | sizeof(boot->boot_system_id))) |
| 216 | blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID", |
| 217 | boot->boot_system_id, |
| 218 | sizeof(boot->boot_system_id)); |
| 219 | off += ISO_SECTOR_SIZE; |
| 220 | continue; |
| 221 | } |
| 222 | |
| 223 | /* Not a Boot record, lets see if its supplemntary volume descriptor */ |
| 224 | iso = (struct iso_volume_descriptor *) boot; |
| 225 | |
| 226 | if (iso->vd_type != ISO_VD_SUPPLEMENTARY) { |
| 227 | off += ISO_SECTOR_SIZE; |
| 228 | continue; |
| 229 | } |
| 230 | |
| 231 | if (memcmp(iso->escape_sequences, "%/@", 3) == 0 || |
| 232 | memcmp(iso->escape_sequences, "%/C", 3) == 0 || |
| 233 | memcmp(iso->escape_sequences, "%/E", 3) == 0) { |
| 234 | |
| 235 | blkid_probe_set_version(pr, "Joliet Extension"); |
| 236 | |
| 237 | /* Is the Joliet (UTF16BE) label equal to the label in |
| 238 | * the PVD? If yes, use PVD label. The Jolied version |
| 239 | * of the label could be trimed (because UTF16..). |
| 240 | */ |
| 241 | if (ascii_eq_utf16be(label, iso->volume_id, 32)) |
| 242 | break; |
| 243 | |
| 244 | blkid_probe_set_utf8label(pr, |
| 245 | iso->volume_id, |
| 246 | sizeof(iso->volume_id), |
| 247 | BLKID_ENC_UTF16BE); |
| 248 | goto has_label; |
| 249 | } |
| 250 | off += ISO_SECTOR_SIZE; |
| 251 | } |
| 252 | |
| 253 | /* Joliet not found, let use standard iso label */ |
| 254 | blkid_probe_set_label(pr, label, sizeof(label)); |
| 255 | |
| 256 | has_label: |
| 257 | return 0; |
| 258 | } |
| 259 | |
| 260 | const struct blkid_idinfo iso9660_idinfo = |
| 261 | { |
| 262 | .name = "iso9660", |
| 263 | .usage = BLKID_USAGE_FILESYSTEM, |
| 264 | .probefunc = probe_iso9660, |
| 265 | .flags = BLKID_IDINFO_TOLERANT, |
| 266 | .magics = |
| 267 | { |
| 268 | { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 }, |
| 269 | { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9 }, |
| 270 | { NULL } |
| 271 | } |
| 272 | }; |
| 273 | |