Blame lib/udf/udf_fs.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2005-2006, 2008, 2011, 2013-2014, 2017
Packit dd8086
  Rocky Bernstein <rocky@gnu.org>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
/*
Packit dd8086
 * Portions copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
Packit dd8086
 * All rights reserved.
Packit dd8086
 *
Packit dd8086
 * Redistribution and use in source and binary forms, with or without
Packit dd8086
 * modification, are permitted provided that the following conditions
Packit dd8086
 * are met:
Packit dd8086
 * 1. Redistributions of source code must retain the above copyright
Packit dd8086
 *    notice, this list of conditions and the following disclaimer.
Packit dd8086
 * 2. Redistributions in binary form must reproduce the above copyright
Packit dd8086
 *    notice, this list of conditions and the following disclaimer in the
Packit dd8086
 *    documentation and/or other materials provided with the distribution.
Packit dd8086
 *
Packit dd8086
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
Packit dd8086
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit dd8086
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit dd8086
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
Packit dd8086
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
Packit dd8086
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
Packit dd8086
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
Packit dd8086
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
Packit dd8086
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
Packit dd8086
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
Packit dd8086
 * SUCH DAMAGE.
Packit dd8086
 */
Packit dd8086
Packit dd8086

Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
# include "config.h"
Packit dd8086
# define __CDIO_CONFIG_H__ 1
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/util.h>
Packit dd8086
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
#include <stdio.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
# include <string.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDLIB_H
Packit dd8086
# include <stdlib.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/* These definitions are also to make debugging easy. Note that they
Packit dd8086
   have to come *before* #include <cdio/ecma_167.h> which sets
Packit dd8086
   #defines for these.
Packit dd8086
*/
Packit dd8086
const char VSD_STD_ID_BEA01[] = {'B', 'E', 'A', '0', '1'};
Packit dd8086
const char VSD_STD_ID_BOOT2[] = {'B', 'O', 'O', 'T', '2'};
Packit dd8086
const char VSD_STD_ID_CD001[] = {'C', 'D', '0', '0', '1'};
Packit dd8086
const char VSD_STD_ID_CDW01[] = {'C', 'D', 'W', '0', '2'};
Packit dd8086
const char VSD_STD_ID_NSR03[] = {'N', 'S', 'R', '0', '3'};
Packit dd8086
const char VSD_STD_ID_TEA01[] = {'T', 'E', 'A', '0', '1'};
Packit dd8086
Packit dd8086
#include <cdio/bytesex.h>
Packit dd8086
#include <cdio/utf8.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
Packit dd8086
#include "udf_private.h"
Packit dd8086
#include "udf_fs.h"
Packit dd8086
#include "cdio_assert.h"
Packit dd8086
Packit dd8086
/*
Packit dd8086
 * The UDF specs are pretty clear on how each data structure is made
Packit dd8086
 * up, but not very clear on how they relate to each other.  Here is
Packit dd8086
 * the skinny... This demostrates a filesystem with one file in the
Packit dd8086
 * root directory.  Subdirectories are treated just as normal files,
Packit dd8086
 * but they have File Id Descriptors of their children as their file
Packit dd8086
 * data.  As for the Anchor Volume Descriptor Pointer, it can exist in
Packit dd8086
 * two of the following three places: sector 256, sector n (the max
Packit dd8086
 * sector of the disk), or sector n - 256.  It's a pretty good bet
Packit dd8086
 * that one will exist at sector 256 though.  One caveat is unclosed
Packit dd8086
 * CD media.  For that, sector 256 cannot be written, so the Anchor
Packit dd8086
 * Volume Descriptor Pointer can exist at sector 512 until the media
Packit dd8086
 * is closed.
Packit dd8086
 *
Packit dd8086
 *  Sector:
Packit dd8086
 *     256:
Packit dd8086
 *       n: Anchor Volume Descriptor Pointer
Packit dd8086
 * n - 256:	|
Packit dd8086
 *		|
Packit dd8086
 *		|-->Main Volume Descriptor Sequence
Packit dd8086
 *			|	|
Packit dd8086
 *			|	|
Packit dd8086
 *			|	|-->Logical Volume Descriptor
Packit dd8086
 *			|			  |
Packit dd8086
 *			|-->Partition Descriptor  |
Packit dd8086
 *				|		  |
Packit dd8086
 *				|		  |
Packit dd8086
 *				|-->Fileset Descriptor
Packit dd8086
 *					|
Packit dd8086
 *					|
Packit dd8086
 *					|-->Root Dir File Entry
Packit dd8086
 *						|
Packit dd8086
 *						|
Packit dd8086
 *						|-->File data:
Packit dd8086
 *						    File Id Descriptor
Packit dd8086
 *							|
Packit dd8086
 *							|
Packit dd8086
 *							|-->File Entry
Packit dd8086
 *								|
Packit dd8086
 *								|
Packit dd8086
 *								|-->File data
Packit dd8086
 */
