udf.c 29.7 KB
Newer Older
1 2 3
/* udf.c - Universal Disk Format filesystem.  */
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2008,2009  Free Software Foundation, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/err.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/disk.h>
#include <grub/dl.h>
#include <grub/types.h>
#include <grub/fshelp.h>
28
#include <grub/charset.h>
29
#include <grub/datetime.h>
30
#include <grub/udf.h>
31

32 33
GRUB_MOD_LICENSE ("GPLv3+");

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
#define GRUB_UDF_MAX_PDS		2
#define GRUB_UDF_MAX_PMS		6

#define U16				grub_le_to_cpu16
#define U32				grub_le_to_cpu32
#define U64				grub_le_to_cpu64

#define GRUB_UDF_TAG_IDENT_PVD		0x0001
#define GRUB_UDF_TAG_IDENT_AVDP		0x0002
#define GRUB_UDF_TAG_IDENT_VDP		0x0003
#define GRUB_UDF_TAG_IDENT_IUVD		0x0004
#define GRUB_UDF_TAG_IDENT_PD		0x0005
#define GRUB_UDF_TAG_IDENT_LVD		0x0006
#define GRUB_UDF_TAG_IDENT_USD		0x0007
#define GRUB_UDF_TAG_IDENT_TD		0x0008
#define GRUB_UDF_TAG_IDENT_LVID		0x0009

#define GRUB_UDF_TAG_IDENT_FSD		0x0100
#define GRUB_UDF_TAG_IDENT_FID		0x0101
#define GRUB_UDF_TAG_IDENT_AED		0x0102
#define GRUB_UDF_TAG_IDENT_IE		0x0103
#define GRUB_UDF_TAG_IDENT_TE		0x0104
#define GRUB_UDF_TAG_IDENT_FE		0x0105
#define GRUB_UDF_TAG_IDENT_EAHD		0x0106
#define GRUB_UDF_TAG_IDENT_USE		0x0107
#define GRUB_UDF_TAG_IDENT_SBD		0x0108
#define GRUB_UDF_TAG_IDENT_PIE		0x0109
#define GRUB_UDF_TAG_IDENT_EFE		0x010A

#define GRUB_UDF_ICBTAG_TYPE_UNDEF	0x00
#define GRUB_UDF_ICBTAG_TYPE_USE	0x01
#define GRUB_UDF_ICBTAG_TYPE_PIE	0x02
#define GRUB_UDF_ICBTAG_TYPE_IE		0x03
#define GRUB_UDF_ICBTAG_TYPE_DIRECTORY	0x04
#define GRUB_UDF_ICBTAG_TYPE_REGULAR	0x05
#define GRUB_UDF_ICBTAG_TYPE_BLOCK	0x06
#define GRUB_UDF_ICBTAG_TYPE_CHAR	0x07
#define GRUB_UDF_ICBTAG_TYPE_EA		0x08
#define GRUB_UDF_ICBTAG_TYPE_FIFO	0x09
#define GRUB_UDF_ICBTAG_TYPE_SOCKET	0x0A
#define GRUB_UDF_ICBTAG_TYPE_TE		0x0B
#define GRUB_UDF_ICBTAG_TYPE_SYMLINK	0x0C
#define GRUB_UDF_ICBTAG_TYPE_STREAMDIR	0x0D

#define GRUB_UDF_ICBTAG_FLAG_AD_MASK	0x0007
#define GRUB_UDF_ICBTAG_FLAG_AD_SHORT	0x0000
#define GRUB_UDF_ICBTAG_FLAG_AD_LONG	0x0001
#define GRUB_UDF_ICBTAG_FLAG_AD_EXT	0x0002
#define GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB	0x0003

#define GRUB_UDF_EXT_NORMAL		0x00000000
#define GRUB_UDF_EXT_NREC_ALLOC		0x40000000
#define GRUB_UDF_EXT_NREC_NALLOC	0x80000000
#define GRUB_UDF_EXT_MASK		0xC0000000

#define GRUB_UDF_FID_CHAR_HIDDEN	0x01
#define GRUB_UDF_FID_CHAR_DIRECTORY	0x02
#define GRUB_UDF_FID_CHAR_DELETED	0x04
#define GRUB_UDF_FID_CHAR_PARENT	0x08
#define GRUB_UDF_FID_CHAR_METADATA	0x10

#define GRUB_UDF_STD_IDENT_BEA01	"BEA01"
#define GRUB_UDF_STD_IDENT_BOOT2	"BOOT2"
#define GRUB_UDF_STD_IDENT_CD001	"CD001"
#define GRUB_UDF_STD_IDENT_CDW02	"CDW02"
#define GRUB_UDF_STD_IDENT_NSR02	"NSR02"
#define GRUB_UDF_STD_IDENT_NSR03	"NSR03"
#define GRUB_UDF_STD_IDENT_TEA01	"TEA01"

