2587456336e5e5033cd1542e3a16ddae0648c83d
[grub.git] / grub-core / fs / udf.c
1 /* udf.c - Universal Disk Format filesystem.  */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 2008,2009  Free Software Foundation, Inc.
5  *
6  *  GRUB is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  GRUB is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27 #include <grub/fshelp.h>
28 #include <grub/charset.h>
29 #include <grub/datetime.h>
30 #include <grub/udf.h>
31
32 GRUB_MOD_LICENSE ("GPLv3+");
33
34 #define GRUB_UDF_MAX_PDS                2
35 #define GRUB_UDF_MAX_PMS                6
36
37 #define U16                             grub_le_to_cpu16
38 #define U32                             grub_le_to_cpu32
39 #define U64                             grub_le_to_cpu64
40
41 #define GRUB_UDF_TAG_IDENT_PVD          0x0001
42 #define GRUB_UDF_TAG_IDENT_AVDP         0x0002
43 #define GRUB_UDF_TAG_IDENT_VDP          0x0003
44 #define GRUB_UDF_TAG_IDENT_IUVD         0x0004
45 #define GRUB_UDF_TAG_IDENT_PD           0x0005
46 #define GRUB_UDF_TAG_IDENT_LVD          0x0006
47 #define GRUB_UDF_TAG_IDENT_USD          0x0007
48 #define GRUB_UDF_TAG_IDENT_TD           0x0008
49 #define GRUB_UDF_TAG_IDENT_LVID         0x0009
50
51 #define GRUB_UDF_TAG_IDENT_FSD          0x0100
52 #define GRUB_UDF_TAG_IDENT_FID          0x0101
53 #define GRUB_UDF_TAG_IDENT_AED          0x0102
54 #define GRUB_UDF_TAG_IDENT_IE           0x0103
55 #define GRUB_UDF_TAG_IDENT_TE           0x0104
56 #define GRUB_UDF_TAG_IDENT_FE           0x0105
57 #define GRUB_UDF_TAG_IDENT_EAHD         0x0106
58 #define GRUB_UDF_TAG_IDENT_USE          0x0107
59 #define GRUB_UDF_TAG_IDENT_SBD          0x0108
60 #define GRUB_UDF_TAG_IDENT_PIE          0x0109
61 #define GRUB_UDF_TAG_IDENT_EFE          0x010A
62
63 #define GRUB_UDF_ICBTAG_TYPE_UNDEF      0x00
64 #define GRUB_UDF_ICBTAG_TYPE_USE        0x01
65 #define GRUB_UDF_ICBTAG_TYPE_PIE        0x02
66 #define GRUB_UDF_ICBTAG_TYPE_IE         0x03
67 #define GRUB_UDF_ICBTAG_TYPE_DIRECTORY  0x04
68 #define GRUB_UDF_ICBTAG_TYPE_REGULAR    0x05
69 #define GRUB_UDF_ICBTAG_TYPE_BLOCK      0x06
70 #define GRUB_UDF_ICBTAG_TYPE_CHAR       0x07
71 #define GRUB_UDF_ICBTAG_TYPE_EA         0x08
72 #define GRUB_UDF_ICBTAG_TYPE_FIFO       0x09
73 #define GRUB_UDF_ICBTAG_TYPE_SOCKET     0x0A
74 #define GRUB_UDF_ICBTAG_TYPE_TE         0x0B
75 #define GRUB_UDF_ICBTAG_TYPE_SYMLINK    0x0C
76 #define GRUB_UDF_ICBTAG_TYPE_STREAMDIR  0x0D
77
78 #define GRUB_UDF_ICBTAG_FLAG_AD_MASK    0x0007
79 #define GRUB_UDF_ICBTAG_FLAG_AD_SHORT   0x0000
80 #define GRUB_UDF_ICBTAG_FLAG_AD_LONG    0x0001
81 #define GRUB_UDF_ICBTAG_FLAG_AD_EXT     0x0002
82 #define GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB  0x0003
83
84 #define GRUB_UDF_EXT_NORMAL             0x00000000
85 #define GRUB_UDF_EXT_NREC_ALLOC         0x40000000
86 #define GRUB_UDF_EXT_NREC_NALLOC        0x80000000
87 #define GRUB_UDF_EXT_MASK               0xC0000000
88
89 #define GRUB_UDF_FID_CHAR_HIDDEN        0x01
90 #define GRUB_UDF_FID_CHAR_DIRECTORY     0x02
91 #define GRUB_UDF_FID_CHAR_DELETED       0x04
92 #define GRUB_UDF_FID_CHAR_PARENT        0x08
93 #define GRUB_UDF_FID_CHAR_METADATA      0x10
94
95 #define GRUB_UDF_STD_IDENT_BEA01        "BEA01"
96 #define GRUB_UDF_STD_IDENT_BOOT2        "BOOT2"
97 #define GRUB_UDF_STD_IDENT_CD001        "CD001"
98 #define GRUB_UDF_STD_IDENT_CDW02        "CDW02"
99 #define GRUB_UDF_STD_IDENT_NSR02        "NSR02"
100 #define GRUB_UDF_STD_IDENT_NSR03        "NSR03"
101 #define GRUB_UDF_STD_IDENT_TEA01        "TEA01"
102
103 #define GRUB_UDF_CHARSPEC_TYPE_CS0      0x00
104 #define GRUB_UDF_CHARSPEC_TYPE_CS1      0x01
105 #define GRUB_UDF_CHARSPEC_TYPE_CS2      0x02
106 #define GRUB_UDF_CHARSPEC_TYPE_CS3      0x03
107 #define GRUB_UDF_CHARSPEC_TYPE_CS4      0x04
108 #define GRUB_UDF_CHARSPEC_TYPE_CS5      0x05
109 #define GRUB_UDF_CHARSPEC_TYPE_CS6      0x06
110 #define GRUB_UDF_CHARSPEC_TYPE_CS7      0x07
111 #define GRUB_UDF_CHARSPEC_TYPE_CS8      0x08
112
113 #define GRUB_UDF_PARTMAP_TYPE_1         1
114 #define GRUB_UDF_PARTMAP_TYPE_2         2
115
116 struct grub_udf_lb_addr
117 {
118   grub_uint32_t block_num;
119   grub_uint16_t part_ref;
120 } GRUB_PACKED;
121
122 struct grub_udf_short_ad
123 {
124   grub_uint32_t length;
125   grub_uint32_t position;
126 } GRUB_PACKED;
127
128 struct grub_udf_long_ad
129 {
130   grub_uint32_t length;
131   struct grub_udf_lb_addr block;
132   grub_uint8_t imp_use[6];
133 } GRUB_PACKED;
134
135 struct grub_udf_extent_ad
136 {
137   grub_uint32_t length;
138   grub_uint32_t start;
139 } GRUB_PACKED;
140
141 struct grub_udf_charspec
142 {
143   grub_uint8_t charset_type;
144   grub_uint8_t charset_info[63];
145 } GRUB_PACKED;
146
147 struct grub_udf_timestamp
148 {
149   grub_uint16_t type_and_timezone;
150   grub_uint16_t year;
151   grub_uint8_t month;
152   grub_uint8_t day;
153   grub_uint8_t hour;
154   grub_uint8_t minute;
155   grub_uint8_t second;
156   grub_uint8_t centi_seconds;
157   grub_uint8_t hundreds_of_micro_seconds;
158   grub_uint8_t micro_seconds;
159 } GRUB_PACKED;
160
161 struct grub_udf_regid
162 {
163   grub_uint8_t flags;
164   grub_uint8_t ident[23];
165   grub_uint8_t ident_suffix[8];
166 } GRUB_PACKED;
167
168 struct grub_udf_tag
169 {
170   grub_uint16_t tag_ident;
171   grub_uint16_t desc_version;
172   grub_uint8_t tag_checksum;
173   grub_uint8_t reserved;
174   grub_uint16_t tag_serial_number;
175   grub_uint16_t desc_crc;
176   grub_uint16_t desc_crc_length;
177   grub_uint32_t tag_location;
178 } GRUB_PACKED;
179
180 struct grub_udf_fileset
181 {
182   struct grub_udf_tag tag;
183   struct grub_udf_timestamp datetime;
184   grub_uint16_t interchange_level;
185   grub_uint16_t max_interchange_level;
186   grub_uint32_t charset_list;
187   grub_uint32_t max_charset_list;
188   grub_uint32_t fileset_num;
189   grub_uint32_t fileset_desc_num;
190   struct grub_udf_charspec vol_charset;
191   grub_uint8_t vol_ident[128];
192   struct grub_udf_charspec fileset_charset;
193   grub_uint8_t fileset_ident[32];
194   grub_uint8_t copyright_file_ident[32];
195   grub_uint8_t abstract_file_ident[32];
196   struct grub_udf_long_ad root_icb;
197   struct grub_udf_regid domain_ident;
198   struct grub_udf_long_ad next_ext;
199   struct grub_udf_long_ad streamdir_icb;
200 } GRUB_PACKED;
201
202 struct grub_udf_icbtag
203 {
204   grub_uint32_t prior_recorded_num_direct_entries;
205   grub_uint16_t strategy_type;
206   grub_uint16_t strategy_parameter;
207   grub_uint16_t num_entries;
208   grub_uint8_t reserved;
209   grub_uint8_t file_type;
210   struct grub_udf_lb_addr parent_idb;
211   grub_uint16_t flags;
212 } GRUB_PACKED;
213
214 struct grub_udf_file_ident
215 {
216   struct grub_udf_tag tag;
217   grub_uint16_t version_num;
218   grub_uint8_t characteristics;
219 #define MAX_FILE_IDENT_LENGTH 256
220   grub_uint8_t file_ident_length;
221   struct grub_udf_long_ad icb;
222   grub_uint16_t imp_use_length;
223 } GRUB_PACKED;
224
225 struct grub_udf_file_entry
226 {
227   struct grub_udf_tag tag;
228   struct grub_udf_icbtag icbtag;
229   grub_uint32_t uid;
230   grub_uint32_t gid;
231   grub_uint32_t permissions;
232   grub_uint16_t link_count;
233   grub_uint8_t record_format;
234   grub_uint8_t record_display_attr;
235   grub_uint32_t record_length;
236   grub_uint64_t file_size;
237   grub_uint64_t blocks_recorded;
238   struct grub_udf_timestamp access_time;
239   struct grub_udf_timestamp modification_time;
240   struct grub_udf_timestamp attr_time;
241   grub_uint32_t checkpoint;
242   struct grub_udf_long_ad extended_attr_idb;
243   struct grub_udf_regid imp_ident;
244   grub_uint64_t unique_id;
245   grub_uint32_t ext_attr_length;
246   grub_uint32_t alloc_descs_length;
247   grub_uint8_t ext_attr[0];
248 } GRUB_PACKED;
249
250 struct grub_udf_extended_file_entry
251 {
252   struct grub_udf_tag tag;
253   struct grub_udf_icbtag icbtag;
254   grub_uint32_t uid;
255   grub_uint32_t gid;
256   grub_uint32_t permissions;
257   grub_uint16_t link_count;
258   grub_uint8_t record_format;
259   grub_uint8_t record_display_attr;
260   grub_uint32_t record_length;
261   grub_uint64_t file_size;
262   grub_uint64_t object_size;
263   grub_uint64_t blocks_recorded;
264   struct grub_udf_timestamp access_time;
265   struct grub_udf_timestamp modification_time;
266   struct grub_udf_timestamp create_time;
267   struct grub_udf_timestamp attr_time;
268   grub_uint32_t checkpoint;
269   grub_uint32_t reserved;
270   struct grub_udf_long_ad extended_attr_icb;
271   struct grub_udf_long_ad streamdir_icb;
272   struct grub_udf_regid imp_ident;
273   grub_uint64_t unique_id;
274   grub_uint32_t ext_attr_length;
275   grub_uint32_t alloc_descs_length;
276   grub_uint8_t ext_attr[0];
277 } GRUB_PACKED;
278
279 struct grub_udf_vrs
280 {
281   grub_uint8_t type;
282   grub_uint8_t magic[5];
283   grub_uint8_t version;
284 } GRUB_PACKED;
285
286 struct grub_udf_avdp
287 {
288   struct grub_udf_tag tag;
289   struct grub_udf_extent_ad vds;
290 } GRUB_PACKED;
291
292 struct grub_udf_pd
293 {
294   struct grub_udf_tag tag;
295   grub_uint32_t seq_num;
296   grub_uint16_t flags;
297   grub_uint16_t part_num;
298   struct grub_udf_regid contents;
299   grub_uint8_t contents_use[128];
300   grub_uint32_t access_type;
301   grub_uint32_t start;
302   grub_uint32_t length;
303 } GRUB_PACKED;
304
305 struct grub_udf_partmap
306 {
307   grub_uint8_t type;
308   grub_uint8_t length;
309   union
310   {
311     struct
312     {
313       grub_uint16_t seq_num;
314       grub_uint16_t part_num;
315     } type1;
316
317     struct
318     {
319       grub_uint8_t ident[62];
320     } type2;
321   };
322 } GRUB_PACKED;
323
324 struct grub_udf_lvd
325 {
326   struct grub_udf_tag tag;
327   grub_uint32_t seq_num;
328   struct grub_udf_charspec charset;
329   grub_uint8_t ident[128];
330   grub_uint32_t bsize;
331   struct grub_udf_regid domain_ident;
332   struct grub_udf_long_ad root_fileset;
333   grub_uint32_t map_table_length;
334   grub_uint32_t num_part_maps;
335   struct grub_udf_regid imp_ident;
336   grub_uint8_t imp_use[128];
337   struct grub_udf_extent_ad integrity_seq_ext;
338   grub_uint8_t part_maps[1608];
339 } GRUB_PACKED;
340
341 struct grub_udf_aed
342 {
343   struct grub_udf_tag tag;
344   grub_uint32_t prev_ae;
345   grub_uint32_t ae_len;
346 } GRUB_PACKED;
347
348 struct grub_udf_data
349 {
350   grub_disk_t disk;
351   struct grub_udf_lvd lvd;
352   struct grub_udf_pd pds[GRUB_UDF_MAX_PDS];
353   struct grub_udf_partmap *pms[GRUB_UDF_MAX_PMS];
354   struct grub_udf_long_ad root_icb;
355   int npd, npm, lbshift;
356 };
357
358 struct grub_fshelp_node
359 {
360   struct grub_udf_data *data;
361   int part_ref;
362   union
363   {
364     struct grub_udf_file_entry fe;
365     struct grub_udf_extended_file_entry efe;
366     char raw[0];
367   } block;
368 };
369
370 static inline grub_size_t
371 get_fshelp_size (struct grub_udf_data *data)
372 {
373   struct grub_fshelp_node *x = NULL;
374   return sizeof (*x)
375     + (1 << (GRUB_DISK_SECTOR_BITS
376              + data->lbshift)) - sizeof (x->block);
377 }
378
379 static grub_dl_t my_mod;
380
381 static grub_uint32_t
382 grub_udf_get_block (struct grub_udf_data *data,
383                     grub_uint16_t part_ref, grub_uint32_t block)
384 {
385   part_ref = U16 (part_ref);
386
387   if (part_ref >= data->npm)
388     {
389       grub_error (GRUB_ERR_BAD_FS, "invalid part ref");
390       return 0;
391     }
392
393   return (U32 (data->pds[data->pms[part_ref]->type1.part_num].start)
394           + U32 (block));
395 }
396
397 static grub_err_t
398 grub_udf_read_icb (struct grub_udf_data *data,
399                    struct grub_udf_long_ad *icb,
400                    struct grub_fshelp_node *node)
401 {
402   grub_uint32_t block;
403
404   block = grub_udf_get_block (data,
405                               icb->block.part_ref,
406                               icb->block.block_num);
407
408   if (grub_errno)
409     return grub_errno;
410
411   if (grub_disk_read (data->disk, block << data->lbshift, 0,
412                       1 << (GRUB_DISK_SECTOR_BITS
413                             + data->lbshift),
414                       &node->block))
415     return grub_errno;
416
417   if ((U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FE) &&
418       (U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_EFE))
419     return grub_error (GRUB_ERR_BAD_FS, "invalid fe/efe descriptor");
420
421   node->part_ref = icb->block.part_ref;
422   node->data = data;
423   return 0;
424 }
425
426 static grub_disk_addr_t
427 grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
428 {
429   char *buf = NULL;
430   char *ptr;
431   grub_ssize_t len;
432   grub_disk_addr_t filebytes;
433
434   switch (U16 (node->block.fe.tag.tag_ident))
435     {
436     case GRUB_UDF_TAG_IDENT_FE:
437       ptr = (char *) &node->block.fe.ext_attr[0] + U32 (node->block.fe.ext_attr_length);
438       len = U32 (node->block.fe.alloc_descs_length);
439       break;
440
441     case GRUB_UDF_TAG_IDENT_EFE:
442       ptr = (char *) &node->block.efe.ext_attr[0] + U32 (node->block.efe.ext_attr_length);
443       len = U32 (node->block.efe.alloc_descs_length);
444       break;
445
446     default:
447       grub_error (GRUB_ERR_BAD_FS, "invalid file entry");
448       return 0;
449     }
450
451   if ((U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
452       == GRUB_UDF_ICBTAG_FLAG_AD_SHORT)
453     {
454       struct grub_udf_short_ad *ad = (struct grub_udf_short_ad *) ptr;
455
456       filebytes = fileblock * U32 (node->data->lvd.bsize);
457       while (len >= (grub_ssize_t) sizeof (struct grub_udf_short_ad))
458         {
459           grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff;
460           grub_uint32_t adtype = U32 (ad->length) >> 30;
461           if (adtype == 3)
462             {
463               struct grub_udf_aed *extension;
464               grub_disk_addr_t sec = grub_udf_get_block(node->data,
465                                                         node->part_ref,
466                                                         ad->position);
467               if (!buf)
468                 {
469                   buf = grub_malloc (U32 (node->data->lvd.bsize));
470                   if (!buf)
471                     return 0;
472                 }
473               if (grub_disk_read (node->data->disk, sec << node->data->lbshift,
474                                   0, adlen, buf))
475                 goto fail;
476
477               extension = (struct grub_udf_aed *) buf;
478               if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED)
479                 {
480                   grub_error (GRUB_ERR_BAD_FS, "invalid aed tag");
481                   goto fail;
482                 }
483
484               len = U32 (extension->ae_len);
485               ad = (struct grub_udf_short_ad *)
486                     (buf + sizeof (struct grub_udf_aed));
487               continue;
488             }
489
490           if (filebytes < adlen)
491             {
492               grub_uint32_t ad_pos = ad->position;
493               grub_free (buf);
494               return ((U32 (ad_pos) & GRUB_UDF_EXT_MASK) ? 0 :
495                       (grub_udf_get_block (node->data, node->part_ref, ad_pos)
496                        + (filebytes >> (GRUB_DISK_SECTOR_BITS
497                                         + node->data->lbshift))));
498             }
499
500           filebytes -= adlen;
501           ad++;
502           len -= sizeof (struct grub_udf_short_ad);
503         }
504     }
505   else
506     {
507       struct grub_udf_long_ad *ad = (struct grub_udf_long_ad *) ptr;
508
509       filebytes = fileblock * U32 (node->data->lvd.bsize);
510       while (len >= (grub_ssize_t) sizeof (struct grub_udf_long_ad))
511         {
512           grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff;
513           grub_uint32_t adtype = U32 (ad->length) >> 30;
514           if (adtype == 3)
515             {
516               struct grub_udf_aed *extension;
517               grub_disk_addr_t sec = grub_udf_get_block(node->data,
518                                                         ad->block.part_ref,
519                                                         ad->block.block_num);
520               if (!buf)
521                 {
522                   buf = grub_malloc (U32 (node->data->lvd.bsize));
523                   if (!buf)
524                     return 0;
525                 }
526               if (grub_disk_read (node->data->disk, sec << node->data->lbshift,
527                                   0, adlen, buf))
528                 goto fail;
529
530               extension = (struct grub_udf_aed *) buf;
531               if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED)
532                 {
533                   grub_error (GRUB_ERR_BAD_FS, "invalid aed tag");
534                   goto fail;
535                 }
536
537               len = U32 (extension->ae_len);
538               ad = (struct grub_udf_long_ad *)
539                     (buf + sizeof (struct grub_udf_aed));
540               continue;
541             }
542               
543           if (filebytes < adlen)
544             {
545               grub_uint32_t ad_block_num = ad->block.block_num;
546               grub_uint32_t ad_part_ref = ad->block.part_ref;
547               grub_free (buf);
548               return ((U32 (ad_block_num) & GRUB_UDF_EXT_MASK) ?  0 :
549                       (grub_udf_get_block (node->data, ad_part_ref,
550                                            ad_block_num)
551                        + (filebytes >> (GRUB_DISK_SECTOR_BITS
552                                         + node->data->lbshift))));
553             }
554
555           filebytes -= adlen;
556           ad++;
557           len -= sizeof (struct grub_udf_long_ad);
558         }
559     }
560
561 fail:
562   grub_free (buf);
563
564   return 0;
565 }
566
567 static grub_ssize_t
568 grub_udf_read_file (grub_fshelp_node_t node,
569                     grub_disk_read_hook_t read_hook, void *read_hook_data,
570                     grub_off_t pos, grub_size_t len, char *buf)
571 {
572   switch (U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
573     {
574     case GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB:
575       {
576         char *ptr;
577
578         ptr = ((U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) ?
579                ((char *) &node->block.fe.ext_attr[0]
580                 + U32 (node->block.fe.ext_attr_length)) :
581                ((char *) &node->block.efe.ext_attr[0]
582                 + U32 (node->block.efe.ext_attr_length)));
583
584         grub_memcpy (buf, ptr + pos, len);
585
586         return len;
587       }
588
589     case GRUB_UDF_ICBTAG_FLAG_AD_EXT:
590       grub_error (GRUB_ERR_BAD_FS, "invalid extent type");
591       return 0;
592     }
593
594   return grub_fshelp_read_file (node->data->disk, node,
595                                 read_hook, read_hook_data,
596                                 pos, len, buf, grub_udf_read_block,
597                                 U64 (node->block.fe.file_size),
598                                 node->data->lbshift, 0);
599 }
600
601 static unsigned sblocklist[] = { 256, 512, 0 };
602
603 static struct grub_udf_data *
604 grub_udf_mount (grub_disk_t disk)
605 {
606   struct grub_udf_data *data = 0;
607   struct grub_udf_fileset root_fs;
608   unsigned *sblklist;
609   grub_uint32_t block, vblock;
610   int i, lbshift;
611
612   data = grub_malloc (sizeof (struct grub_udf_data));
613   if (!data)
614     return 0;
615
616   data->disk = disk;
617
618   /* Search for Anchor Volume Descriptor Pointer (AVDP)
619    * and determine logical block size.  */
620   block = 0;
621   for (lbshift = 0; lbshift < 4; lbshift++)
622     {
623       for (sblklist = sblocklist; *sblklist; sblklist++)
624         {
625           struct grub_udf_avdp avdp;
626
627           if (grub_disk_read (disk, *sblklist << lbshift, 0,
628                               sizeof (struct grub_udf_avdp), &avdp))
629             {
630               grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
631               goto fail;
632             }
633
634           if (U16 (avdp.tag.tag_ident) == GRUB_UDF_TAG_IDENT_AVDP &&
635               U32 (avdp.tag.tag_location) == *sblklist)
636             {
637               block = U32 (avdp.vds.start);
638               break;
639             }
640         }
641
642       if (block)
643         break;
644     }
645
646   if (!block)
647     {
648       grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
649       goto fail;
650     }
651   data->lbshift = lbshift;
652
653   /* Search for Volume Recognition Sequence (VRS).  */
654   for (vblock = (32767 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1;;
655        vblock += (2047 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1)
656     {
657       struct grub_udf_vrs vrs;
658
659       if (grub_disk_read (disk, vblock << lbshift, 0,
660                           sizeof (struct grub_udf_vrs), &vrs))
661         {
662           grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
663           goto fail;
664         }
665
666       if ((!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR03, 5)) ||
667           (!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR02, 5)))
668         break;
669
670       if ((grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BEA01, 5)) &&
671           (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BOOT2, 5)) &&
672           (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CD001, 5)) &&
673           (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CDW02, 5)) &&
674           (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_TEA01, 5)))
675         {
676           grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
677           goto fail;
678         }
679     }
680
681   data->npd = data->npm = 0;
682   /* Locate Partition Descriptor (PD) and Logical Volume Descriptor (LVD).  */
683   while (1)
684     {
685       struct grub_udf_tag tag;
686
687       if (grub_disk_read (disk, block << lbshift, 0,
688                           sizeof (struct grub_udf_tag), &tag))
689         {
690           grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
691           goto fail;
692         }
693
694       tag.tag_ident = U16 (tag.tag_ident);
695       if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PD)
696         {
697           if (data->npd >= GRUB_UDF_MAX_PDS)
698             {
699               grub_error (GRUB_ERR_BAD_FS, "too many PDs");
700               goto fail;
701             }
702
703           if (grub_disk_read (disk, block << lbshift, 0,
704                               sizeof (struct grub_udf_pd),
705                               &data->pds[data->npd]))
706             {
707               grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
708               goto fail;
709             }
710
711           data->npd++;
712         }
713       else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_LVD)
714         {
715           int k;
716
717           struct grub_udf_partmap *ppm;
718
719           if (grub_disk_read (disk, block << lbshift, 0,
720                               sizeof (struct grub_udf_lvd),
721                               &data->lvd))
722             {
723               grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
724               goto fail;
725             }
726
727           if (data->npm + U32 (data->lvd.num_part_maps) > GRUB_UDF_MAX_PMS)
728             {
729               grub_error (GRUB_ERR_BAD_FS, "too many partition maps");
730               goto fail;
731             }
732
733           ppm = (struct grub_udf_partmap *) &data->lvd.part_maps;
734           for (k = U32 (data->lvd.num_part_maps); k > 0; k--)
735             {
736               if (ppm->type != GRUB_UDF_PARTMAP_TYPE_1)
737                 {
738                   grub_error (GRUB_ERR_BAD_FS, "partmap type not supported");
739                   goto fail;
740                 }
741
742               data->pms[data->npm++] = ppm;
743               ppm = (struct grub_udf_partmap *) ((char *) ppm +
744                                                  U32 (ppm->length));
745             }
746         }
747       else if (tag.tag_ident > GRUB_UDF_TAG_IDENT_TD)
748         {
749           grub_error (GRUB_ERR_BAD_FS, "invalid tag ident");
750           goto fail;
751         }
752       else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_TD)
753         break;
754
755       block++;
756     }
757
758   for (i = 0; i < data->npm; i++)
759     {
760       int j;
761
762       for (j = 0; j < data->npd; j++)
763         if (data->pms[i]->type1.part_num == data->pds[j].part_num)
764           {
765             data->pms[i]->type1.part_num = j;
766             break;
767           }
768
769       if (j == data->npd)
770         {
771           grub_error (GRUB_ERR_BAD_FS, "can\'t find PD");
772           goto fail;
773         }
774     }
775
776   block = grub_udf_get_block (data,
777                               data->lvd.root_fileset.block.part_ref,
778                               data->lvd.root_fileset.block.block_num);
779
780   if (grub_errno)
781     goto fail;
782
783   if (grub_disk_read (disk, block << lbshift, 0,
784                       sizeof (struct grub_udf_fileset), &root_fs))
785     {
786       grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
787       goto fail;
788     }
789
790   if (U16 (root_fs.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FSD)
791     {
792       grub_error (GRUB_ERR_BAD_FS, "invalid fileset descriptor");
793       goto fail;
794     }
795
796   data->root_icb = root_fs.root_icb;
797
798   return data;
799
800 fail:
801   grub_free (data);
802   return 0;
803 }
804
805 #ifdef GRUB_UTIL
806 grub_disk_addr_t
807 grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
808 {
809   grub_disk_addr_t ret;
810   static struct grub_udf_data *data;
811
812   data = grub_udf_mount (disk);
813   if (!data)
814     return 0;
815
816   ret = U32 (data->pds[data->pms[0]->type1.part_num].start);
817   *sec_per_lcn = 1ULL << data->lbshift;
818   grub_free (data);
819   return ret;
820 }
821 #endif
822
823 static char *
824 read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
825 {
826   grub_uint16_t *utf16 = NULL;
827   grub_size_t utf16len = 0;
828
829   if (sz == 0)
830     return NULL;
831
832   if (raw[0] != 8 && raw[0] != 16)
833     return NULL;
834
835   if (raw[0] == 8)
836     {
837       unsigned i;
838       utf16len = sz - 1;
839       utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
840       if (!utf16)
841         return NULL;
842       for (i = 0; i < utf16len; i++)
843         utf16[i] = raw[i + 1];
844     }
845   if (raw[0] == 16)
846     {
847       unsigned i;
848       utf16len = (sz - 1) / 2;
849       utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
850       if (!utf16)
851         return NULL;
852       for (i = 0; i < utf16len; i++)
853         utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
854     }
855   if (!outbuf)
856     outbuf = grub_malloc (utf16len * GRUB_MAX_UTF8_PER_UTF16 + 1);
857   if (outbuf)
858     *grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0';
859   grub_free (utf16);
860   return outbuf;
861 }
862
863 static char *
864 read_dstring (const grub_uint8_t *raw, grub_size_t sz)
865 {
866   grub_size_t len;
867
868   if (raw[0] == 0) {
869       char *outbuf = grub_malloc (1);
870       if (!outbuf)
871         return NULL;
872       outbuf[0] = 0;
873       return outbuf;
874     }
875
876   len = raw[sz - 1];
877   if (len > sz - 1)
878     len = sz - 1;
879   return read_string (raw, len, NULL);
880 }
881
882 static int
883 grub_udf_iterate_dir (grub_fshelp_node_t dir,
884                       grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
885 {
886   grub_fshelp_node_t child;
887   struct grub_udf_file_ident dirent;
888   grub_off_t offset = 0;
889
890   child = grub_malloc (get_fshelp_size (dir->data));
891   if (!child)
892     return 0;
893
894   /* The current directory is not stored.  */
895   grub_memcpy (child, dir, get_fshelp_size (dir->data));
896
897   if (hook (".", GRUB_FSHELP_DIR, child, hook_data))
898     return 1;
899
900   while (offset < U64 (dir->block.fe.file_size))
901     {
902       if (grub_udf_read_file (dir, 0, 0, offset, sizeof (dirent),
903                               (char *) &dirent) != sizeof (dirent))
904         return 0;
905
906       if (U16 (dirent.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FID)
907         {
908           grub_error (GRUB_ERR_BAD_FS, "invalid fid tag");
909           return 0;
910         }
911
912       offset += sizeof (dirent) + U16 (dirent.imp_use_length);
913       if (!(dirent.characteristics & GRUB_UDF_FID_CHAR_DELETED))
914         {
915           child = grub_malloc (get_fshelp_size (dir->data));
916           if (!child)
917             return 0;
918
919           if (grub_udf_read_icb (dir->data, &dirent.icb, child))
920             return 0;
921
922           if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT)
923             {
924               /* This is the parent directory.  */
925               if (hook ("..", GRUB_FSHELP_DIR, child, hook_data))
926                 return 1;
927             }
928           else
929             {
930               enum grub_fshelp_filetype type;
931               char *filename;
932               grub_uint8_t raw[MAX_FILE_IDENT_LENGTH];
933
934               type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ?
935                       (GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG));
936               if (child->block.fe.icbtag.file_type == GRUB_UDF_ICBTAG_TYPE_SYMLINK)
937                 type = GRUB_FSHELP_SYMLINK;
938
939               if ((grub_udf_read_file (dir, 0, 0, offset,
940                                        dirent.file_ident_length,
941                                        (char *) raw))
942                   != dirent.file_ident_length)
943                 return 0;
944
945               filename = read_string (raw, dirent.file_ident_length, 0);
946               if (!filename)
947                 grub_print_error ();
948
949               if (filename && hook (filename, type, child, hook_data))
950                 {
951                   grub_free (filename);
952                   return 1;
953                 }
954               grub_free (filename);
955             }
956         }
957
958       /* Align to dword boundary.  */
959       offset = (offset + dirent.file_ident_length + 3) & (~3);
960     }
961
962   return 0;
963 }
964
965 static char *
966 grub_udf_read_symlink (grub_fshelp_node_t node)
967 {
968   grub_size_t sz = U64 (node->block.fe.file_size);
969   grub_uint8_t *raw;
970   const grub_uint8_t *ptr;
971   char *out, *optr;
972
973   if (sz < 4)
974     return NULL;
975   raw = grub_malloc (sz);
976   if (!raw)
977     return NULL;
978   if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0)
979     {
980       grub_free (raw);
981       return NULL;
982     }
983
984   out = grub_malloc (sz * 2 + 1);
985   if (!out)
986     {
987       grub_free (raw);
988       return NULL;
989     }
990
991   optr = out;
992
993   for (ptr = raw; ptr < raw + sz; )
994     {
995       grub_size_t s;
996       if ((grub_size_t) (ptr - raw + 4) > sz)
997         goto fail;
998       if (!(ptr[2] == 0 && ptr[3] == 0))
999         goto fail;
1000       s = 4 + ptr[1];
1001       if ((grub_size_t) (ptr - raw + s) > sz)
1002         goto fail;
1003       switch (*ptr)
1004         {
1005         case 1:
1006           if (ptr[1])
1007             goto fail;
1008           /* Fallthrough.  */
1009         case 2:
1010           /* in 4 bytes. out: 1 byte.  */
1011           optr = out;
1012           *optr++ = '/';
1013           break;
1014         case 3:
1015           /* in 4 bytes. out: 3 bytes.  */
1016           if (optr != out)
1017             *optr++ = '/';
1018           *optr++ = '.';
1019           *optr++ = '.';
1020           break;
1021         case 4:
1022           /* in 4 bytes. out: 2 bytes.  */
1023           if (optr != out)
1024             *optr++ = '/';
1025           *optr++ = '.';
1026           break;
1027         case 5:
1028           /* in 4 + n bytes. out, at most: 1 + 2 * n bytes.  */
1029           if (optr != out)
1030             *optr++ = '/';
1031           if (!read_string (ptr + 4, s - 4, optr))
1032             goto fail;
1033           optr += grub_strlen (optr);
1034           break;
1035         default:
1036           goto fail;
1037         }
1038       ptr += s;
1039     }
1040   *optr = 0;
1041   grub_free (raw);
1042   return out;
1043
1044  fail:
1045   grub_free (raw);
1046   grub_free (out);
1047   grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
1048   return NULL;
1049 }
1050
1051 /* Context for grub_udf_dir.  */
1052 struct grub_udf_dir_ctx
1053 {
1054   grub_fs_dir_hook_t hook;
1055   void *hook_data;
1056 };
1057
1058 /* Helper for grub_udf_dir.  */
1059 static int
1060 grub_udf_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
1061                    grub_fshelp_node_t node, void *data)
1062 {
1063   struct grub_udf_dir_ctx *ctx = data;
1064   struct grub_dirhook_info info;
1065   const struct grub_udf_timestamp *tstamp = NULL;
1066
1067   grub_memset (&info, 0, sizeof (info));
1068   info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
1069   if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
1070     tstamp = &node->block.fe.modification_time;
1071   else if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE)
1072     tstamp = &node->block.efe.modification_time;
1073
1074   if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000)
1075     {
1076       grub_int16_t tz;
1077       struct grub_datetime datetime;
1078
1079       datetime.year = U16 (tstamp->year);
1080       datetime.month = tstamp->month;
1081       datetime.day = tstamp->day;
1082       datetime.hour = tstamp->hour;
1083       datetime.minute = tstamp->minute;
1084       datetime.second = tstamp->second;
1085
1086       tz = U16 (tstamp->type_and_timezone) & 0xfff;
1087       if (tz & 0x800)
1088         tz |= 0xf000;
1089       if (tz == -2047)
1090         tz = 0;
1091
1092       info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime);
1093
1094       info.mtime -= 60 * tz;
1095     }
1096   grub_free (node);
1097   return ctx->hook (filename, &info, ctx->hook_data);
1098 }
1099
1100 static grub_err_t
1101 grub_udf_dir (grub_device_t device, const char *path,
1102               grub_fs_dir_hook_t hook, void *hook_data)
1103 {
1104   struct grub_udf_dir_ctx ctx = { hook, hook_data };
1105   struct grub_udf_data *data = 0;
1106   struct grub_fshelp_node *rootnode = 0;
1107   struct grub_fshelp_node *foundnode = 0;
1108
1109   grub_dl_ref (my_mod);
1110
1111   data = grub_udf_mount (device->disk);
1112   if (!data)
1113     goto fail;
1114
1115   rootnode = grub_malloc (get_fshelp_size (data));
1116   if (!rootnode)
1117     goto fail;
1118
1119   if (grub_udf_read_icb (data, &data->root_icb, rootnode))
1120     goto fail;
1121
1122   if (grub_fshelp_find_file (path, rootnode,
1123                              &foundnode,
1124                              grub_udf_iterate_dir, grub_udf_read_symlink,
1125                              GRUB_FSHELP_DIR))
1126     goto fail;
1127
1128   grub_udf_iterate_dir (foundnode, grub_udf_dir_iter, &ctx);
1129
1130   if (foundnode != rootnode)
1131     grub_free (foundnode);
1132
1133 fail:
1134   grub_free (rootnode);
1135
1136   grub_free (data);
1137
1138   grub_dl_unref (my_mod);
1139
1140   return grub_errno;
1141 }
1142
1143 static grub_err_t
1144 grub_udf_open (struct grub_file *file, const char *name)
1145 {
1146   struct grub_udf_data *data;
1147   struct grub_fshelp_node *rootnode = 0;
1148   struct grub_fshelp_node *foundnode;
1149
1150   grub_dl_ref (my_mod);
1151
1152   data = grub_udf_mount (file->device->disk);
1153   if (!data)
1154     goto fail;
1155
1156   rootnode = grub_malloc (get_fshelp_size (data));
1157   if (!rootnode)
1158     goto fail;
1159
1160   if (grub_udf_read_icb (data, &data->root_icb, rootnode))
1161     goto fail;
1162
1163   if (grub_fshelp_find_file (name, rootnode,
1164                              &foundnode,
1165                              grub_udf_iterate_dir, grub_udf_read_symlink,
1166                              GRUB_FSHELP_REG))
1167     goto fail;
1168
1169   file->data = foundnode;
1170   file->offset = 0;
1171   file->size = U64 (foundnode->block.fe.file_size);
1172
1173   grub_free (rootnode);
1174
1175   return 0;
1176
1177 fail:
1178   grub_dl_unref (my_mod);
1179
1180   grub_free (data);
1181   grub_free (rootnode);
1182
1183   return grub_errno;
1184 }
1185
1186 static grub_ssize_t
1187 grub_udf_read (grub_file_t file, char *buf, grub_size_t len)
1188 {
1189   struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;
1190
1191   return grub_udf_read_file (node, file->read_hook, file->read_hook_data,
1192                              file->offset, len, buf);
1193 }
1194
1195 static grub_err_t
1196 grub_udf_close (grub_file_t file)
1197 {
1198   if (file->data)
1199     {
1200       struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;
1201
1202       grub_free (node->data);
1203       grub_free (node);
1204     }
1205
1206   grub_dl_unref (my_mod);
1207
1208   return GRUB_ERR_NONE;
1209 }
1210
1211 static grub_err_t
1212 grub_udf_label (grub_device_t device, char **label)
1213 {
1214   struct grub_udf_data *data;
1215   data = grub_udf_mount (device->disk);
1216
1217   if (data)
1218     {
1219       *label = read_dstring (data->lvd.ident, sizeof (data->lvd.ident));
1220       grub_free (data);
1221     }
1222   else
1223     *label = 0;
1224
1225   return grub_errno;
1226 }
1227
1228 static struct grub_fs grub_udf_fs = {
1229   .name = "udf",
1230   .dir = grub_udf_dir,
1231   .open = grub_udf_open,
1232   .read = grub_udf_read,
1233   .close = grub_udf_close,
1234   .label = grub_udf_label,
1235 #ifdef GRUB_UTIL
1236   .reserved_first_sector = 1,
1237   .blocklist_install = 1,
1238 #endif
1239   .next = 0
1240 };
1241
1242 GRUB_MOD_INIT (udf)
1243 {
1244   grub_fs_register (&grub_udf_fs);
1245   my_mod = mod;
1246 }
1247
1248 GRUB_MOD_FINI (udf)
1249 {
1250   grub_fs_unregister (&grub_udf_fs);
1251 }