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 #define is_charset_udf(charspec) ((charspec).type == 0 && strncmp((charspec).info, "OSTA Compressed Unicode", sizeof((charspec).info)) == 0)
23
24 #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 #define TAG_ID_PVD 1
141 #define TAG_ID_AVDP 2
142 #define TAG_ID_IUVD 4
143 #define TAG_ID_LVD 6
144 #define TAG_ID_TD 8
145 #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 #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 #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 #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 {
170 int enc;
171 size_t i;
172 size_t len;
173 size_t clen;
174 size_t nonhexpos;
175 unsigned char buf[17];
176
177 memset(buf, 0, sizeof(buf));
178
179 clen = volset_id->clen;
180 if (clen > 0)
181 --clen;
182 if (clen > sizeof(volset_id->c))
183 clen = sizeof(volset_id->c);
184
185 enc = udf_cid_to_enc(volset_id->cid);
186 if (enc == -1)
187 return -1;
188
189 len = ul_encode_to_utf8(enc, buf, sizeof(buf), volset_id->c, clen);
190 if (len < 8)
191 return -1;
192
193 nonhexpos = 16;
194 for (i = 0; i < 16; ++i) {
195 if (!isxdigit(buf[i])) {
196 nonhexpos = i;
197 break;
198 }
199 }
200
201 if (nonhexpos < 8) {
202 snprintf((char *) uuid, 17, "%02x%02x%02x%02x%02x%02x%02x%02x",
203 buf[0], buf[1], buf[2], buf[3],
204 buf[4], buf[5], buf[6], buf[7]);
205 } else if (nonhexpos < 16) {
206 for (i = 0; i < 8; ++i)
207 uuid[i] = tolower(buf[i]);
208 snprintf((char *) uuid + 8, 9, "%02x%02x%02x%02x",
209 buf[8], buf[9], buf[10], buf[11]);
210 } else {
211 for (i = 0; i < 16; ++i)
212 uuid[i] = tolower(buf[i]);
213 uuid[16] = 0;
214 }
215
216 return 0;
217 }
218
219 static int probe_udf(blkid_probe pr,
220 const struct blkid_idmag *mag __attribute__((__unused__)))
221 {
222 struct volume_descriptor *vd;
223 struct volume_structure_descriptor *vsd;
224 struct logical_vol_integ_descriptor_imp_use *lvidiu;
225 uint32_t lvid_len = 0;
226 uint32_t lvid_loc = 0;
227 uint64_t s_off;
228 uint32_t bs;
229 uint32_t b;
230 uint16_t type;
231 uint32_t count;
232 uint32_t loc;
233 size_t i;
234 uint32_t vsd_len;
235 uint16_t udf_rev = 0;
236 int vsd_2048_valid = -1;
237 int have_label = 0;
238 int have_uuid = 0;
239 int have_logvolid = 0;
240 int have_volid = 0;
241 int have_volsetid = 0;
242 int have_applicationid = 0;
243 int have_publisherid = 0;
244
245 /* Session offset */
246 if (blkid_probe_get_hint(pr, "session_offset", &s_off) < 0)
247 s_off = 0;
248
249 /* The block size of a UDF filesystem is that of the underlying
250 * storage; we check later on for the special case of image files,
251 * which may have any block size valid for UDF filesystem */
252 uint32_t pbs[] = { 0, 512, 1024, 2048, 4096 };
253 pbs[0] = blkid_probe_get_sectorsize(pr);
254
255 for (i = 0; i < ARRAY_SIZE(pbs); i++) {
256 /* Do not try with block size same as sector size two times */
257 if (i != 0 && pbs[0] == pbs[i])
258 continue;
259
260 /* Do not try with block size which is not divisor of session offset */
261 if (s_off % pbs[i])
262 continue;
263
264 /* ECMA-167 2/8.4, 2/9.1: Each VSD is either 2048 bytes long or
265 * its size is same as blocksize (for blocksize > 2048 bytes)
266 * plus padded with zeros */
267 vsd_len = pbs[i] > 2048 ? pbs[i] : 2048;
268
269 /* Process 2048 bytes long VSD on first session only once
270 * as its location is same for any blocksize */
271 if (s_off == 0 && vsd_len == 2048) {
272 if (vsd_2048_valid == 0)
273 continue;
274 if (vsd_2048_valid == 1)
275 goto anchor;
276 }
277
278 /* Check for a Volume Structure Descriptor (VSD) */
279 for (b = 0; b < 64; b++) {
280 vsd = (struct volume_structure_descriptor *)
281 blkid_probe_get_buffer(pr,
282 s_off + UDF_VSD_OFFSET + b * vsd_len,
283 sizeof(*vsd));
284 if (!vsd)
285 return errno ? -errno : 1;
286 if (vsd->id[0] == '\0')
287 break;
288 if (memcmp(vsd->id, "NSR02", 5) == 0 ||
289 memcmp(vsd->id, "NSR03", 5) == 0)
290 goto anchor;
291 else if (memcmp(vsd->id, "BEA01", 5) != 0 &&
292 memcmp(vsd->id, "BOOT2", 5) != 0 &&
293 memcmp(vsd->id, "CD001", 5) != 0 &&
294 memcmp(vsd->id, "CDW02", 5) != 0 &&
295 memcmp(vsd->id, "TEA01", 5) != 0)
296 /* ECMA-167 2/8.3.1: The volume recognition sequence is
297 * terminated by the first sector which is not a valid
298 * descriptor.
299 * UDF-2.60 2.1.7: UDF 2.00 and lower revisions do not
300 * have requirement that NSR descriptor is in Extended Area
301 * (between BEA01 and TEA01) and that there is only one
302 * Extended Area. So do not stop scanning after TEA01. */
303 break;
304 }
305
306 if (s_off == 0 && vsd_len == 2048)
307 vsd_2048_valid = 0;
308
309 /* NSR was not found, try with next block size */
310 continue;
311
312 anchor:
313 if (s_off == 0 && vsd_len == 2048)
314 vsd_2048_valid = 1;
315
316 /* Read Anchor Volume Descriptor (AVDP), detect block size */
317 vd = (struct volume_descriptor *)
318 blkid_probe_get_buffer(pr, s_off + 256 * pbs[i], sizeof(*vd));
319 if (!vd)
320 return errno ? -errno : 1;
321
322 /* Check that we read correct sector and detected correct block size */
323 if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 256) {
324 type = le16_to_cpu(vd->tag.id);
325 if (type == TAG_ID_AVDP)
326 goto real_blksz;
327 }
328
329 /* UDF-2.60: 2.2.3: Unclosed sequential Write-Once media may
330 * have a single AVDP present at either sector 256 or 512. */
331 vd = (struct volume_descriptor *)
332 blkid_probe_get_buffer(pr, s_off + 512 * pbs[i], sizeof(*vd));
333 if (!vd)
334 return errno ? -errno : 1;
335
336 if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 512) {
337 type = le16_to_cpu(vd->tag.id);
338 if (type == TAG_ID_AVDP)
339 goto real_blksz;
340 }
341
342 }
343 return 1;
344
345 real_blksz:
346 /* Use the actual block size from here on out */
347 bs = pbs[i];
348
349 /* get descriptor list address and block count */
350 count = le32_to_cpu(vd->type.anchor.length) / bs;
351 loc = le32_to_cpu(vd->type.anchor.location);
352
353 /* pick the primary descriptor from the list and read UDF identifiers */
354 for (b = 0; b < count; b++) {
355 vd = (struct volume_descriptor *)
356 blkid_probe_get_buffer(pr,
357 (uint64_t) (loc + b) * bs,
358 sizeof(*vd));
359 if (!vd)
360 return errno ? -errno : 1;
361 type = le16_to_cpu(vd->tag.id);
362 if (type == 0)
363 break;
364 if (le32_to_cpu(vd->tag.location) != loc + b)
365 break;
366 if (type == TAG_ID_TD)
367 break;
368 if (type == TAG_ID_PVD) {
369 if (!have_volid && is_charset_udf(vd->type.primary.desc_charset)) {
370 int enc = udf_cid_to_enc(vd->type.primary.ident.cid);
371 uint8_t clen = vd->type.primary.ident.clen;
372 if (clen > 0)
373 --clen;
374 if (clen > sizeof(vd->type.primary.ident.c))
375 clen = sizeof(vd->type.primary.ident.c);
376 if (enc != -1)
377 have_volid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_ID",
378 vd->type.primary.ident.c, clen, enc);
379 }
380 if (!have_uuid && is_charset_udf(vd->type.primary.desc_charset)) {
381 /* VolumeSetIdentifier in UDF 2.01 specification:
382 * =================================================================================
383 * 2.2.2.5 dstring VolumeSetIdentifier
384 *
385 * Interpreted as specifying the identifier for the volume set.
386 *
387 * The first 16 characters of this field should be set to a unique value. The
388 * remainder of the field may be set to any allowed value. Specifically, software
389 * generating volumes conforming to this specification shall not set this field to a
390 * fixed or trivial value. Duplicate disks which are intended to be identical may
391 * contain the same value in this field.
392 *
393 * NOTE: The intended purpose of this is to guarantee Volume Sets with unique
394 * identifiers. The first 8 characters of the unique part should come from a CS0
395 * hexadecimal representation of a 32-bit time value. The remaining 8 characters
396 * are free for implementation use.
397 * =================================================================================
398 *
399 * Implementation in libblkid:
400 * The first 16 (Unicode) characters of VolumeSetIdentifier are encoded to UTF-8
401 * and then first 16 UTF-8 bytes are used to generate UUID. If all 16 bytes are
402 * hexadecimal digits then their lowercase variants are used as UUID. If one of
403 * the first 8 bytes (time value) is not hexadecimal digit then first 8 bytes are
404 * encoded to their hexadecimal representations, resulting in 16 characters and
405 * set as UUID. If all first 8 bytes (time value) are hexadecimal digits but some
406 * remaining not then lowercase variant of the first 8 bytes are used as first
407 * part of UUID and next 4 bytes encoded in hexadecimal representations (resulting
408 * in 8 characters) are used as second part of UUID string.
409 */
410 unsigned char uuid[17];
411 if (gen_uuid_from_volset_id(uuid, &vd->type.primary.volset_id) == 0)
412 have_uuid = !blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid));
413 }
414 if (!have_volsetid && is_charset_udf(vd->type.primary.desc_charset)) {
415 int enc = udf_cid_to_enc(vd->type.primary.volset_id.cid);
416 uint8_t clen = vd->type.primary.volset_id.clen;
417 if (clen > 0)
418 --clen;
419 if (clen > sizeof(vd->type.primary.volset_id.c))
420 clen = sizeof(vd->type.primary.volset_id.c);
421 if (enc != -1)
422 have_volsetid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID",
423 vd->type.primary.volset_id.c, clen, enc);
424 }
425 if (!have_applicationid) {
426 /* UDF-2.60: 2.2.2.9: This field specifies a valid Entity Identifier identifying the application that last wrote this field */
427 const unsigned char *app_id = (const unsigned char *)vd->type.primary.app_id;
428 size_t app_id_len = strnlen(vd->type.primary.app_id, sizeof(vd->type.primary.app_id));
429 if (app_id_len > 0 && app_id[0] == '*') {
430 app_id++;
431 app_id_len--;
432 }
433 /* When Application Identifier is not set then use Developer ID from Implementation Identifier */
434 if (app_id_len == 0) {
435 /* UDF-2.60: 2.1.5.2: "*Developer ID" refers to an Entity Identifier that uniquely identifies the current implementation */
436 app_id = (const unsigned char *)vd->type.primary.imp_id;
437 app_id_len = strnlen(vd->type.primary.imp_id, sizeof(vd->type.primary.imp_id));
438 if (app_id_len > 0 && app_id[0] == '*') {
439 app_id++;
440 app_id_len--;
441 }
442 }
443 if (app_id_len > 0) {
444 /* UDF-2.60: 2.1.5.2: Values used by UDF for this field are specified in terms of ASCII character strings */
445 have_applicationid = !blkid_probe_set_id_label(pr, "APPLICATION_ID", app_id, app_id_len);
446 }
447 }
448 } else if (type == TAG_ID_LVD) {
449 if (!lvid_len || !lvid_loc) {
450 uint32_t num_partition_maps = le32_to_cpu(vd->type.logical.num_partition_maps);
451 /* ECMA-167 3/10.6.12: If num_partition_maps is 0, then no LVID is specified */
452 if (num_partition_maps) {
453 lvid_len = le32_to_cpu(vd->type.logical.lvid_length);
454 lvid_loc = le32_to_cpu(vd->type.logical.lvid_location);
455 }
456 }
457 if (!udf_rev) {
458 /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document
459 * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */
460 if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0)
461 udf_rev = le16_to_cpu(vd->type.logical.udf_rev);
462 }
463 if ((!have_logvolid || !have_label) && is_charset_udf(vd->type.logical.desc_charset)) {
464 /* LogicalVolumeIdentifier in UDF 2.01 specification:
465 * ===============================================================
466 * 2. Basic Restrictions & Requirements
467 *
468 * Logical Volume Descriptor
469 *
470 * There shall be exactly one prevailing Logical Volume
471 * Descriptor recorded per Volume Set.
472 *
473 * The LogicalVolumeIdentifier field shall not be null and
474 * should contain an identifier that aids in the identification of
475 * the logical volume. Specifically, software generating
476 * volumes conforming to this specification shall not set this
477 * field to a fixed or trivial value. Duplicate disks, which are
478 * intended to be identical, may contain the same value in this
479 * field. This field is extremely important in logical volume
480 * identification when multiple media are present within a
481 * jukebox. This name is typically what is displayed to the user.
482 * ===============================================================
483 *
484 * Implementation in libblkid:
485 * The LogicalVolumeIdentifier field is used for LABEL. MS Windows
486 * read Volume Label also from LogicalVolumeIdentifier. Grub2 read
487 * LABEL also from this field. Program newfs_udf (from UDFclient)
488 * when formatting disk set this field from user option Disc Name.
489 */
490 int enc = udf_cid_to_enc(vd->type.logical.logvol_id.cid);
491 uint8_t clen = vd->type.logical.logvol_id.clen;
492 if (clen > 0)
493 --clen;
494 if (clen > sizeof(vd->type.logical.logvol_id.c))
495 clen = sizeof(vd->type.logical.logvol_id.c);
496 if (enc != -1) {
497 if (!have_label)
498 have_label = !blkid_probe_set_utf8label(pr,
499 vd->type.logical.logvol_id.c, clen, enc);
500 if (!have_logvolid)
501 have_logvolid = !blkid_probe_set_utf8_id_label(pr, "LOGICAL_VOLUME_ID",
502 vd->type.logical.logvol_id.c, clen, enc);
503 }
504 }
505 } else if (type == TAG_ID_IUVD) {
506 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)) {
507 /* UDF-2.60: 2.2.7.2.3: Field LVInfo1 could contain information such as Owner Name
508 * More UDF generating tools set this field to person who creating the filesystem
509 * therefore its meaning is similar to ISO9660 Publisher Identifier. So for
510 * compatibility with iso9660 superblock code export this field via PUBLISHER_ID.
511 */
512 int enc = udf_cid_to_enc(vd->type.imp_use_volume.lvinfo1.cid);
513 uint8_t clen = vd->type.imp_use_volume.lvinfo1.clen;
514 if (clen > 0)
515 --clen;
516 if (clen > sizeof(vd->type.imp_use_volume.lvinfo1.c))
517 clen = sizeof(vd->type.imp_use_volume.lvinfo1.c);
518 if (enc != -1)
519 have_publisherid = !blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID",
520 vd->type.imp_use_volume.lvinfo1.c, clen, enc);
521 }
522 }
523 if (have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid)
524 break;
525 }
526
527 /* Pick the first logical volume integrity descriptor and read UDF revision */
528 if (lvid_loc && lvid_len >= sizeof(*vd)) {
529 vd = (struct volume_descriptor *)
530 blkid_probe_get_buffer(pr,
531 (uint64_t) lvid_loc * bs,
532 sizeof(*vd));
533 if (!vd)
534 return errno ? -errno : 1;
535 type = le16_to_cpu(vd->tag.id);
536 if (type == TAG_ID_LVID &&
537 le32_to_cpu(vd->tag.location) == lvid_loc &&
538 UDF_LVIDIU_LENGTH(*vd) >= sizeof(*lvidiu)) {
539 /* ECMA-167 3/8.8.2: There is stored sequence of LVIDs and valid is just last
540 * one. So correctly we should jump to next_lvid_location and read next LVID
541 * until we find last one. This could be time consuming process and could
542 * lead to scanning lot of disk blocks. Because we use LVID only for UDF
543 * version, in the worst case we would report only wrong ID_FS_VERSION. */
544 uint16_t lvidiu_udf_rev;
545 lvidiu = (struct logical_vol_integ_descriptor_imp_use *)
546 blkid_probe_get_buffer(pr,
547 (uint64_t) lvid_loc * bs + UDF_LVIDIU_OFFSET(*vd),
548 sizeof(*lvidiu));
549 if (!lvidiu)
550 return errno ? -errno : 1;
551 /* UDF-2.60: 2. Basic Restrictions & Requirements:
552 * The Minimum UDF Read Revision value shall be at most #0250
553 * for all media with a UDF 2.60 file system.
554 * Because some 2.60 implementations put 2.50 into both LVIDIU
555 * fields and 2.60 into LVD, use maximal value from LVD,
556 * Minimum UDF Read Revision and Minimum UDF Write Revision for
557 * ID_FS_VERSION to distinguish between UDF 2.50 and UDF 2.60 discs. */
558 lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_read_rev);
559 if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev)
560 udf_rev = lvidiu_udf_rev;
561 lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_write_rev);
562 if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev)
563 udf_rev = lvidiu_udf_rev;
564 }
565 }
566
567 if (udf_rev)
568 /* UDF revision is stored as decimal number in hexadecimal format.
569 * E.g. number 0x0150 is revision 1.50, number 0x0201 is revision 2.01. */
570 blkid_probe_sprintf_version(pr, "%x.%02x", (unsigned int)(udf_rev >> 8), (unsigned int)(udf_rev & 0xFF));
571
572 blkid_probe_set_fsblocksize(pr, bs);
573 blkid_probe_set_block_size(pr, bs);
574
575 return 0;
576 }
577
578
579 const struct blkid_idinfo udf_idinfo =
580 {
581 .name = "udf",
582 .usage = BLKID_USAGE_FILESYSTEM,
583 .probefunc = probe_udf,
584 .flags = BLKID_IDINFO_TOLERANT,
585 .magics =
586 {
587 { .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
588 { .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
589 { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
590 { .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
591 { .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
592 { .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
593 { .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
594 { NULL }
595 }
596 };