#define GRUB_UDF_CHARSPEC_TYPE_CS0	0x00
#define GRUB_UDF_CHARSPEC_TYPE_CS1	0x01
#define GRUB_UDF_CHARSPEC_TYPE_CS2	0x02
#define GRUB_UDF_CHARSPEC_TYPE_CS3	0x03
#define GRUB_UDF_CHARSPEC_TYPE_CS4	0x04
#define GRUB_UDF_CHARSPEC_TYPE_CS5	0x05
#define GRUB_UDF_CHARSPEC_TYPE_CS6	0x06
#define GRUB_UDF_CHARSPEC_TYPE_CS7	0x07
#define GRUB_UDF_CHARSPEC_TYPE_CS8	0x08

#define GRUB_UDF_PARTMAP_TYPE_1		1
#define GRUB_UDF_PARTMAP_TYPE_2		2

struct grub_udf_lb_addr
{
  grub_uint32_t block_num;
  grub_uint16_t part_ref;
120
} GRUB_PACKED;
121 122 123 124 125

struct grub_udf_short_ad
{
  grub_uint32_t length;
  grub_uint32_t position;
126
} GRUB_PACKED;
127 128 129 130 131 132

struct grub_udf_long_ad
{
  grub_uint32_t length;
  struct grub_udf_lb_addr block;
  grub_uint8_t imp_use[6];
133
} GRUB_PACKED;
134 135 136 137 138

struct grub_udf_extent_ad
{
  grub_uint32_t length;
  grub_uint32_t start;
139
} GRUB_PACKED;
140 141 142 143 144

struct grub_udf_charspec
{
  grub_uint8_t charset_type;
  grub_uint8_t charset_info[63];
145
} GRUB_PACKED;
146 147 148 149 150 151 152 153 154 155 156 157 158

struct grub_udf_timestamp
{
  grub_uint16_t type_and_timezone;
  grub_uint16_t year;
  grub_uint8_t month;
  grub_uint8_t day;
  grub_uint8_t hour;
  grub_uint8_t minute;
  grub_uint8_t second;
  grub_uint8_t centi_seconds;
  grub_uint8_t hundreds_of_micro_seconds;
  grub_uint8_t micro_seconds;
159
} GRUB_PACKED;
160 161 162 163 164 165

struct grub_udf_regid
{
  grub_uint8_t flags;
  grub_uint8_t ident[23];
  grub_uint8_t ident_suffix[8];
166
} GRUB_PACKED;
167 168 169 170 171 172 173 174 175 176 177

struct grub_udf_tag
{
  grub_uint16_t tag_ident;
  grub_uint16_t desc_version;
  grub_uint8_t tag_checksum;
  grub_uint8_t reserved;
  grub_uint16_t tag_serial_number;
  grub_uint16_t desc_crc;
  grub_uint16_t desc_crc_length;
  grub_uint32_t tag_location;
178
} GRUB_PACKED;
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199

struct grub_udf_fileset
{
  struct grub_udf_tag tag;
  struct grub_udf_timestamp datetime;
  grub_uint16_t interchange_level;
  grub_uint16_t max_interchange_level;
  grub_uint32_t charset_list;
  grub_uint32_t max_charset_list;
  grub_uint32_t fileset_num;
  grub_uint32_t fileset_desc_num;
  struct grub_udf_charspec vol_charset;
  grub_uint8_t vol_ident[128];
  struct grub_udf_charspec fileset_charset;
  grub_uint8_t fileset_ident[32];
  grub_uint8_t copyright_file_ident[32];
  grub_uint8_t abstract_file_ident[32];
  struct grub_udf_long_ad root_icb;
  struct grub_udf_regid domain_ident;
  struct grub_udf_long_ad next_ext;
  struct grub_udf_long_ad streamdir_icb;
200
} GRUB_PACKED;
201 202 203 204 205 206 207 208 209 210 211

struct grub_udf_icbtag
{
  grub_uint32_t prior_recorded_num_direct_entries;
  grub_uint16_t strategy_type;
  grub_uint16_t strategy_parameter;
  grub_uint16_t num_entries;
  grub_uint8_t reserved;
  grub_uint8_t file_type;
  struct grub_udf_lb_addr parent_idb;
  grub_uint16_t flags;
212
} GRUB_PACKED;
213 214 215 216 217 218

struct grub_udf_file_ident
{
  struct grub_udf_tag tag;
  grub_uint16_t version_num;
  grub_uint8_t characteristics;
219
#define MAX_FILE_IDENT_LENGTH 256
220 221 222
  grub_uint8_t file_ident_length;
  struct grub_udf_long_ad icb;
  grub_uint16_t imp_use_length;
223
} GRUB_PACKED;
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