Packit dd8086
Packit dd8086
static udf_dirent_t *
Packit dd8086
udf_new_dirent(udf_file_entry_t *p_udf_fe, udf_t *p_udf,
Packit dd8086
	       const char *psz_name, bool b_dir, bool b_parent);
Packit dd8086
Packit dd8086
/**
Packit dd8086
 * Check the descriptor tag for both the correct id and correct checksum.
Packit dd8086
 * Return zero if all is good, -1 if not.
Packit dd8086
 */
Packit dd8086
int
Packit dd8086
udf_checktag(const udf_tag_t *p_tag, udf_Uint16_t tag_id)
Packit dd8086
{
Packit dd8086
  uint8_t *itag;
Packit dd8086
  uint8_t i;
Packit dd8086
  uint8_t cksum = 0;
Packit dd8086
Packit dd8086
  itag = (uint8_t *)p_tag;
Packit dd8086
Packit dd8086
#ifdef WORDS_BIGENDIAN
Packit dd8086
  tag_id = UINT16_SWAP_LE_BE(tag_id);
Packit dd8086
#endif
Packit dd8086
Packit dd8086
  if (p_tag->id != tag_id)
Packit dd8086
    return -1;
Packit dd8086
Packit dd8086
  for (i = 0; i < 15; i++)
Packit dd8086
    cksum = cksum + itag[i];
Packit dd8086
  cksum = cksum - itag[4];
Packit dd8086
Packit dd8086
  if (cksum == p_tag->cksum)
Packit dd8086
    return 0;
Packit dd8086
Packit dd8086
  return -1;
Packit dd8086
}
Packit dd8086
Packit dd8086
bool
Packit dd8086
udf_get_lba(const udf_file_entry_t *p_udf_fe,
Packit dd8086
	    /*out*/ uint32_t *start, /*out*/ uint32_t *end)
