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 #include "cctype.h"
23 #include "iso9660.h"
24
25 struct iso9660_date {
26 unsigned char year[4];
27 unsigned char month[2];
28 unsigned char day[2];
29 unsigned char hour[2];
30 unsigned char minute[2];
31 unsigned char second[2];
32 unsigned char hundredth[2];
33 unsigned char offset;
34 } __attribute__ ((packed));
35
36 /* PVD - Primary volume descriptor */
37 struct iso_volume_descriptor {
38 unsigned char vd_type;
39 unsigned char vd_id[5];
40 unsigned char vd_version;
41 unsigned char flags;
42 unsigned char system_id[32];
43 unsigned char volume_id[32];
44 unsigned char unused[8];
45 unsigned char space_size[8];
46 unsigned char escape_sequences[8];
47 unsigned char unused2[32];
48 unsigned char logical_block_size[4];
49 unsigned char unused3[58];
50 unsigned char volume_set_id[128];
51 unsigned char publisher_id[128];
52 unsigned char data_preparer_id[128];
53 unsigned char application_id[128];
54 unsigned char unused4[111];
55 struct iso9660_date created;
56 struct iso9660_date modified;
57 } __attribute__((packed));
58
59 /* Boot Record */
60 struct boot_record {
61 unsigned char vd_type;
62 unsigned char vd_id[5];
63 unsigned char vd_version;
64 unsigned char boot_system_id[32];
65 unsigned char boot_id[32];
66 unsigned char unused[1];
67 } __attribute__((packed));
68
69 #define ISO_SUPERBLOCK_OFFSET 0x8000
70 #define ISO_SECTOR_SIZE 0x800
71 #define ISO_VD_BOOT_RECORD 0x0
72 #define ISO_VD_PRIMARY 0x1
73 #define ISO_VD_SUPPLEMENTARY 0x2
74 #define ISO_VD_END 0xff
75 #define ISO_VD_MAX 16
76 /* maximal string field size used anywhere in ISO; update if necessary */
77 #define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, volume_set_id)
78
79 struct high_sierra_volume_descriptor {
80 unsigned char foo[8];
81 unsigned char type;
82 unsigned char id[5];
83 unsigned char version;
84 unsigned char unused1;
85 unsigned char system_id[32];
86 unsigned char volume_id[32];
87 } __attribute__((packed));
88
89 /* old High Sierra format */
90 static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag)
91 {
92 struct high_sierra_volume_descriptor *iso;
93
94 iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor);
95 if (!iso)
96 return errno ? -errno : 1;
97
98 blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE);
99 blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE);
100 blkid_probe_set_version(pr, "High Sierra");
101 blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id));
102 return 0;
103 }
104
105 static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date)
106 {
107 unsigned char buffer[16];
108 unsigned int i, zeros = 0;
109
110 buffer[0] = date->year[0];
111 buffer[1] = date->year[1];
112 buffer[2] = date->year[2];
113 buffer[3] = date->year[3];
114 buffer[4] = date->month[0];
115 buffer[5] = date->month[1];
116 buffer[6] = date->day[0];
117 buffer[7] = date->day[1];
118 buffer[8] = date->hour[0];
119 buffer[9] = date->hour[1];
120 buffer[10] = date->minute[0];
121 buffer[11] = date->minute[1];
122 buffer[12] = date->second[0];
123 buffer[13] = date->second[1];
124 buffer[14] = date->hundredth[0];
125 buffer[15] = date->hundredth[1];
126
127 /* count the number of zeros ('0') in the date buffer */
128 for (i = 0, zeros = 0; i < sizeof(buffer); i++)
129 if (buffer[i] == '0')
130 zeros++;
131
132 /* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */
133 if (zeros == sizeof(buffer) && date->offset == 0)
134 return 0;
135
136 /* generate an UUID using this date and return success */
137 blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer),
138 "%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
139 buffer[0], buffer[1], buffer[2], buffer[3],
140 buffer[4], buffer[5],
141 buffer[6], buffer[7],
142 buffer[8], buffer[9],
143 buffer[10], buffer[11],
144 buffer[12], buffer[13],
145 buffer[14], buffer[15]);
146
147 return 1;
148 }
149
150 static int is_str_empty(const unsigned char *str, size_t len)
151 {
152 size_t i;
153
154 if (!str || !*str)
155 return 1;
156
157 for (i = 0; i < len; i++)
158 if (!isspace(str[i]))
159 return 0;
160 return 1;
161 }
162
163 static int is_utf16be_str_empty(unsigned char *utf16, size_t len)
164 {
165 size_t i;
166
167 for (i = 0; i < len; i += 2) {
168 if (utf16[i] != 0x0 || !isspace(utf16[i + 1]))
169 return 0;
170 }
171 return 1;
172 }
173
174 /* if @utf16 is prefix of @ascii (ignoring non-representable characters and upper-case conversion)
175 * then reconstruct prefix from @utf16 and @ascii, append suffix from @ascii, fill it into @out
176 * and returns length of bytes written into @out; otherwise returns zero */
177 static size_t merge_utf16be_ascii(unsigned char *out, size_t out_len, const unsigned char *utf16, const unsigned char *ascii, size_t len)
178 {
179 size_t o, a, u;
180
181 for (o = 0, a = 0, u = 0; u + 1 < len && a < len && o + 1 < out_len; o += 2, a++, u += 2) {
182 /* Surrogate pair with code point above U+FFFF */
183 if (utf16[u] >= 0xD8 && utf16[u] <= 0xDB && u + 3 < len &&
184 utf16[u + 2] >= 0xDC && utf16[u + 2] <= 0xDF) {
185 out[o++] = utf16[u++];
186 out[o++] = utf16[u++];
187 }
188 /* Value '_' is replacement for non-representable character */
189 if (ascii[a] == '_') {
190 out[o] = utf16[u];
191 out[o + 1] = utf16[u + 1];
192 } else if (utf16[u] == 0x00 && utf16[u + 1] == '_') {
193 out[o] = 0x00;
194 out[o + 1] = ascii[a];
195 } else if (utf16[u] == 0x00 && c_toupper(ascii[a]) == c_toupper(utf16[u + 1])) {
196 out[o] = 0x00;
197 out[o + 1] = c_isupper(ascii[a]) ? utf16[u + 1] : ascii[a];
198 } else {
199 return 0;
200 }
201 }
202
203 for (; a < len && o + 1 < out_len; o += 2, a++) {
204 out[o] = 0x00;
205 out[o + 1] = ascii[a];
206 }
207
208 return o;
209 }
210
211 /* iso9660 [+ Microsoft Joliet Extension] */
212 static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
213 {
214 struct boot_record *boot = NULL;
215 struct iso_volume_descriptor *pvd = NULL;
216 struct iso_volume_descriptor *joliet = NULL;
217 /* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */
218 unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2];
219 size_t len;
220 int is_unicode_empty;
221 int is_ascii_empty;
222 int i;
223 uint64_t off;
224
225 if (blkid_probe_get_hint(pr, mag->hoff, &off) < 0)
226 off = 0;
227
228 if (off % ISO_SECTOR_SIZE)
229 return 1;
230
231 if (strcmp(mag->magic, "CDROM") == 0)
232 return probe_iso9660_hsfs(pr, mag);
233
234 for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || !joliet); i++, off += ISO_SECTOR_SIZE) {
235 unsigned char *desc =
236 blkid_probe_get_buffer(pr,
237 off,
238 max(sizeof(struct boot_record),
239 sizeof(struct iso_volume_descriptor)));
240
241 if (desc == NULL || desc[0] == ISO_VD_END)
242 break;
243 else if (!boot && desc[0] == ISO_VD_BOOT_RECORD)
244 boot = (struct boot_record *)desc;
245 else if (!pvd && desc[0] == ISO_VD_PRIMARY)
246 pvd = (struct iso_volume_descriptor *)desc;
247 else if (!joliet && desc[0] == ISO_VD_SUPPLEMENTARY) {
248 joliet = (struct iso_volume_descriptor *)desc;
249 if (memcmp(joliet->escape_sequences, "%/@", 3) != 0 &&
250 memcmp(joliet->escape_sequences, "%/C", 3) != 0 &&
251 memcmp(joliet->escape_sequences, "%/E", 3) != 0)
252 joliet = NULL;
253 }
254 }
255
256 if (!pvd)
257 return errno ? -errno : 1;
258
259 uint16_t logical_block_size = isonum_723(pvd->logical_block_size, false);
260 uint32_t space_size = isonum_733(pvd->space_size, false);
261
262 blkid_probe_set_fsblocksize(pr, logical_block_size);
263 blkid_probe_set_block_size(pr, logical_block_size);
264 blkid_probe_set_fssize(pr, (uint64_t) space_size * logical_block_size);
265
266 if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->system_id, pvd->system_id, sizeof(pvd->system_id))) != 0)
267 blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", buf, len, UL_ENCODE_UTF16BE);
268 else if (joliet)
269 blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", joliet->system_id, sizeof(joliet->system_id), UL_ENCODE_UTF16BE);
270 else
271 blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id));
272
273 if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0)
274 blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE);
275 else if (joliet)
276 blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE);
277 else
278 blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->volume_set_id, sizeof(pvd->volume_set_id));
279
280 is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_');
281 is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_'));
282 if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0)
283 blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE);
284 else if (!is_unicode_empty)
285 blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE);
286 else if (!is_ascii_empty)
287 blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->publisher_id, sizeof(pvd->publisher_id));
288
289 is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_');
290 is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_'));
291 if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0)
292 blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE);
293 else if (!is_unicode_empty)
294 blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE);
295 else if (!is_ascii_empty)
296 blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->data_preparer_id, sizeof(pvd->data_preparer_id));
297
298 is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_');
299 is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_'));
300 if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0)
301 blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE);
302 else if (!is_unicode_empty)
303 blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE);
304 else if (!is_ascii_empty)
305 blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->application_id, sizeof(pvd->application_id));
306
307 /* create an UUID using the modified/created date */
308 if (! probe_iso9660_set_uuid(pr, &pvd->modified))
309 probe_iso9660_set_uuid(pr, &pvd->created);
310
311 if (boot)
312 blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID",
313 boot->boot_system_id,
314 sizeof(boot->boot_system_id));
315
316 if (joliet)
317 blkid_probe_set_version(pr, "Joliet Extension");
318
319 /* Label in Joliet is UNICODE (UTF16BE) but can contain only 16 characters. Label in PVD is
320 * subset of ASCII but can contain up to the 32 characters. Non-representable characters are
321 * stored as replacement character '_'. Label in Joliet is in most cases trimmed but UNICODE
322 * version of label in PVD. Based on these facts try to reconstruct original label if label
323 * in Joliet is prefix of the label in PVD (ignoring non-representable characters).
324 */
325 if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_id, pvd->volume_id, sizeof(pvd->volume_id))) != 0)
326 blkid_probe_set_utf8label(pr, buf, len, UL_ENCODE_UTF16BE);
327 else if (joliet)
328 blkid_probe_set_utf8label(pr, joliet->volume_id, sizeof(joliet->volume_id), UL_ENCODE_UTF16BE);
329 else
330 blkid_probe_set_label(pr, pvd->volume_id, sizeof(pvd->volume_id));
331
332 return 0;
333 }
334
335 const struct blkid_idinfo iso9660_idinfo =
336 {
337 .name = "iso9660",
338 .usage = BLKID_USAGE_FILESYSTEM,
339 .probefunc = probe_iso9660,
340 .flags = BLKID_IDINFO_TOLERANT,
341 .magics =
342 {
343 { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
344 { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" },
345 { NULL }
346 }
347 };
348