struct grub_udf_file_entry
{
  struct grub_udf_tag tag;
  struct grub_udf_icbtag icbtag;
  grub_uint32_t uid;
  grub_uint32_t gid;
  grub_uint32_t permissions;
  grub_uint16_t link_count;
  grub_uint8_t record_format;
  grub_uint8_t record_display_attr;
  grub_uint32_t record_length;
  grub_uint64_t file_size;
  grub_uint64_t blocks_recorded;
  struct grub_udf_timestamp access_time;
  struct grub_udf_timestamp modification_time;
  struct grub_udf_timestamp attr_time;
  grub_uint32_t checkpoint;
  struct grub_udf_long_ad extended_attr_idb;
  struct grub_udf_regid imp_ident;
  grub_uint64_t unique_id;
  grub_uint32_t ext_attr_length;
  grub_uint32_t alloc_descs_length;
247
  grub_uint8_t ext_attr[0];
248
} GRUB_PACKED;
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

struct grub_udf_extended_file_entry
{
  struct grub_udf_tag tag;
  struct grub_udf_icbtag icbtag;
  grub_uint32_t uid;
  grub_uint32_t gid;
  grub_uint32_t permissions;
  grub_uint16_t link_count;
  grub_uint8_t record_format;
  grub_uint8_t record_display_attr;
  grub_uint32_t record_length;
  grub_uint64_t file_size;
  grub_uint64_t object_size;
  grub_uint64_t blocks_recorded;
  struct grub_udf_timestamp access_time;
  struct grub_udf_timestamp modification_time;
  struct grub_udf_timestamp create_time;
  struct grub_udf_timestamp attr_time;
  grub_uint32_t checkpoint;
  grub_uint32_t reserved;
  struct grub_udf_long_ad extended_attr_icb;
  struct grub_udf_long_ad streamdir_icb;
  struct grub_udf_regid imp_ident;
  grub_uint64_t unique_id;
  grub_uint32_t ext_attr_length;
  grub_uint32_t alloc_descs_length;
276
  grub_uint8_t ext_attr[0];
277
} GRUB_PACKED;
278 279 280 281 282 283

struct grub_udf_vrs
{
  grub_uint8_t type;
  grub_uint8_t magic[5];
  grub_uint8_t version;
284
} GRUB_PACKED;
285 286 287 288 289

struct grub_udf_avdp
{
  struct grub_udf_tag tag;
  struct grub_udf_extent_ad vds;
290
} GRUB_PACKED;
291 292 293 294 295 296 297 298 299 300 301 302

struct grub_udf_pd
{
  struct grub_udf_tag tag;
  grub_uint32_t seq_num;
  grub_uint16_t flags;
  grub_uint16_t part_num;
  struct grub_udf_regid contents;
  grub_uint8_t contents_use[128];
  grub_uint32_t access_type;
  grub_uint32_t start;
  grub_uint32_t length;
303
} GRUB_PACKED;
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321

struct grub_udf_partmap
{
  grub_uint8_t type;
  grub_uint8_t length;
  union
  {
    struct
    {
      grub_uint16_t seq_num;
      grub_uint16_t part_num;
    } type1;

    struct
    {
      grub_uint8_t ident[62];
    } type2;
  };
322
} GRUB_PACKED;
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

struct grub_udf_lvd
{
  struct grub_udf_tag tag;
  grub_uint32_t seq_num;
  struct grub_udf_charspec charset;
  grub_uint8_t ident[128];
  grub_uint32_t bsize;
  struct grub_udf_regid domain_ident;
  struct grub_udf_long_ad root_fileset;
  grub_uint32_t map_table_length;
  grub_uint32_t num_part_maps;
  struct grub_udf_regid imp_ident;
  grub_uint8_t imp_use[128];
  struct grub_udf_extent_ad integrity_seq_ext;
  grub_uint8_t part_maps[1608];
339
} GRUB_PACKED;
340

341 342 343 344 345
struct grub_udf_aed
{
  struct grub_udf_tag tag;
  grub_uint32_t prev_ae;
  grub_uint32_t ae_len;
346
} GRUB_PACKED;
347

348 349 350 351 352 353 354
struct grub_udf_data
{
  grub_disk_t disk;
  struct grub_udf_lvd lvd;
  struct grub_udf_pd pds[GRUB_UDF_MAX_PDS];
  struct grub_udf_partmap *pms[GRUB_UDF_MAX_PMS];
  struct grub_udf_long_ad root_icb;
355
  int npd, npm, lbshift;
356 357 358 359 360
};

struct grub_fshelp_node
{
  struct grub_udf_data *data;
361
  int part_ref;
362 363 364 365
  union
  {
    struct grub_udf_file_entry fe;
    struct grub_udf_extended_file_entry efe;
366 367
    char raw[0];
  } block;
368 369
};