Packit dd8086
{
Packit dd8086
  if (! p_udf_fe->i_alloc_descs)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  switch (p_udf_fe->icb_tag.flags & ICBTAG_FLAG_AD_MASK) {
Packit dd8086
  case ICBTAG_FLAG_AD_SHORT:
Packit dd8086
    {
Packit dd8086
      /* The allocation descriptor field is filled with short_ad's. */
Packit dd8086
      udf_short_ad_t *p_ad = (udf_short_ad_t *)
Packit dd8086
	(p_udf_fe->u.ext_attr + uint32_from_le(p_udf_fe->i_extended_attr));
Packit dd8086
Packit dd8086
      *start = uint32_from_le(p_ad->pos);
Packit dd8086
      *end = *start +
Packit dd8086
	((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE;
Packit dd8086
      return true;
Packit dd8086
    }
Packit dd8086
    break;
Packit dd8086
  case ICBTAG_FLAG_AD_LONG:
Packit dd8086
    {
Packit dd8086
      /* The allocation descriptor field is filled with long_ad's */
Packit dd8086
      udf_long_ad_t *p_ad = (udf_long_ad_t *)
Packit dd8086
	(p_udf_fe->u.ext_attr + uint32_from_le(p_udf_fe->i_extended_attr));
Packit dd8086
Packit dd8086
      *start = uint32_from_le(p_ad->loc.lba); /* ignore partition number */
Packit dd8086
      *end = *start +
Packit dd8086
	((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE;
Packit dd8086
      return true;
Packit dd8086
    }
Packit dd8086
    break;
Packit dd8086
  case ICBTAG_FLAG_AD_EXTENDED:
Packit dd8086
    {
Packit dd8086
      udf_ext_ad_t *p_ad = (udf_ext_ad_t *)
Packit dd8086
	(p_udf_fe->u.ext_attr + uint32_from_le(p_udf_fe->i_extended_attr));
Packit dd8086
Packit dd8086
      *start = uint32_from_le(p_ad->ext_loc.lba); /* ignore partition number */
Packit dd8086
      *end = *start +
Packit dd8086
	((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE;
Packit dd8086
      return true;
Packit dd8086
    }
Packit dd8086
    break;
Packit dd8086
  default:
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
  return false;
Packit dd8086
}
Packit dd8086
Packit dd8086
#define udf_PATH_DELIMITERS "/\\"
Packit dd8086
Packit dd8086
/* Searches p_udf_dirent for a directory entry called psz_token.
Packit dd8086
   Note that p_udf_dirent may be replaced or freed during this call
Packit dd8086
   and only the returned udf_dirent_t must be used afterwards.
Packit dd8086
*/
Packit dd8086
static
Packit dd8086
udf_dirent_t *
Packit dd8086
udf_ff_traverse(udf_dirent_t *p_udf_dirent, char *psz_token)
Packit dd8086
{
Packit dd8086
  while ((p_udf_dirent = udf_readdir(p_udf_dirent))) {
Packit dd8086
    if (strcmp(psz_token, p_udf_dirent->psz_name) == 0) {
Packit dd8086
      char *next_tok = strtok(NULL, udf_PATH_DELIMITERS);
Packit dd8086
Packit dd8086
      if (!next_tok)
Packit dd8086
	return p_udf_dirent; /* found */
Packit dd8086
      else if (p_udf_dirent->b_dir) {
Packit dd8086
	udf_dirent_t * p_udf_dirent_next = udf_opendir(p_udf_dirent);
Packit dd8086
Packit dd8086
	if (p_udf_dirent_next) {
Packit dd8086
	  /* free p_udf_dirent to avoid leaking memory. */
Packit dd8086
	  udf_dirent_free(p_udf_dirent);
Packit dd8086
Packit dd8086
	  /* previous p_udf_dirent_next is freed by udf_ff_traverse. */
Packit dd8086
	  p_udf_dirent_next = udf_ff_traverse(p_udf_dirent_next, next_tok);
Packit dd8086
Packit dd8086
	  return p_udf_dirent_next;
Packit dd8086
	}
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* FIXME! */
Packit dd8086
#define udf_MAX_PATHLEN 2048
Packit dd8086
Packit dd8086
udf_dirent_t *
Packit dd8086
udf_fopen(udf_dirent_t *p_udf_root, const char *psz_name)
Packit dd8086
{
Packit dd8086
  udf_dirent_t *p_udf_file = NULL;
Packit dd8086
Packit dd8086
  if (p_udf_root) {
Packit dd8086
    char tokenline[udf_MAX_PATHLEN];
Packit dd8086
    char *psz_token;
Packit dd8086
Packit dd8086
    /* file position must be reset when accessing a new file */
Packit dd8086
    p_udf_root->p_udf->i_position = 0;
Packit dd8086
Packit dd8086
    strncpy(tokenline, psz_name, udf_MAX_PATHLEN-1);
Packit dd8086
    tokenline[udf_MAX_PATHLEN-1] = '\0';
Packit dd8086
    psz_token = strtok(tokenline, udf_PATH_DELIMITERS);
Packit dd8086
    if (psz_token) {
Packit dd8086
      udf_dirent_t *p_udf_dirent =
Packit dd8086
	udf_new_dirent(&p_udf_root->fe, p_udf_root->p_udf,
Packit dd8086
		       p_udf_root->psz_name, p_udf_root->b_dir,
Packit dd8086
		       p_udf_root->b_parent);
Packit dd8086
      p_udf_file = udf_ff_traverse(p_udf_dirent, psz_token);
Packit dd8086
    }
Packit dd8086
    else if ( 0 == strncmp("/", psz_name, sizeof("/")) ) {
Packit dd8086
      return udf_new_dirent(&p_udf_root->fe, p_udf_root->p_udf,
Packit dd8086
			    p_udf_root->psz_name, p_udf_root->b_dir,
Packit dd8086
			    p_udf_root->b_parent);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return p_udf_file;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Convert unicode16 to UTF-8.
Packit dd8086
   The returned string is allocated and must be freed by the caller
Packit dd8086
*/
Packit dd8086
static char*
Packit dd8086
unicode16_decode(const uint8_t *data, int i_len)
Packit dd8086
{
Packit dd8086
  int i;
Packit dd8086
  char* r = NULL;
Packit dd8086
Packit dd8086
  switch (data[0])
Packit dd8086
  {
Packit dd8086
  case 8:
Packit dd8086
    r = (char*)calloc(i_len, 1);
Packit dd8086
    if (r == NULL)
Packit dd8086
      return r;
Packit dd8086
    for (i=0; i
Packit dd8086
      r[i] = data[i+1];
Packit dd8086
    return r;
Packit dd8086
  case 16:
Packit dd8086
    cdio_charset_to_utf8((char*)&data[1], i_len-1, &r, "UCS-2BE");
Packit dd8086
    return r;
Packit dd8086
  default:
Packit dd8086
    /* Empty string, as some existing sections can't take a NULL pointer */
Packit dd8086
    r = (char*)calloc(1, 1);
Packit dd8086
    return r;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
static udf_dirent_t *
Packit dd8086
udf_new_dirent(udf_file_entry_t *p_udf_fe, udf_t *p_udf,
Packit dd8086
	       const char *psz_name, bool b_dir, bool b_parent)
Packit dd8086
{
Packit dd8086
  udf_dirent_t *p_udf_dirent = (udf_dirent_t *)
Packit dd8086
    calloc(1, sizeof(udf_dirent_t));
Packit dd8086
  if (!p_udf_dirent) return NULL;
Packit dd8086
Packit dd8086
  p_udf_dirent->psz_name     = strdup(psz_name);
Packit dd8086
  p_udf_dirent->b_dir        = b_dir;
Packit dd8086
  p_udf_dirent->b_parent     = b_parent;
Packit dd8086
  p_udf_dirent->p_udf        = p_udf;
Packit dd8086
  p_udf_dirent->i_part_start = p_udf->i_part_start;
Packit dd8086
  p_udf_dirent->dir_left     = uint64_from_le(p_udf_fe->info_len);
Packit dd8086
Packit dd8086
  memcpy(&(p_udf_dirent->fe), p_udf_fe,
Packit dd8086
	 sizeof(udf_file_entry_t));
Packit dd8086
  udf_get_lba( p_udf_fe, &(p_udf_dirent->i_loc),
Packit dd8086
	       &(p_udf_dirent->i_loc_end) );
Packit dd8086
  return p_udf_dirent;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Seek to a position i_start and then read i_blocks. Number of blocks read is
Packit dd8086
  returned. One normally expects the return to be equal to i_blocks.
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
udf_read_sectors (const udf_t *p_udf, void *ptr, lsn_t i_start,
Packit dd8086
		 long i_blocks)
Packit dd8086
{
Packit dd8086
  driver_return_code_t ret;
Packit dd8086
  long i_read;
Packit dd8086
  off_t i_byte_offset;
Packit dd8086
Packit dd8086
  if (!p_udf) return 0;
Packit dd8086
  /* Without the cast, i_start * UDF_BLOCKSIZE may be evaluated as 32 bit */
Packit dd8086
  i_byte_offset = ((off_t)i_start) * UDF_BLOCKSIZE;
Packit dd8086
  /* Since we're using SEEK_SET, the value must be positive */
Packit dd8086
  if (i_byte_offset < 0) {
Packit dd8086
    if (sizeof(off_t) <= 4)	/* probably missing LFS */
Packit dd8086
      cdio_warn("Large File Support is required to access streams of 2 GB or more");
Packit dd8086
    return DRIVER_OP_BAD_PARAMETER;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (p_udf->b_stream) {
Packit dd8086
    ret = cdio_stream_seek (p_udf->stream, i_byte_offset, SEEK_SET);
Packit dd8086
    if (DRIVER_OP_SUCCESS != ret) return ret;
Packit dd8086
    i_read = cdio_stream_read (p_udf->stream, ptr, UDF_BLOCKSIZE, i_blocks);
Packit dd8086
    if (i_read) return DRIVER_OP_SUCCESS;
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  } else {
Packit dd8086
    return cdio_read_data_sectors(p_udf->cdio, ptr, i_start, UDF_BLOCKSIZE,
Packit dd8086
				  i_blocks);
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Open an UDF for reading. Maybe in the future we will have
Packit dd8086
  a mode. NULL is returned on error.
Packit dd8086
Packit dd8086
  Caller must free result - use udf_close for that.
Packit dd8086
*/
Packit dd8086
udf_t *
Packit dd8086
udf_open (const char *psz_path)
Packit dd8086
{
Packit dd8086
  udf_t *p_udf = (udf_t *) calloc(1, sizeof(udf_t)) ;
Packit dd8086
  uint8_t data[UDF_BLOCKSIZE];
Packit dd8086
Packit dd8086
  if (!p_udf) return NULL;
Packit dd8086
Packit dd8086
  /* Sanity check */
Packit dd8086
  cdio_assert(sizeof(udf_file_entry_t) == UDF_BLOCKSIZE);
Packit dd8086
Packit dd8086
  p_udf->cdio = cdio_open(psz_path, DRIVER_UNKNOWN);
Packit dd8086
  if (!p_udf->cdio) {
Packit dd8086
    /* Not a CD-ROM drive or CD Image. Maybe it's a UDF file not
Packit dd8086
       encapsulated as a CD-ROM Image (e.g. often .UDF or (sic) .ISO)
Packit dd8086
    */
Packit dd8086
    p_udf->stream = cdio_stdio_new( psz_path );
Packit dd8086
    if (!p_udf->stream)
Packit dd8086
      goto error;
Packit dd8086
    p_udf->b_stream = true;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /*
Packit dd8086
   * Look for an Anchor Volume Descriptor Pointer at sector 256.
Packit dd8086
   */
Packit dd8086
  if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, &data, 256, 1) )
Packit dd8086
    goto error;
Packit dd8086
Packit dd8086
  memcpy(&(p_udf->anchor_vol_desc_ptr), &data, sizeof(anchor_vol_desc_ptr_t));
Packit dd8086
Packit dd8086
  if (udf_checktag((udf_tag_t *)&(p_udf->anchor_vol_desc_ptr), TAGID_ANCHOR))
Packit dd8086
    goto error;
Packit dd8086
Packit dd8086
  /*
Packit dd8086
   * Then try to find a reference to a Primary Volume Descriptor.
Packit dd8086
   */
Packit dd8086
  {
Packit dd8086
    anchor_vol_desc_ptr_t *p_avdp = &p_udf->anchor_vol_desc_ptr;
Packit dd8086
Packit dd8086
    const uint32_t mvds_start =
Packit dd8086
      uint32_from_le(p_avdp->main_vol_desc_seq_ext.loc);
Packit dd8086
    const uint32_t mvds_end   = mvds_start +
Packit dd8086
      (uint32_from_le(p_avdp->main_vol_desc_seq_ext.len) - 1) / UDF_BLOCKSIZE;
Packit dd8086
Packit dd8086
    uint32_t i_lba;
Packit dd8086
Packit dd8086
    for (i_lba = mvds_start; i_lba < mvds_end; i_lba++) {
Packit dd8086
      udf_pvd_t *p_pvd = (udf_pvd_t *) &dat;;
Packit dd8086
Packit dd8086
      if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_pvd, i_lba, 1) )
Packit dd8086
	goto error;
Packit dd8086
Packit dd8086
      if (!udf_checktag(&p_pvd->tag, TAGID_PRI_VOL)) {
Packit dd8086
	p_udf->pvd_lba = i_lba;
Packit dd8086
	break;
Packit dd8086
      }
Packit dd8086
Packit dd8086
    }
Packit dd8086
Packit dd8086
    /*
Packit dd8086
     * If we couldn't find a reference, bail out.
Packit dd8086
     */
Packit dd8086
    if (i_lba == mvds_end)
Packit dd8086
      goto error;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return p_udf;
Packit dd8086
Packit dd8086
 error:
Packit dd8086
  cdio_stdio_destroy(p_udf->stream);
Packit dd8086
  free(p_udf);
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
 * Gets the Volume Identifier, as an UTF-8 string
Packit dd8086
 * psz_volid, place to put the string
Packit dd8086
 * i_volid, size of the buffer psz_volid points to
Packit dd8086
 * returns the size of buffer needed for all data
Packit dd8086
 * Note: this call accepts a NULL psz_volid, to retrieve the length required.
Packit dd8086
 */
Packit dd8086
int
Packit dd8086
udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid, unsigned int i_volid)
Packit dd8086
{
Packit dd8086
  uint8_t data[UDF_BLOCKSIZE];
Packit dd8086
  const udf_pvd_t *p_pvd = (udf_pvd_t *) &dat;;
Packit dd8086
  char* r;
Packit dd8086
  unsigned int volid_len;
Packit dd8086
Packit dd8086
  /* clear the output to empty string */
Packit dd8086
  if (psz_volid != NULL)
Packit dd8086
    psz_volid[0] = 0;
Packit dd8086
Packit dd8086
  /* get primary volume descriptor */
Packit dd8086
  if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) )
Packit dd8086
    return 0;
Packit dd8086
Packit dd8086
  volid_len = p_pvd->vol_ident[UDF_VOLID_SIZE-1];
Packit dd8086
  if(volid_len > UDF_VOLID_SIZE-1) {
Packit dd8086
    /* this field is only UDF_VOLID_SIZE bytes something is wrong */
Packit dd8086
    volid_len = UDF_VOLID_SIZE-1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  r = unicode16_decode((uint8_t *) p_pvd->vol_ident, volid_len);
Packit dd8086
  if (r == NULL)
Packit dd8086
    return 0;
Packit dd8086
Packit dd8086
  volid_len = strlen(r)+1;     /* +1 for NUL terminator */
Packit dd8086
  if (psz_volid != NULL) {
Packit dd8086
    strncpy(psz_volid, r, MIN(volid_len, i_volid));
Packit dd8086
    psz_volid[i_volid-1] = 0;  /* strncpy does not always terminate the dest */
Packit dd8086
  }
Packit dd8086
  free(r);
Packit dd8086
Packit dd8086
  return volid_len;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
 * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded)
Packit dd8086
 * WARNING This is not a null terminated string
Packit dd8086
 * volsetid, place to put the data
Packit dd8086
 * i_volsetid, size of the buffer psz_volsetid points to
Packit dd8086
 * the buffer should be >=128 bytes to store the whole volumesetidentifier
Packit dd8086
 * returns the size of the available volsetid information (128)
Packit dd8086
 * or 0 on error
Packit dd8086
 */
Packit dd8086
int
Packit dd8086
udf_get_volumeset_id(udf_t *p_udf, /*out*/ uint8_t *volsetid,
Packit dd8086
		     unsigned int i_volsetid)
Packit dd8086
{
Packit dd8086
  uint8_t data[UDF_BLOCKSIZE];
Packit dd8086
  const udf_pvd_t *p_pvd = (udf_pvd_t *) &dat;;
Packit dd8086
Packit dd8086
  /* get primary volume descriptor */
Packit dd8086
  if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) )
Packit dd8086
    return 0;
Packit dd8086
Packit dd8086
  if (i_volsetid > UDF_VOLSET_ID_SIZE) {
Packit dd8086
    i_volsetid = UDF_VOLSET_ID_SIZE;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  memcpy(volsetid, p_pvd->volset_id, i_volsetid);
Packit dd8086
Packit dd8086
  return UDF_VOLSET_ID_SIZE;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
 * Gets the Logical Volume Identifier string, as an UTF-8 string
Packit dd8086
 * psz_logvolid, place to put the string (should be at least 64 bytes)
Packit dd8086
 * i_logvolid, size of the buffer psz_logvolid points to
Packit dd8086
 * returns the size of buffer needed for all data, including NUL terminator
Packit dd8086
 * A call to udf_get_root() should have been issued before this call
Packit dd8086
 * Note: this call accepts a NULL psz_volid, to retrieve the length required.
Packit dd8086
 */
Packit dd8086
int
Packit dd8086
udf_get_logical_volume_id(udf_t *p_udf, /*out*/ char *psz_logvolid, unsigned int i_logvolid)
Packit dd8086
{
Packit dd8086
  uint8_t data[UDF_BLOCKSIZE];
Packit dd8086
  logical_vol_desc_t *p_logvol = (logical_vol_desc_t *) &dat;;
Packit dd8086
  char* r;
Packit dd8086
  int logvolid_len;
Packit dd8086
Packit dd8086
  /* clear the output to empty string */
Packit dd8086
  if (psz_logvolid != NULL)
Packit dd8086
    psz_logvolid[0] = 0;
Packit dd8086
Packit dd8086
  if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_logvol, p_udf->lvd_lba, 1) )
Packit dd8086
    return 0;
Packit dd8086
Packit dd8086
  r = unicode16_decode((uint8_t *) p_logvol->logvol_id, p_logvol->logvol_id[127]);
Packit dd8086
  if (r == NULL)
Packit dd8086
    return 0;
Packit dd8086
Packit dd8086
  logvolid_len = strlen(r)+1;  /* +1 for NUL terminator */
Packit dd8086
  if (psz_logvolid != NULL) {
Packit dd8086
    strncpy(psz_logvolid, r, MIN(logvolid_len, i_logvolid));
Packit dd8086
    psz_logvolid[i_logvolid-1] = 0;    /* strncpy does not always terminate the dest */
Packit dd8086
  }
Packit dd8086
  free(r);
Packit dd8086
Packit dd8086
  return logvolid_len;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Get the root in p_udf. If b_any_partition is false then
Packit dd8086
  the root must be in the given partition.
Packit dd8086
  NULL is returned if the partition is not found or a root is not found or
Packit dd8086
  there is on error.
Packit dd8086
Packit dd8086
  Caller must free result - use udf_dirent_free for that.
Packit dd8086
*/
Packit dd8086
udf_dirent_t *
Packit dd8086
udf_get_root (udf_t *p_udf, bool b_any_partition, partition_num_t i_partition)
Packit dd8086
{
Packit dd8086
  const anchor_vol_desc_ptr_t *p_avdp = &p_udf->anchor_vol_desc_ptr;
Packit dd8086
  const uint32_t mvds_start =
Packit dd8086
    uint32_from_le(p_avdp->main_vol_desc_seq_ext.loc);
Packit dd8086
  const uint32_t mvds_end   = mvds_start +
Packit dd8086
    (uint32_from_le(p_avdp->main_vol_desc_seq_ext.len) - 1) / UDF_BLOCKSIZE;
Packit dd8086
  uint32_t i_lba;
Packit dd8086
  uint8_t data[UDF_BLOCKSIZE];
Packit dd8086
Packit dd8086
  /*
Packit dd8086
     Now we have the joy of finding the Partition Descriptor and the
Packit dd8086
     Logical Volume Descriptor for the Main Volume Descriptor
Packit dd8086
     Sequence. Once we've got that, we use the Logical Volume
Packit dd8086
     Descriptor to get a Fileset Descriptor and that has the Root
Packit dd8086
     Directory File Entry.
Packit dd8086
  */
Packit dd8086
  for (i_lba = mvds_start; i_lba < mvds_end; i_lba++) {
Packit dd8086
    uint8_t data2[UDF_BLOCKSIZE];
Packit dd8086
Packit dd8086
    partition_desc_t *p_partition = (partition_desc_t *) &data2;
Packit dd8086
Packit dd8086
    if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_partition, i_lba, 1) )
Packit dd8086
      return NULL;
Packit dd8086
Packit dd8086
    if (!udf_checktag(&p_partition->tag, TAGID_PARTITION)) {
Packit dd8086
      const partition_num_t i_partition_check
Packit dd8086
	= uint16_from_le(p_partition->number);
Packit dd8086
      if (b_any_partition || i_partition_check == i_partition) {
Packit dd8086
	/* Squirrel away some data regarding partition */
Packit dd8086
	p_udf->i_partition = uint16_from_le(p_partition->number);
Packit dd8086
	p_udf->i_part_start = uint32_from_le(p_partition->start_loc);
Packit dd8086
	if (p_udf->lvd_lba) break;
Packit dd8086
      }
Packit dd8086
    } else if (!udf_checktag(&p_partition->tag, TAGID_LOGVOL)) {
Packit dd8086
      /* Get fileset descriptor */
Packit dd8086
      logical_vol_desc_t *p_logvol = (logical_vol_desc_t *) &data2;
Packit dd8086
      bool b_valid =
Packit dd8086
	UDF_BLOCKSIZE == uint32_from_le(p_logvol->logical_blocksize);
Packit dd8086
Packit dd8086
      if (b_valid) {
Packit dd8086
	p_udf->lvd_lba = i_lba;
Packit dd8086
	p_udf->fsd_offset =
Packit dd8086
	  uint32_from_le(p_logvol->lvd_use.fsd_loc.loc.lba);
Packit dd8086
	if (p_udf->i_part_start) break;
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  if (p_udf->lvd_lba && p_udf->i_part_start) {
Packit dd8086
    udf_fsd_t *p_fsd = (udf_fsd_t *) &dat;;
Packit dd8086
Packit dd8086
    driver_return_code_t ret =
Packit dd8086
      udf_read_sectors(p_udf, p_fsd, p_udf->i_part_start + p_udf->fsd_offset,
Packit dd8086
		       1);
Packit dd8086
Packit dd8086
    if (DRIVER_OP_SUCCESS == ret && !udf_checktag(&p_fsd->tag, TAGID_FSD)) {
Packit dd8086
      udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &dat;;
Packit dd8086
      const uint32_t parent_icb = uint32_from_le(p_fsd->root_icb.loc.lba);
Packit dd8086
Packit dd8086
      /* Check partition numbers match of last-read block?  */
Packit dd8086
Packit dd8086
      ret = udf_read_sectors(p_udf, p_udf_fe,
Packit dd8086
			     p_udf->i_part_start + parent_icb, 1);
Packit dd8086
      if (ret == DRIVER_OP_SUCCESS &&
Packit dd8086
	  !udf_checktag(&p_udf_fe->tag, TAGID_FILE_ENTRY)) {
Packit dd8086
Packit dd8086
	/* Check partition numbers match of last-read block? */
Packit dd8086
Packit dd8086
	/* We win! - Save root directory information. */
Packit dd8086
	return udf_new_dirent(p_udf_fe, p_udf, "/", true, false );
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
#define free_and_null(x) \
Packit dd8086
  CDIO_FREE_IF_NOT_NULL(x); \
Packit dd8086
  x=NULL
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Close UDF and free resources associated with p_udf.
Packit dd8086
*/
Packit dd8086
bool
Packit dd8086
udf_close (udf_t *p_udf)
Packit dd8086
{
Packit dd8086
  if (!p_udf) return true;
Packit dd8086
  if (p_udf->b_stream) {
Packit dd8086
    cdio_stdio_destroy(p_udf->stream);
Packit dd8086
  } else {
Packit dd8086
    cdio_destroy(p_udf->cdio);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Get rid of root directory if allocated. */
Packit dd8086
Packit dd8086
  free_and_null(p_udf);
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
udf_dirent_t *
Packit dd8086
udf_opendir(const udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent->b_dir && !p_udf_dirent->b_parent && p_udf_dirent->fid) {
Packit dd8086
    udf_t *p_udf = p_udf_dirent->p_udf;
Packit dd8086
    udf_file_entry_t udf_fe;
Packit dd8086
Packit dd8086
    driver_return_code_t i_ret =
Packit dd8086
      udf_read_sectors(p_udf, &udf_fe, p_udf->i_part_start
Packit dd8086
		       + p_udf_dirent->fid->icb.loc.lba, 1);
Packit dd8086
Packit dd8086
    if (DRIVER_OP_SUCCESS == i_ret
Packit dd8086
	&& !udf_checktag(&udf_fe.tag, TAGID_FILE_ENTRY)) {
Packit dd8086
Packit dd8086
      if (ICBTAG_FILE_TYPE_DIRECTORY == udf_fe.icb_tag.file_type) {
Packit dd8086
	udf_dirent_t *p_udf_dirent_new =
Packit dd8086
	  udf_new_dirent(&udf_fe, p_udf, p_udf_dirent->psz_name, true, true);
Packit dd8086
	return p_udf_dirent_new;
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
udf_dirent_t *
Packit dd8086
udf_readdir(udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  udf_t *p_udf;
Packit dd8086
  uint8_t* p;
Packit dd8086
Packit dd8086
  if (p_udf_dirent->dir_left <= 0) {
Packit dd8086
    udf_dirent_free(p_udf_dirent);
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* file position must be reset when accessing a new file */
Packit dd8086
  p_udf = p_udf_dirent->p_udf;
Packit dd8086
  p_udf->i_position = 0;
Packit dd8086
Packit dd8086
  if (p_udf_dirent->fid) {
Packit dd8086
    /* advance to next File Identifier Descriptor */
Packit dd8086
    /* FIXME: need to advance file entry (fe) as well.  */
Packit dd8086
    uint32_t ofs = 4 *
Packit dd8086
      ((sizeof(*(p_udf_dirent->fid)) + p_udf_dirent->fid->u.i_imp_use
Packit dd8086
	+ p_udf_dirent->fid->i_file_id + 3) / 4);
Packit dd8086
Packit dd8086
    p_udf_dirent->fid =
Packit dd8086
      (udf_fileid_desc_t *)((uint8_t *)p_udf_dirent->fid + ofs);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (!p_udf_dirent->fid) {
Packit dd8086
    uint32_t i_sectors =
Packit dd8086
      (p_udf_dirent->i_loc_end - p_udf_dirent->i_loc + 1);
Packit dd8086
    uint32_t size = UDF_BLOCKSIZE * i_sectors;
Packit dd8086
    driver_return_code_t i_ret;
Packit dd8086
Packit dd8086
    if (!p_udf_dirent->sector)
Packit dd8086
      p_udf_dirent->sector = (uint8_t*) malloc(size);
Packit dd8086
    i_ret = udf_read_sectors(p_udf, p_udf_dirent->sector,
Packit dd8086
			     p_udf_dirent->i_part_start+p_udf_dirent->i_loc,
Packit dd8086
			     i_sectors);
Packit dd8086
    if (DRIVER_OP_SUCCESS == i_ret)
Packit dd8086
      p_udf_dirent->fid = (udf_fileid_desc_t *) p_udf_dirent->sector;
Packit dd8086
    else
Packit dd8086
      p_udf_dirent->fid = NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (p_udf_dirent->fid && !udf_checktag(&(p_udf_dirent->fid->tag), TAGID_FID))
Packit dd8086
    {
Packit dd8086
      uint32_t ofs =
Packit dd8086
	4 * ((sizeof(*p_udf_dirent->fid) + p_udf_dirent->fid->u.i_imp_use
Packit dd8086
	      + p_udf_dirent->fid->i_file_id + 3) / 4);
Packit dd8086
Packit dd8086
      p_udf_dirent->dir_left -= ofs;
Packit dd8086
      p_udf_dirent->b_dir =
Packit dd8086
	(p_udf_dirent->fid->file_characteristics & UDF_FILE_DIRECTORY) != 0;
Packit dd8086
      p_udf_dirent->b_parent =
Packit dd8086
	(p_udf_dirent->fid->file_characteristics & UDF_FILE_PARENT) != 0;
Packit dd8086
Packit dd8086
      {
Packit dd8086
	const unsigned int i_len = p_udf_dirent->fid->i_file_id;
Packit dd8086
Packit dd8086
	if (DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &p_udf_dirent->fe, p_udf->i_part_start
Packit dd8086
			 + uint32_from_le(p_udf_dirent->fid->icb.loc.lba), 1)) {
Packit dd8086
		udf_dirent_free(p_udf_dirent);
Packit dd8086
		return NULL;
Packit dd8086
	}
Packit dd8086
Packit dd8086
       free_and_null(p_udf_dirent->psz_name);
Packit dd8086
       p = (uint8_t*)p_udf_dirent->fid->u.imp_use.data + p_udf_dirent->fid->u.i_imp_use;
Packit dd8086
       p_udf_dirent->psz_name = unicode16_decode(p, i_len);
Packit dd8086
      }
Packit dd8086
      return p_udf_dirent;
Packit dd8086
    }
Packit dd8086
  udf_dirent_free(p_udf_dirent);
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  free free resources associated with p_udf_dirent.
Packit dd8086
*/
Packit dd8086
bool
Packit dd8086
udf_dirent_free(udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent) {
Packit dd8086
    p_udf_dirent->fid = NULL;
Packit dd8086
    free_and_null(p_udf_dirent->psz_name);
Packit dd8086
    free_and_null(p_udf_dirent->sector);
Packit dd8086
    free_and_null(p_udf_dirent);
Packit dd8086
  }
Packit dd8086
  return true;
Packit dd8086
}