/src/util-linux/libblkid/src/superblocks/udf.c
Line | Count | Source |
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 | | * Copyright (C) 2014-2017 Pali Rohár <pali.rohar@gmail.com> |
8 | | * |
9 | | * This file may be redistributed under the terms of the |
10 | | * GNU Lesser General Public License. |
11 | | */ |
12 | | #include <stdio.h> |
13 | | #include <stdlib.h> |
14 | | #include <unistd.h> |
15 | | #include <string.h> |
16 | | #include <errno.h> |
17 | | #include <ctype.h> |
18 | | #include <stdint.h> |
19 | | |
20 | | #include "superblocks.h" |
21 | | |
22 | 27 | #define is_charset_udf(charspec) ((charspec).type == 0 && strncmp((charspec).info, "OSTA Compressed Unicode", sizeof((charspec).info)) == 0) |
23 | | |
24 | 0 | #define udf_cid_to_enc(cid) ((cid) == 8 ? UL_ENCODE_LATIN1 : (cid) == 16 ? UL_ENCODE_UTF16BE : -1) |
25 | | |
26 | | struct charspec { |
27 | | uint8_t type; |
28 | | char info[63]; |
29 | | } __attribute__((packed)); |
30 | | |
31 | | struct dstring128 { |
32 | | uint8_t cid; |
33 | | uint8_t c[126]; |
34 | | uint8_t clen; |
35 | | } __attribute__((packed)); |
36 | | |
37 | | struct dstring32 { |
38 | | uint8_t cid; |
39 | | uint8_t c[30]; |
40 | | uint8_t clen; |
41 | | } __attribute__((packed)); |
42 | | |
43 | | struct dstring36 { |
44 | | uint8_t cid; |
45 | | uint8_t c[34]; |
46 | | uint8_t clen; |
47 | | } __attribute__((packed)); |
48 | | |
49 | | struct volume_descriptor { |
50 | | struct descriptor_tag { |
51 | | uint16_t id; |
52 | | uint16_t version; |
53 | | uint8_t checksum; |
54 | | uint8_t reserved; |
55 | | uint16_t serial; |
56 | | uint16_t crc; |
57 | | uint16_t crc_len; |
58 | | uint32_t location; |
59 | | } __attribute__((packed)) tag; |
60 | | |
61 | | union { |
62 | | struct anchor_descriptor { |
63 | | uint32_t length; |
64 | | uint32_t location; |
65 | | } __attribute__((packed)) anchor; |
66 | | |
67 | | struct primary_descriptor { |
68 | | uint32_t seq_num; |
69 | | uint32_t desc_num; |
70 | | struct dstring32 ident; |
71 | | uint16_t vds_num; |
72 | | uint16_t max_vol_seq; |
73 | | uint16_t ichg_lvl; |
74 | | uint16_t max_ichg_lvl; |
75 | | uint32_t charset_list; |
76 | | uint32_t max_charset_list; |
77 | | struct dstring128 volset_id; |
78 | | struct charspec desc_charset; |
79 | | struct charspec exp_charset; |
80 | | uint32_t vol_abstract[2]; |
81 | | uint32_t vol_copyright[2]; |
82 | | uint8_t app_id_flags; |
83 | | char app_id[23]; |
84 | | uint8_t app_id_reserved[8]; |
85 | | uint8_t recording_date[12]; |
86 | | uint8_t imp_id_flags; |
87 | | char imp_id[23]; |
88 | | uint8_t imp_id_os_class; |
89 | | uint8_t imp_id_os_id; |
90 | | uint8_t imp_id_reserved[6]; |
91 | | } __attribute__((packed)) primary; |
92 | | |
93 | | struct logical_descriptor { |
94 | | uint32_t seq_num; |
95 | | struct charspec desc_charset; |
96 | | struct dstring128 logvol_id; |
97 | | uint32_t logical_blocksize; |
98 | | uint8_t domain_id_flags; |
99 | | char domain_id[23]; |
100 | | uint16_t udf_rev; |
101 | | uint8_t domain_suffix_flags; |
102 | | uint8_t reserved[5]; |
103 | | uint8_t logical_contents_use[16]; |
104 | | uint32_t map_table_length; |
105 | | uint32_t num_partition_maps; |
106 | | uint8_t imp_id[32]; |
107 | | uint8_t imp_use[128]; |
108 | | uint32_t lvid_length; |
109 | | uint32_t lvid_location; |
110 | | } __attribute__((packed)) logical; |
111 | | |
112 | | struct logical_vol_integ_descriptor { |
113 | | uint8_t recording_date[12]; |
114 | | uint32_t type; |
115 | | uint32_t next_lvid_length; |
116 | | uint32_t next_lvid_location; |
117 | | uint8_t logical_contents_use[32]; |
118 | | uint32_t num_partitions; |
119 | | uint32_t imp_use_length; |
120 | | } __attribute__((packed)) logical_vol_integ; |
121 | | |
122 | | struct imp_use_volume_descriptor { |
123 | | uint32_t seq_num; |
124 | | uint8_t lvi_id_flags; |
125 | | char lvi_id[23]; |
126 | | uint16_t lvi_id_udf_rev; |
127 | | uint8_t lvi_id_os_class; |
128 | | uint8_t lvi_id_os_id; |
129 | | uint8_t lvi_id_reserved[4]; |
130 | | struct charspec lvi_charset; |
131 | | struct dstring128 logvol_id; |
132 | | struct dstring36 lvinfo1; |
133 | | struct dstring36 lvinfo2; |
134 | | struct dstring36 lvinfo3; |
135 | | } __attribute__((packed)) imp_use_volume; |
136 | | } __attribute__((packed)) type; |
137 | | |
138 | | } __attribute__((packed)); |
139 | | |
140 | 26 | #define TAG_ID_PVD 1 |
141 | 76 | #define TAG_ID_AVDP 2 |
142 | 17 | #define TAG_ID_IUVD 4 |
143 | 17 | #define TAG_ID_LVD 6 |
144 | 26 | #define TAG_ID_TD 8 |
145 | 0 | #define TAG_ID_LVID 9 |
146 | | |
147 | | struct volume_structure_descriptor { |
148 | | uint8_t type; |
149 | | uint8_t id[5]; |
150 | | uint8_t version; |
151 | | } __attribute__((packed)); |
152 | | |
153 | 1.54k | #define UDF_VSD_OFFSET 0x8000LL |
154 | | |
155 | | struct logical_vol_integ_descriptor_imp_use |
156 | | { |
157 | | uint8_t imp_id[32]; |
158 | | uint32_t num_files; |
159 | | uint32_t num_dirs; |
160 | | uint16_t min_udf_read_rev; |
161 | | uint16_t min_udf_write_rev; |
162 | | uint16_t max_udf_write_rev; |
163 | | } __attribute__ ((packed)); |
164 | | |
165 | 0 | #define UDF_LVIDIU_OFFSET(vd) (sizeof((vd).tag) + sizeof((vd).type.logical_vol_integ) + 2 * 4 * le32_to_cpu((vd).type.logical_vol_integ.num_partitions)) |
166 | 0 | #define UDF_LVIDIU_LENGTH(vd) (le32_to_cpu((vd).type.logical_vol_integ.imp_use_length)) |
167 | | |
168 | | static inline int gen_uuid_from_volset_id(unsigned char uuid[17], struct dstring128 *volset_id) |
169 | 0 | { |
170 | 0 | int enc; |
171 | 0 | size_t i; |
172 | 0 | size_t len; |
173 | 0 | size_t clen; |
174 | 0 | size_t nonhexpos; |
175 | 0 | unsigned char buf[17]; |
176 | |
|
177 | 0 | memset(buf, 0, sizeof(buf)); |
178 | |
|
179 | 0 | clen = volset_id->clen; |
180 | 0 | if (clen > 0) |
181 | 0 | --clen; |
182 | 0 | if (clen > sizeof(volset_id->c)) |
183 | 0 | clen = sizeof(volset_id->c); |
184 | |
|
185 | 0 | enc = udf_cid_to_enc(volset_id->cid); |
186 | 0 | if (enc == -1) |
187 | 0 | return -1; |
188 | | |
189 | 0 | len = ul_encode_to_utf8(enc, buf, sizeof(buf), volset_id->c, clen); |
190 | 0 | if (len < 8) |
191 | 0 | return -1; |
192 | | |
193 | 0 | nonhexpos = 16; |
194 | 0 | for (i = 0; i < 16; ++i) { |
195 | 0 | if (!isxdigit(buf[i])) { |
196 | 0 | nonhexpos = i; |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | } |
200 | |
|
201 | 0 | if (nonhexpos < 8) { |
202 | 0 | snprintf((char *) uuid, 17, "%02x%02x%02x%02x%02x%02x%02x%02x", |
203 | 0 | buf[0], buf[1], buf[2], buf[3], |
204 | 0 | buf[4], buf[5], buf[6], buf[7]); |
205 | 0 | } else if (nonhexpos < 16) { |
206 | 0 | for (i = 0; i < 8; ++i) |
207 | 0 | uuid[i] = tolower(buf[i]); |
208 | 0 | snprintf((char *) uuid + 8, 9, "%02x%02x%02x%02x", |
209 | 0 | buf[8], buf[9], buf[10], buf[11]); |
210 | 0 | } else { |
211 | 0 | for (i = 0; i < 16; ++i) |
212 | 0 | uuid[i] = tolower(buf[i]); |
213 | 0 | uuid[16] = 0; |
214 | 0 | } |
215 | |
|
216 | 0 | return 0; |
217 | 0 | } |
218 | | |
219 | | static int probe_udf(blkid_probe pr, |
220 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
221 | 462 | { |
222 | 462 | struct volume_descriptor *vd; |
223 | 462 | struct volume_structure_descriptor *vsd; |
224 | 462 | struct logical_vol_integ_descriptor_imp_use *lvidiu; |
225 | 462 | uint32_t lvid_len = 0; |
226 | 462 | uint32_t lvid_loc = 0; |
227 | 462 | uint64_t s_off; |
228 | 462 | uint32_t bs; |
229 | 462 | uint32_t b; |
230 | 462 | uint16_t type; |
231 | 462 | uint32_t count; |
232 | 462 | uint32_t loc; |
233 | 462 | size_t i; |
234 | 462 | uint32_t vsd_len; |
235 | 462 | uint16_t udf_rev = 0; |
236 | 462 | int is_udf = 0; |
237 | 462 | int vsd_2048_valid = -1; |
238 | 462 | int have_label = 0; |
239 | 462 | int have_uuid = 0; |
240 | 462 | int have_logvolid = 0; |
241 | 462 | int have_volid = 0; |
242 | 462 | int have_volsetid = 0; |
243 | 462 | int have_applicationid = 0; |
244 | 462 | int have_publisherid = 0; |
245 | | |
246 | | /* Session offset */ |
247 | 462 | if (blkid_probe_get_hint(pr, "session_offset", &s_off) < 0) |
248 | 462 | s_off = 0; |
249 | | |
250 | | /* The block size of a UDF filesystem is that of the underlying |
251 | | * storage; we check later on for the special case of image files, |
252 | | * which may have any block size valid for UDF filesystem */ |
253 | 462 | uint32_t pbs[] = { 0, 512, 1024, 2048, 4096 }; |
254 | 462 | pbs[0] = blkid_probe_get_sectorsize(pr); |
255 | | |
256 | 2.57k | for (i = 0; i < ARRAY_SIZE(pbs); i++) { |
257 | | /* Do not try with block size same as sector size two times */ |
258 | 2.17k | if (i != 0 && pbs[0] == pbs[i]) |
259 | 462 | continue; |
260 | | |
261 | | /* Do not try with block size which is not divisor of session offset */ |
262 | 1.71k | if (s_off % pbs[i]) |
263 | 0 | continue; |
264 | | |
265 | | /* ECMA-167 2/8.4, 2/9.1: Each VSD is either 2048 bytes long or |
266 | | * its size is same as blocksize (for blocksize > 2048 bytes) |
267 | | * plus padded with zeros */ |
268 | 1.71k | vsd_len = pbs[i] > 2048 ? pbs[i] : 2048; |
269 | | |
270 | | /* Process 2048 bytes long VSD on first session only once |
271 | | * as its location is same for any blocksize */ |
272 | 1.71k | if (s_off == 0 && vsd_len == 2048) { |
273 | 1.31k | if (vsd_2048_valid == 0) |
274 | 568 | continue; |
275 | 751 | if (vsd_2048_valid == 1) |
276 | 289 | goto anchor; |
277 | 751 | } |
278 | | |
279 | | /* Check for a Volume Structure Descriptor (VSD) */ |
280 | 1.54k | for (b = 0; b < 64; b++) { |
281 | 1.54k | vsd = (struct volume_structure_descriptor *) |
282 | 1.54k | blkid_probe_get_buffer(pr, |
283 | 1.54k | s_off + UDF_VSD_OFFSET + b * vsd_len, |
284 | 1.54k | sizeof(*vsd)); |
285 | 1.54k | if (!vsd) |
286 | 0 | return errno ? -errno : 1; |
287 | 1.54k | if (vsd->id[0] == '\0') |
288 | 167 | break; |
289 | 1.37k | if (memcmp(vsd->id, "NSR02", 5) == 0 || |
290 | 1.24k | memcmp(vsd->id, "NSR03", 5) == 0) |
291 | 291 | goto anchor; |
292 | 1.08k | else if (memcmp(vsd->id, "BEA01", 5) != 0 && |
293 | 1.00k | memcmp(vsd->id, "BOOT2", 5) != 0 && |
294 | 971 | memcmp(vsd->id, "CD001", 5) != 0 && |
295 | 475 | memcmp(vsd->id, "CDW02", 5) != 0 && |
296 | 446 | memcmp(vsd->id, "TEA01", 5) != 0) |
297 | | /* ECMA-167 2/8.3.1: The volume recognition sequence is |
298 | | * terminated by the first sector which is not a valid |
299 | | * descriptor. |
300 | | * UDF-2.60 2.1.7: UDF 2.00 and lower revisions do not |
301 | | * have requirement that NSR descriptor is in Extended Area |
302 | | * (between BEA01 and TEA01) and that there is only one |
303 | | * Extended Area. So do not stop scanning after TEA01. */ |
304 | 399 | break; |
305 | 1.37k | } |
306 | | |
307 | 566 | if (s_off == 0 && vsd_len == 2048) |
308 | 284 | vsd_2048_valid = 0; |
309 | | |
310 | | /* NSR was not found, try with next block size */ |
311 | 566 | continue; |
312 | | |
313 | 580 | anchor: |
314 | 580 | if (s_off == 0 && vsd_len == 2048) |
315 | 467 | vsd_2048_valid = 1; |
316 | | |
317 | | /* Read Anchor Volume Descriptor (AVDP), detect block size */ |
318 | 580 | vd = (struct volume_descriptor *) |
319 | 580 | blkid_probe_get_buffer(pr, s_off + 256 * pbs[i], sizeof(*vd)); |
320 | 580 | if (!vd) |
321 | 0 | return errno ? -errno : 1; |
322 | | |
323 | | /* Check that we read correct sector and detected correct block size */ |
324 | 580 | if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 256) { |
325 | 70 | type = le16_to_cpu(vd->tag.id); |
326 | 70 | if (type == TAG_ID_AVDP) |
327 | 67 | goto real_blksz; |
328 | 70 | } |
329 | | |
330 | | /* UDF-2.60: 2.2.3: Unclosed sequential Write-Once media may |
331 | | * have a single AVDP present at either sector 256 or 512. */ |
332 | 513 | vd = (struct volume_descriptor *) |
333 | 513 | blkid_probe_get_buffer(pr, s_off + 512 * pbs[i], sizeof(*vd)); |
334 | 513 | if (!vd) |
335 | 0 | return errno ? -errno : 1; |
336 | | |
337 | 513 | if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 512) { |
338 | 6 | type = le16_to_cpu(vd->tag.id); |
339 | 6 | if (type == TAG_ID_AVDP) |
340 | 0 | goto real_blksz; |
341 | 6 | } |
342 | | |
343 | 513 | } |
344 | 395 | return 1; |
345 | | |
346 | 67 | real_blksz: |
347 | | /* At this stage we detected ISO/IEC 13346 or ECMA-167 filesystem recognition sequence, it does not have to be UDF */ |
348 | | |
349 | | /* Use the actual block size from here on out */ |
350 | 67 | bs = pbs[i]; |
351 | | |
352 | | /* get descriptor list address and block count */ |
353 | 67 | count = le32_to_cpu(vd->type.anchor.length) / bs; |
354 | 67 | loc = le32_to_cpu(vd->type.anchor.location); |
355 | | |
356 | | /* pick the primary descriptor from the list and read UDF identifiers */ |
357 | 93 | for (b = 0; b < count; b++) { |
358 | 68 | vd = (struct volume_descriptor *) |
359 | 68 | blkid_probe_get_buffer(pr, |
360 | 68 | (uint64_t) (loc + b) * bs, |
361 | 68 | sizeof(*vd)); |
362 | 68 | if (!vd) |
363 | 3 | return errno ? -errno : 1; |
364 | 65 | type = le16_to_cpu(vd->tag.id); |
365 | 65 | if (type == 0) |
366 | 3 | break; |
367 | 62 | if (le32_to_cpu(vd->tag.location) != loc + b) |
368 | 36 | break; |
369 | 26 | if (type == TAG_ID_TD) |
370 | 0 | break; |
371 | 26 | if (type == TAG_ID_PVD) { |
372 | 9 | if (!have_volid && is_charset_udf(vd->type.primary.desc_charset)) { |
373 | 0 | int enc = udf_cid_to_enc(vd->type.primary.ident.cid); |
374 | 0 | uint8_t clen = vd->type.primary.ident.clen; |
375 | 0 | if (clen > 0) |
376 | 0 | --clen; |
377 | 0 | if (clen > sizeof(vd->type.primary.ident.c)) |
378 | 0 | clen = sizeof(vd->type.primary.ident.c); |
379 | 0 | if (enc != -1) |
380 | 0 | have_volid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_ID", |
381 | 0 | vd->type.primary.ident.c, clen, enc); |
382 | 0 | } |
383 | 9 | if (!have_uuid && is_charset_udf(vd->type.primary.desc_charset)) { |
384 | | /* VolumeSetIdentifier in UDF 2.01 specification: |
385 | | * ================================================================================= |
386 | | * 2.2.2.5 dstring VolumeSetIdentifier |
387 | | * |
388 | | * Interpreted as specifying the identifier for the volume set. |
389 | | * |
390 | | * The first 16 characters of this field should be set to a unique value. The |
391 | | * remainder of the field may be set to any allowed value. Specifically, software |
392 | | * generating volumes conforming to this specification shall not set this field to a |
393 | | * fixed or trivial value. Duplicate disks which are intended to be identical may |
394 | | * contain the same value in this field. |
395 | | * |
396 | | * NOTE: The intended purpose of this is to guarantee Volume Sets with unique |
397 | | * identifiers. The first 8 characters of the unique part should come from a CS0 |
398 | | * hexadecimal representation of a 32-bit time value. The remaining 8 characters |
399 | | * are free for implementation use. |
400 | | * ================================================================================= |
401 | | * |
402 | | * Implementation in libblkid: |
403 | | * The first 16 (Unicode) characters of VolumeSetIdentifier are encoded to UTF-8 |
404 | | * and then first 16 UTF-8 bytes are used to generate UUID. If all 16 bytes are |
405 | | * hexadecimal digits then their lowercase variants are used as UUID. If one of |
406 | | * the first 8 bytes (time value) is not hexadecimal digit then first 8 bytes are |
407 | | * encoded to their hexadecimal representations, resulting in 16 characters and |
408 | | * set as UUID. If all first 8 bytes (time value) are hexadecimal digits but some |
409 | | * remaining not then lowercase variant of the first 8 bytes are used as first |
410 | | * part of UUID and next 4 bytes encoded in hexadecimal representations (resulting |
411 | | * in 8 characters) are used as second part of UUID string. |
412 | | */ |
413 | 0 | unsigned char uuid[17]; |
414 | 0 | if (gen_uuid_from_volset_id(uuid, &vd->type.primary.volset_id) == 0) |
415 | 0 | have_uuid = !blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid)); |
416 | 0 | } |
417 | 9 | if (!have_volsetid && is_charset_udf(vd->type.primary.desc_charset)) { |
418 | 0 | int enc = udf_cid_to_enc(vd->type.primary.volset_id.cid); |
419 | 0 | uint8_t clen = vd->type.primary.volset_id.clen; |
420 | 0 | if (clen > 0) |
421 | 0 | --clen; |
422 | 0 | if (clen > sizeof(vd->type.primary.volset_id.c)) |
423 | 0 | clen = sizeof(vd->type.primary.volset_id.c); |
424 | 0 | if (enc != -1) |
425 | 0 | have_volsetid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", |
426 | 0 | vd->type.primary.volset_id.c, clen, enc); |
427 | 0 | } |
428 | 9 | if (!have_applicationid) { |
429 | | /* UDF-2.60: 2.2.2.9: This field specifies a valid Entity Identifier identifying the application that last wrote this field */ |
430 | 9 | const unsigned char *app_id = (const unsigned char *)vd->type.primary.app_id; |
431 | 9 | size_t app_id_len = strnlen(vd->type.primary.app_id, sizeof(vd->type.primary.app_id)); |
432 | 9 | if (app_id_len > 0 && app_id[0] == '*') { |
433 | 0 | app_id++; |
434 | 0 | app_id_len--; |
435 | 0 | } |
436 | | /* When Application Identifier is not set then use Developer ID from Implementation Identifier */ |
437 | 9 | if (app_id_len == 0) { |
438 | | /* UDF-2.60: 2.1.5.2: "*Developer ID" refers to an Entity Identifier that uniquely identifies the current implementation */ |
439 | 6 | app_id = (const unsigned char *)vd->type.primary.imp_id; |
440 | 6 | app_id_len = strnlen(vd->type.primary.imp_id, sizeof(vd->type.primary.imp_id)); |
441 | 6 | if (app_id_len > 0 && app_id[0] == '*') { |
442 | 0 | app_id++; |
443 | 0 | app_id_len--; |
444 | 0 | } |
445 | 6 | } |
446 | 9 | if (app_id_len > 0) { |
447 | | /* UDF-2.60: 2.1.5.2: Values used by UDF for this field are specified in terms of ASCII character strings */ |
448 | 6 | have_applicationid = !blkid_probe_set_id_label(pr, "APPLICATION_ID", app_id, app_id_len); |
449 | 6 | } |
450 | 9 | } |
451 | 17 | } else if (type == TAG_ID_LVD) { |
452 | 0 | if (!lvid_len || !lvid_loc) { |
453 | 0 | uint32_t num_partition_maps = le32_to_cpu(vd->type.logical.num_partition_maps); |
454 | | /* ECMA-167 3/10.6.12: If num_partition_maps is 0, then no LVID is specified */ |
455 | 0 | if (num_partition_maps) { |
456 | 0 | lvid_len = le32_to_cpu(vd->type.logical.lvid_length); |
457 | 0 | lvid_loc = le32_to_cpu(vd->type.logical.lvid_location); |
458 | 0 | } |
459 | 0 | } |
460 | 0 | if (!is_udf || !udf_rev) { |
461 | | /* UDF-2.60: 2.2.4.3: This field shall indicate that the contents of |
462 | | * this logical volume conforms to the domain defined in this document. |
463 | | * This distinguish UDF from all other ISO/IEC 13346 and ECMA-167 filesystems. */ |
464 | 0 | if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0) { |
465 | 0 | is_udf = 1; |
466 | | /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document |
467 | | * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */ |
468 | 0 | if (!udf_rev) |
469 | 0 | udf_rev = le16_to_cpu(vd->type.logical.udf_rev); |
470 | 0 | } |
471 | 0 | } |
472 | 0 | if ((!have_logvolid || !have_label) && is_charset_udf(vd->type.logical.desc_charset)) { |
473 | | /* LogicalVolumeIdentifier in UDF 2.01 specification: |
474 | | * =============================================================== |
475 | | * 2. Basic Restrictions & Requirements |
476 | | * |
477 | | * Logical Volume Descriptor |
478 | | * |
479 | | * There shall be exactly one prevailing Logical Volume |
480 | | * Descriptor recorded per Volume Set. |
481 | | * |
482 | | * The LogicalVolumeIdentifier field shall not be null and |
483 | | * should contain an identifier that aids in the identification of |
484 | | * the logical volume. Specifically, software generating |
485 | | * volumes conforming to this specification shall not set this |
486 | | * field to a fixed or trivial value. Duplicate disks, which are |
487 | | * intended to be identical, may contain the same value in this |
488 | | * field. This field is extremely important in logical volume |
489 | | * identification when multiple media are present within a |
490 | | * jukebox. This name is typically what is displayed to the user. |
491 | | * =============================================================== |
492 | | * |
493 | | * Implementation in libblkid: |
494 | | * The LogicalVolumeIdentifier field is used for LABEL. MS Windows |
495 | | * read Volume Label also from LogicalVolumeIdentifier. Grub2 read |
496 | | * LABEL also from this field. Program newfs_udf (from UDFclient) |
497 | | * when formatting disk set this field from user option Disc Name. |
498 | | */ |
499 | 0 | int enc = udf_cid_to_enc(vd->type.logical.logvol_id.cid); |
500 | 0 | uint8_t clen = vd->type.logical.logvol_id.clen; |
501 | 0 | if (clen > 0) |
502 | 0 | --clen; |
503 | 0 | if (clen > sizeof(vd->type.logical.logvol_id.c)) |
504 | 0 | clen = sizeof(vd->type.logical.logvol_id.c); |
505 | 0 | if (enc != -1) { |
506 | 0 | if (!have_label) |
507 | 0 | have_label = !blkid_probe_set_utf8label(pr, |
508 | 0 | vd->type.logical.logvol_id.c, clen, enc); |
509 | 0 | if (!have_logvolid) |
510 | 0 | have_logvolid = !blkid_probe_set_utf8_id_label(pr, "LOGICAL_VOLUME_ID", |
511 | 0 | vd->type.logical.logvol_id.c, clen, enc); |
512 | 0 | } |
513 | 0 | } |
514 | 17 | } else if (type == TAG_ID_IUVD) { |
515 | 3 | if (!have_publisherid && strncmp(vd->type.imp_use_volume.lvi_id, "*UDF LV Info", sizeof(vd->type.imp_use_volume.lvi_id)) == 0 && is_charset_udf(vd->type.imp_use_volume.lvi_charset)) { |
516 | | /* UDF-2.60: 2.2.7.2.3: Field LVInfo1 could contain information such as Owner Name |
517 | | * More UDF generating tools set this field to person who creating the filesystem |
518 | | * therefore its meaning is similar to ISO9660 Publisher Identifier. So for |
519 | | * compatibility with iso9660 superblock code export this field via PUBLISHER_ID. |
520 | | */ |
521 | 0 | int enc = udf_cid_to_enc(vd->type.imp_use_volume.lvinfo1.cid); |
522 | 0 | uint8_t clen = vd->type.imp_use_volume.lvinfo1.clen; |
523 | 0 | if (clen > 0) |
524 | 0 | --clen; |
525 | 0 | if (clen > sizeof(vd->type.imp_use_volume.lvinfo1.c)) |
526 | 0 | clen = sizeof(vd->type.imp_use_volume.lvinfo1.c); |
527 | 0 | if (enc != -1) |
528 | 0 | have_publisherid = !blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", |
529 | 0 | vd->type.imp_use_volume.lvinfo1.c, clen, enc); |
530 | 0 | } |
531 | 3 | } |
532 | 26 | if (is_udf && have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid) |
533 | 0 | break; |
534 | 26 | } |
535 | | |
536 | 64 | if (!is_udf) { |
537 | | /* We detected some other ISO/IEC 13346 or ECMA-167 filesystem, not UDF */ |
538 | 64 | return 1; |
539 | 64 | } |
540 | | |
541 | | /* Pick the first logical volume integrity descriptor and read UDF revision */ |
542 | 0 | if (lvid_loc && lvid_len >= sizeof(*vd)) { |
543 | 0 | vd = (struct volume_descriptor *) |
544 | 0 | blkid_probe_get_buffer(pr, |
545 | 0 | (uint64_t) lvid_loc * bs, |
546 | 0 | sizeof(*vd)); |
547 | 0 | if (!vd) |
548 | 0 | return errno ? -errno : 1; |
549 | 0 | type = le16_to_cpu(vd->tag.id); |
550 | 0 | if (type == TAG_ID_LVID && |
551 | 0 | le32_to_cpu(vd->tag.location) == lvid_loc && |
552 | 0 | UDF_LVIDIU_LENGTH(*vd) >= sizeof(*lvidiu)) { |
553 | | /* ECMA-167 3/8.8.2: There is stored sequence of LVIDs and valid is just last |
554 | | * one. So correctly we should jump to next_lvid_location and read next LVID |
555 | | * until we find last one. This could be time consuming process and could |
556 | | * lead to scanning lot of disk blocks. Because we use LVID only for UDF |
557 | | * version, in the worst case we would report only wrong ID_FS_VERSION. */ |
558 | 0 | uint16_t lvidiu_udf_rev; |
559 | 0 | lvidiu = (struct logical_vol_integ_descriptor_imp_use *) |
560 | 0 | blkid_probe_get_buffer(pr, |
561 | 0 | (uint64_t) lvid_loc * bs + UDF_LVIDIU_OFFSET(*vd), |
562 | 0 | sizeof(*lvidiu)); |
563 | 0 | if (!lvidiu) |
564 | 0 | return errno ? -errno : 1; |
565 | | /* UDF-2.60: 2. Basic Restrictions & Requirements: |
566 | | * The Minimum UDF Read Revision value shall be at most #0250 |
567 | | * for all media with a UDF 2.60 file system. |
568 | | * Because some 2.60 implementations put 2.50 into both LVIDIU |
569 | | * fields and 2.60 into LVD, use maximal value from LVD, |
570 | | * Minimum UDF Read Revision and Minimum UDF Write Revision for |
571 | | * ID_FS_VERSION to distinguish between UDF 2.50 and UDF 2.60 discs. */ |
572 | 0 | lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_read_rev); |
573 | 0 | if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev) |
574 | 0 | udf_rev = lvidiu_udf_rev; |
575 | 0 | lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_write_rev); |
576 | 0 | if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev) |
577 | 0 | udf_rev = lvidiu_udf_rev; |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | 0 | if (udf_rev) |
582 | | /* UDF revision is stored as decimal number in hexadecimal format. |
583 | | * E.g. number 0x0150 is revision 1.50, number 0x0201 is revision 2.01. */ |
584 | 0 | blkid_probe_sprintf_version(pr, "%x.%02x", (unsigned int)(udf_rev >> 8), (unsigned int)(udf_rev & 0xFF)); |
585 | |
|
586 | 0 | blkid_probe_set_fsblocksize(pr, bs); |
587 | 0 | blkid_probe_set_block_size(pr, bs); |
588 | |
|
589 | 0 | return 0; |
590 | 0 | } |
591 | | |
592 | | |
593 | | const struct blkid_idinfo udf_idinfo = |
594 | | { |
595 | | .name = "udf", |
596 | | .usage = BLKID_USAGE_FILESYSTEM, |
597 | | .probefunc = probe_udf, |
598 | | .flags = BLKID_IDINFO_TOLERANT, |
599 | | .magics = |
600 | | { |
601 | | /* These magics are generic to all ISO/IEC 13346 and ECMA-167 filesystems, not just UDF */ |
602 | | { .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
603 | | { .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
604 | | { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
605 | | { .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
606 | | { .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
607 | | { .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
608 | | { .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, |
609 | | { NULL } |
610 | | } |
611 | | }; |