370 371 372 373 374 375 376 377 378
static inline grub_size_t
get_fshelp_size (struct grub_udf_data *data)
{
  struct grub_fshelp_node *x = NULL;
  return sizeof (*x)
    + (1 << (GRUB_DISK_SECTOR_BITS
	     + data->lbshift)) - sizeof (x->block);
}

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
static grub_dl_t my_mod;

static grub_uint32_t
grub_udf_get_block (struct grub_udf_data *data,
		    grub_uint16_t part_ref, grub_uint32_t block)
{
  part_ref = U16 (part_ref);

  if (part_ref >= data->npm)
    {
      grub_error (GRUB_ERR_BAD_FS, "invalid part ref");
      return 0;
    }

  return (U32 (data->pds[data->pms[part_ref]->type1.part_num].start)
          + U32 (block));
}

static grub_err_t
grub_udf_read_icb (struct grub_udf_data *data,
		   struct grub_udf_long_ad *icb,
		   struct grub_fshelp_node *node)
{
  grub_uint32_t block;

  block = grub_udf_get_block (data,
			      icb->block.part_ref,
                              icb->block.block_num);

  if (grub_errno)
    return grub_errno;

411
  if (grub_disk_read (data->disk, block << data->lbshift, 0,
412 413 414
		      1 << (GRUB_DISK_SECTOR_BITS
			    + data->lbshift),
		      &node->block))
415 416
    return grub_errno;

417 418
  if ((U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FE) &&
      (U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_EFE))
419 420 421 422 423 424 425
    return grub_error (GRUB_ERR_BAD_FS, "invalid fe/efe descriptor");

  node->part_ref = icb->block.part_ref;
  node->data = data;
  return 0;
}

426 427
static grub_disk_addr_t
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 438
      ptr = (char *) &node->block.fe.ext_attr[0] + U32 (node->block.fe.ext_attr_length);
      len = U32 (node->block.fe.alloc_descs_length);
439 440 441
      break;

    case GRUB_UDF_TAG_IDENT_EFE:
442 443
      ptr = (char *) &node->block.efe.ext_attr[0] + U32 (node->block.efe.ext_attr_length);
      len = U32 (node->block.efe.alloc_descs_length);
444 445 446 447 448
      break;

    default:
      grub_error (GRUB_ERR_BAD_FS, "invalid file entry");
      return 0;
449 450
    }

451
  if ((U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
452 453 454 455
      == GRUB_UDF_ICBTAG_FLAG_AD_SHORT)
    {
      struct grub_udf_short_ad *ad = (struct grub_udf_short_ad *) ptr;

456
      filebytes = fileblock * U32 (node->data->lvd.bsize);
457
      while (len >= (grub_ssize_t) sizeof (struct grub_udf_short_ad))
458
	{
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
	  grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff;
	  grub_uint32_t adtype = U32 (ad->length) >> 30;
	  if (adtype == 3)
	    {
	      struct grub_udf_aed *extension;
	      grub_disk_addr_t sec = grub_udf_get_block(node->data,
							node->part_ref,
							ad->position);
	      if (!buf)
		{
		  buf = grub_malloc (U32 (node->data->lvd.bsize));
		  if (!buf)
		    return 0;
		}
	      if (grub_disk_read (node->data->disk, sec << node->data->lbshift,
				  0, adlen, buf))
		goto fail;

	      extension = (struct grub_udf_aed *) buf;
	      if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED)
		{
		  grub_error (GRUB_ERR_BAD_FS, "invalid aed tag");
		  goto fail;
		}

	      len = U32 (extension->ae_len);
	      ad = (struct grub_udf_short_ad *)
		    (buf + sizeof (struct grub_udf_aed));
	      continue;
	    }

	  if (filebytes < adlen)
	    {
	      grub_uint32_t ad_pos = ad->position;
	      grub_free (buf);
	      return ((U32 (ad_pos) & GRUB_UDF_EXT_MASK) ? 0 :
		      (grub_udf_get_block (node->data, node->part_ref, ad_pos)
		       + (filebytes >> (GRUB_DISK_SECTOR_BITS
					+ node->data->lbshift))));
	    }

	  filebytes -= adlen;
501
	  ad++;
502
	  len -= sizeof (struct grub_udf_short_ad);
503 504 505 506 507 508
	}
    }
  else
    {
      struct grub_udf_long_ad *ad = (struct grub_udf_long_ad *) ptr;

509
      filebytes = fileblock * U32 (node->data->lvd.bsize);
510
      while (len >= (grub_ssize_t) sizeof (struct grub_udf_long_ad))
511
	{
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
	  grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff;
	  grub_uint32_t adtype = U32 (ad->length) >> 30;
	  if (adtype == 3)
	    {
	      struct grub_udf_aed *extension;
	      grub_disk_addr_t sec = grub_udf_get_block(node->data,
							ad->block.part_ref,
							ad->block.block_num);
	      if (!buf)
		{
		  buf = grub_malloc (U32 (node->data->lvd.bsize));
		  if (!buf)
		    return 0;
		}
	      if (grub_disk_read (node->data->disk, sec << node->data->lbshift,
				  0, adlen, buf))
		goto fail;

	      extension = (struct grub_udf_aed *) buf;
	      if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED)
		{
		  grub_error (GRUB_ERR_BAD_FS, "invalid aed tag");
		  goto fail;
		}

	      len = U32 (extension->ae_len);
	      ad = (struct grub_udf_long_ad *)
		    (buf + sizeof (struct grub_udf_aed));
	      continue;
	    }
	      
	  if (filebytes < adlen)
	    {
	      grub_uint32_t ad_block_num = ad->block.block_num;
	      grub_uint32_t ad_part_ref = ad->block.part_ref;
	      grub_free (buf);
	      return ((U32 (ad_block_num) & GRUB_UDF_EXT_MASK) ?  0 :
		      (grub_udf_get_block (node->data, ad_part_ref,
					   ad_block_num)
		       + (filebytes >> (GRUB_DISK_SECTOR_BITS
				        + node->data->lbshift))));
	    }

	  filebytes -= adlen;
556
	  ad++;
557
	  len -= sizeof (struct grub_udf_long_ad);
558 559 560
	}
    }

561
fail:
562
  grub_free (buf);
563

564 565 566 567 568
  return 0;
}

static grub_ssize_t
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 575 576 577
    {
    case GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB:
      {
	char *ptr;

578 579 580 581 582
	ptr = ((U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) ?
	       ((char *) &node->block.fe.ext_attr[0]
                + U32 (node->block.fe.ext_attr_length)) :
	       ((char *) &node->block.efe.ext_attr[0]
                + U32 (node->block.efe.ext_attr_length)));
583 584 585 586 587 588 589 590 591 592 593

	grub_memcpy (buf, ptr + pos, len);

	return len;
      }

    case GRUB_UDF_ICBTAG_FLAG_AD_EXT:
      grub_error (GRUB_ERR_BAD_FS, "invalid extent type");
      return 0;
    }

594 595 596 597 598
  return grub_fshelp_read_file (node->data->disk, node,
				read_hook, read_hook_data,
				pos, len, buf, grub_udf_read_block,
				U64 (node->block.fe.file_size),
				node->data->lbshift, 0);
599 600
}

601
static unsigned sblocklist[] = { 256, 512, 0 };
602 603 604 605 606 607

static struct grub_udf_data *
grub_udf_mount (grub_disk_t disk)
{
  struct grub_udf_data *data = 0;
  struct grub_udf_fileset root_fs;
608 609 610
  unsigned *sblklist;
  grub_uint32_t block, vblock;
  int i, lbshift;
611 612 613 614 615 616 617

  data = grub_malloc (sizeof (struct grub_udf_data));
  if (!data)
    return 0;

  data->disk = disk;

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
  /* Search for Anchor Volume Descriptor Pointer (AVDP)
   * and determine logical block size.  */
  block = 0;
  for (lbshift = 0; lbshift < 4; lbshift++)
    {
      for (sblklist = sblocklist; *sblklist; sblklist++)
        {
	  struct grub_udf_avdp avdp;

	  if (grub_disk_read (disk, *sblklist << lbshift, 0,
			      sizeof (struct grub_udf_avdp), &avdp))
	    {
	      grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
	      goto fail;
	    }

	  if (U16 (avdp.tag.tag_ident) == GRUB_UDF_TAG_IDENT_AVDP &&
	      U32 (avdp.tag.tag_location) == *sblklist)
	    {
	      block = U32 (avdp.vds.start);
	      break;
	    }
	}

      if (block)
	break;
    }

  if (!block)
    {
      grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
      goto fail;
    }
  data->lbshift = lbshift;

653
  /* Search for Volume Recognition Sequence (VRS).  */
654 655
  for (vblock = (32767 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1;;
       vblock += (2047 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1)
656 657 658
    {
      struct grub_udf_vrs vrs;

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 664 665 666 667 668 669 670 671 672 673 674 675
	  goto fail;
	}

      if ((!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR03, 5)) ||
	  (!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR02, 5)))
	break;

      if ((grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BEA01, 5)) &&
	  (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BOOT2, 5)) &&
	  (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CD001, 5)) &&
	  (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CDW02, 5)) &&
	  (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_TEA01, 5)))
	{
676
	  grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
677 678 679 680 681
	  goto fail;
	}
    }

  data->npd = data->npm = 0;
682
  /* Locate Partition Descriptor (PD) and Logical Volume Descriptor (LVD).  */
683 684 685 686
  while (1)
    {
      struct grub_udf_tag tag;

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 692 693 694 695 696 697 698 699 700 701 702
	  goto fail;
	}

      tag.tag_ident = U16 (tag.tag_ident);
      if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PD)
	{
	  if (data->npd >= GRUB_UDF_MAX_PDS)
	    {
	      grub_error (GRUB_ERR_BAD_FS, "too many PDs");
	      goto fail;
	    }

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 709 710 711 712 713 714
	      goto fail;
	    }

	  data->npd++;
	}
      else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_LVD)
	{
715
	  int k;
716 717 718

	  struct grub_udf_partmap *ppm;

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 725 726 727 728 729 730 731 732 733
	      goto fail;
	    }

	  if (data->npm + U32 (data->lvd.num_part_maps) > GRUB_UDF_MAX_PMS)
	    {
	      grub_error (GRUB_ERR_BAD_FS, "too many partition maps");
	      goto fail;
	    }

	  ppm = (struct grub_udf_partmap *) &data->lvd.part_maps;
734
	  for (k = U32 (data->lvd.num_part_maps); k > 0; k--)
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
	    {
	      if (ppm->type != GRUB_UDF_PARTMAP_TYPE_1)
		{
		  grub_error (GRUB_ERR_BAD_FS, "partmap type not supported");
		  goto fail;
		}

	      data->pms[data->npm++] = ppm;
	      ppm = (struct grub_udf_partmap *) ((char *) ppm +
                                                 U32 (ppm->length));
	    }
	}
      else if (tag.tag_ident > GRUB_UDF_TAG_IDENT_TD)
	{
	  grub_error (GRUB_ERR_BAD_FS, "invalid tag ident");
	  goto fail;
	}
      else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_TD)
	break;

      block++;
    }

  for (i = 0; i < data->npm; i++)
    {
      int j;

      for (j = 0; j < data->npd; j++)
	if (data->pms[i]->type1.part_num == data->pds[j].part_num)
	  {
	    data->pms[i]->type1.part_num = j;
	    break;
	  }

      if (j == data->npd)
	{
	  grub_error (GRUB_ERR_BAD_FS, "can\'t find PD");
	  goto fail;
	}
    }

  block = grub_udf_get_block (data,
			      data->lvd.root_fileset.block.part_ref,
			      data->lvd.root_fileset.block.block_num);

  if (grub_errno)
    goto fail;

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 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
      goto fail;
    }

  if (U16 (root_fs.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FSD)
    {
      grub_error (GRUB_ERR_BAD_FS, "invalid fileset descriptor");
      goto fail;
    }

  data->root_icb = root_fs.root_icb;

  return data;

fail:
  grub_free (data);
  return 0;
}

805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
#ifdef GRUB_UTIL
grub_disk_addr_t
grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
{
  grub_disk_addr_t ret;
  static struct grub_udf_data *data;

  data = grub_udf_mount (disk);
  if (!data)
    return 0;

  ret = U32 (data->pds[data->pms[0]->type1.part_num].start);
  *sec_per_lcn = 1ULL << data->lbshift;
  grub_free (data);
  return ret;
}
#endif

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 828
  grub_size_t utf16len = 0;

829 830 831
  if (sz == 0)
    return NULL;

832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
  if (raw[0] != 8 && raw[0] != 16)
    return NULL;

  if (raw[0] == 8)
    {
      unsigned i;
      utf16len = sz - 1;
      utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
      if (!utf16)
	return NULL;
      for (i = 0; i < utf16len; i++)
	utf16[i] = raw[i + 1];
    }
  if (raw[0] == 16)
    {
      unsigned i;
      utf16len = (sz - 1) / 2;
      utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
      if (!utf16)
	return NULL;
      for (i = 0; i < utf16len; i++)
	utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
    }
855 856 857 858
  if (!outbuf)
    outbuf = grub_malloc (utf16len * GRUB_MAX_UTF8_PER_UTF16 + 1);
  if (outbuf)
    *grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0';
859
  grub_free (utf16);
860
  return outbuf;
861 862
}

863 864
static int
grub_udf_iterate_dir (grub_fshelp_node_t dir,
865
		      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
866 867 868
{
  grub_fshelp_node_t child;
  struct grub_udf_file_ident dirent;
869
  grub_off_t offset = 0;
870

871
  child = grub_malloc (get_fshelp_size (dir->data));
872 873 874 875
  if (!child)
    return 0;

  /* The current directory is not stored.  */
876
  grub_memcpy (child, dir, get_fshelp_size (dir->data));
877

878
  if (hook (".", GRUB_FSHELP_DIR, child, hook_data))
879 880
    return 1;

881
  while (offset < U64 (dir->block.fe.file_size))
882
    {
883
      if (grub_udf_read_file (dir, 0, 0, offset, sizeof (dirent),
884 885 886 887 888 889 890 891 892 893
			      (char *) &dirent) != sizeof (dirent))
	return 0;

      if (U16 (dirent.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FID)
	{
	  grub_error (GRUB_ERR_BAD_FS, "invalid fid tag");
	  return 0;
	}

      offset += sizeof (dirent) + U16 (dirent.imp_use_length);
894
      if (!(dirent.characteristics & GRUB_UDF_FID_CHAR_DELETED))
895
	{
896
	  child = grub_malloc (get_fshelp_size (dir->data));
897
	  if (!child)
898 899
	    return 0;

900 901 902 903
          if (grub_udf_read_icb (dir->data, &dirent.icb, child))
	    return 0;

          if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT)
904
	    {
905
	      /* This is the parent directory.  */
906
	      if (hook ("..", GRUB_FSHELP_DIR, child, hook_data))
907
	        return 1;
908
	    }
909
          else
910
	    {
911
	      enum grub_fshelp_filetype type;
912
	      char *filename;
913
	      grub_uint8_t raw[MAX_FILE_IDENT_LENGTH];
914 915 916

	      type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ?
		      (GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG));
917
	      if (child->block.fe.icbtag.file_type == GRUB_UDF_ICBTAG_TYPE_SYMLINK)
918
		type = GRUB_FSHELP_SYMLINK;
919

920
	      if ((grub_udf_read_file (dir, 0, 0, offset,
921 922 923 924 925
				       dirent.file_ident_length,
				       (char *) raw))
		  != dirent.file_ident_length)
		return 0;

926
	      filename = read_string (raw, dirent.file_ident_length, 0);
927 928
	      if (!filename)
		grub_print_error ();
929

930
	      if (filename && hook (filename, type, child, hook_data))
931 932 933
		{
		  grub_free (filename);
		  return 1;
934
		}
935
	      grub_free (filename);
936
	    }
937 938 939 940 941 942 943 944 945
	}

      /* Align to dword boundary.  */
      offset = (offset + dirent.file_ident_length + 3) & (~3);
    }

  return 0;
}

946
static char *
947
grub_udf_read_symlink (grub_fshelp_node_t node)
948
{
949
  grub_size_t sz = U64 (node->block.fe.file_size);
950
  grub_uint8_t *raw;
951 952
  const grub_uint8_t *ptr;
  char *out, *optr;
953 954 955

  if (sz < 4)
    return NULL;
956
  raw = grub_malloc (sz);
957 958
  if (!raw)
    return NULL;
959
  if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0)
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
    {
      grub_free (raw);
      return NULL;
    }

  out = grub_malloc (sz * 2 + 1);
  if (!out)
    {
      grub_free (raw);
      return NULL;
    }

  optr = out;

  for (ptr = raw; ptr < raw + sz; )
    {
      grub_size_t s;
      if ((grub_size_t) (ptr - raw + 4) > sz)
	goto fail;
      if (!(ptr[2] == 0 && ptr[3] == 0))
	goto fail;
      s = 4 + ptr[1];
      if ((grub_size_t) (ptr - raw + s) > sz)
	goto fail;
      switch (*ptr)
	{
	case 1:
	  if (ptr[1])
	    goto fail;
989
	  /* Fallthrough.  */
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
	case 2:
	  /* in 4 bytes. out: 1 byte.  */
	  optr = out;
	  *optr++ = '/';
	  break;
	case 3:
	  /* in 4 bytes. out: 3 bytes.  */
	  if (optr != out)
	    *optr++ = '/';
	  *optr++ = '.';
	  *optr++ = '.';
	  break;
	case 4:
	  /* in 4 bytes. out: 2 bytes.  */
	  if (optr != out)
	    *optr++ = '/';
	  *optr++ = '.';
	  break;
	case 5:
	  /* in 4 + n bytes. out, at most: 1 + 2 * n bytes.  */
	  if (optr != out)
	    *optr++ = '/';
1012 1013
	  if (!read_string (ptr + 4, s - 4, optr))
	    goto fail;
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
	  optr += grub_strlen (optr);
	  break;
	default:
	  goto fail;
	}
      ptr += s;
    }
  *optr = 0;
  grub_free (raw);
  return out;

 fail:
1026
  grub_free (raw);
1027 1028 1029
  grub_free (out);
  grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
  return NULL;
1030 1031
}

1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
/* Context for grub_udf_dir.  */
struct grub_udf_dir_ctx
{
  grub_fs_dir_hook_t hook;
  void *hook_data;
};

/* Helper for grub_udf_dir.  */
static int
grub_udf_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
		   grub_fshelp_node_t node, void *data)
{
  struct grub_udf_dir_ctx *ctx = data;
  struct grub_dirhook_info info;
  const struct grub_udf_timestamp *tstamp = NULL;

  grub_memset (&info, 0, sizeof (info));
  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
  if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
    tstamp = &node->block.fe.modification_time;
  else if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE)
    tstamp = &node->block.efe.modification_time;

  if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000)
    {
      grub_int16_t tz;
      struct grub_datetime datetime;

      datetime.year = U16 (tstamp->year);
      datetime.month = tstamp->month;
      datetime.day = tstamp->day;
      datetime.hour = tstamp->hour;
      datetime.minute = tstamp->minute;
      datetime.second = tstamp->second;

      tz = U16 (tstamp->type_and_timezone) & 0xfff;
      if (tz & 0x800)
	tz |= 0xf000;
      if (tz == -2047)
	tz = 0;

      info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime);

      info.mtime -= 60 * tz;
    }
  grub_free (node);
  return ctx->hook (filename, &info, ctx->hook_data);
}

1081 1082
static grub_err_t
grub_udf_dir (grub_device_t device, const char *path,
1083
	      grub_fs_dir_hook_t hook, void *hook_data)
1084
{
1085
  struct grub_udf_dir_ctx ctx = { hook, hook_data };
1086
  struct grub_udf_data *data = 0;
1087 1088
  struct grub_fshelp_node *rootnode = 0;
  struct grub_fshelp_node *foundnode = 0;
1089 1090 1091 1092 1093 1094 1095

  grub_dl_ref (my_mod);

  data = grub_udf_mount (device->disk);
  if (!data)
    goto fail;

1096 1097
  rootnode = grub_malloc (get_fshelp_size (data));
  if (!rootnode)
1098 1099
    goto fail;

1100 1101 1102 1103
  if (grub_udf_read_icb (data, &data->root_icb, rootnode))
    goto fail;

  if (grub_fshelp_find_file (path, rootnode,
1104
			     &foundnode,
1105
			     grub_udf_iterate_dir, grub_udf_read_symlink,
1106
			     GRUB_FSHELP_DIR))
1107 1108
    goto fail;

1109
  grub_udf_iterate_dir (foundnode, grub_udf_dir_iter, &ctx);
1110

1111
  if (foundnode != rootnode)
1112 1113 1114
    grub_free (foundnode);

fail:
1115 1116
  grub_free (rootnode);

1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
  grub_free (data);

  grub_dl_unref (my_mod);

  return grub_errno;
}

static grub_err_t
grub_udf_open (struct grub_file *file, const char *name)
{
  struct grub_udf_data *data;
1128
  struct grub_fshelp_node *rootnode = 0;
1129 1130 1131 1132 1133 1134 1135 1136
  struct grub_fshelp_node *foundnode;

  grub_dl_ref (my_mod);

  data = grub_udf_mount (file->device->disk);
  if (!data)
    goto fail;

1137 1138
  rootnode = grub_malloc (get_fshelp_size (data));
  if (!rootnode)
1139 1140
    goto fail;

1141 1142 1143 1144
  if (grub_udf_read_icb (data, &data->root_icb, rootnode))
    goto fail;

  if (grub_fshelp_find_file (name, rootnode,
1145
			     &foundnode,
1146
			     grub_udf_iterate_dir, grub_udf_read_symlink,
1147
			     GRUB_FSHELP_REG))
1148 1149 1150 1151
    goto fail;

  file->data = foundnode;
  file->offset = 0;
1152 1153 1154
  file->size = U64 (foundnode->block.fe.file_size);

  grub_free (rootnode);
1155 1156 1157 1158 1159 1160 1161

  return 0;

fail:
  grub_dl_unref (my_mod);

  grub_free (data);
1162
  grub_free (rootnode);
1163 1164 1165 1166 1167 1168 1169 1170 1171

  return grub_errno;
}

static grub_ssize_t
grub_udf_read (grub_file_t file, char *buf, grub_size_t len)
{
  struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;

1172 1173
  return grub_udf_read_file (node, file->read_hook, file->read_hook_data,
			     file->offset, len, buf);
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
}

static grub_err_t
grub_udf_close (grub_file_t file)
{
  if (file->data)
    {
      struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;

      grub_free (node->data);
      grub_free (node);
    }

  grub_dl_unref (my_mod);

  return GRUB_ERR_NONE;
}

static grub_err_t
grub_udf_label (grub_device_t device, char **label)
{
  struct grub_udf_data *data;
  data = grub_udf_mount (device->disk);

  if (data)
    {
1200
      *label = read_string (data->lvd.ident, sizeof (data->lvd.ident), 0);
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
      grub_free (data);
    }
  else
    *label = 0;

  return grub_errno;
}

static struct grub_fs grub_udf_fs = {
  .name = "udf",
  .dir = grub_udf_dir,
  .open = grub_udf_open,
  .read = grub_udf_read,
  .close = grub_udf_close,
  .label = grub_udf_label,
1216 1217 1218 1219
#ifdef GRUB_UTIL
  .reserved_first_sector = 1,
  .blocklist_install = 1,
#endif
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
  .next = 0
};

GRUB_MOD_INIT (udf)
{
  grub_fs_register (&grub_udf_fs);
  my_mod = mod;
}

GRUB_MOD_FINI (udf)
{
  grub_fs_unregister (&grub_udf_fs);
}