Blame lib/udf/udf_file.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2005, 2006, 2008, 2010, 2012 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
/* Access routines */
Packit dd8086
Packit dd8086
/* udf_private.h has to come first else _FILE_OFFSET_BITS are redefined in
Packit dd8086
   say opensolaris. */
Packit dd8086
#include "udf_private.h"
Packit dd8086
#include <cdio/bytesex.h>
Packit dd8086
#include "udf_fs.h"
Packit dd8086
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
# include <string.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
#include <stdio.h>  /* Remove when adding cdio/logging.h */
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/* Useful defines */
Packit dd8086
Packit dd8086
#define MIN(a, b) (a
Packit dd8086
#define CEILING(x, y) ((x+(y-1))/y)
Packit dd8086
Packit dd8086
#define	GETICB(offset)	\
Packit dd8086
	&p_udf_fe->u.alloc_descs[offset]
Packit dd8086
Packit dd8086
const char *
Packit dd8086
udf_get_filename(const udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  if (!p_udf_dirent) return NULL;
Packit dd8086
  if (!p_udf_dirent->psz_name) return "..";
Packit dd8086
  return p_udf_dirent->psz_name;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Copy an UDF File Entry into a Directory Entry structure. */
Packit dd8086
bool
Packit dd8086
udf_get_file_entry(const udf_dirent_t *p_udf_dirent, 
Packit dd8086
		   /*out*/ udf_file_entry_t *p_udf_fe)
Packit dd8086
{
Packit dd8086
  if (!p_udf_dirent) return false;
Packit dd8086
  memcpy(p_udf_fe, &p_udf_dirent->fe, sizeof(udf_file_entry_t));
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the file id descriptor of the given file.
Packit dd8086
*/
Packit dd8086
bool udf_get_fileid_descriptor(const udf_dirent_t *p_udf_dirent, 
Packit dd8086
			       /*out*/ udf_fileid_desc_t *p_udf_fid)
Packit dd8086
{
Packit dd8086
  
Packit dd8086
  if (!p_udf_dirent) return false;
Packit dd8086
  if (!p_udf_dirent->fid) {
Packit dd8086
    /* FIXME do something about trying to get the descriptor. */
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
  memcpy(p_udf_fid, p_udf_dirent->fid, sizeof(udf_fileid_desc_t));
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the number of hard links of the file. Return 0 if error.
Packit dd8086
*/
Packit dd8086
uint16_t udf_get_link_count(const udf_dirent_t *p_udf_dirent) 
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent) {
Packit dd8086
    return uint16_from_le(p_udf_dirent->fe.link_count);
Packit dd8086
  }
Packit dd8086
  return 0; /* Error. Non-error case handled above. */
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the file length the file. Return 2147483647L if error.
Packit dd8086
*/
Packit dd8086
uint64_t udf_get_file_length(const udf_dirent_t *p_udf_dirent) 
Packit dd8086
{
Packit dd8086
  if (p_udf_dirent) {
Packit dd8086
    return uint64_from_le(p_udf_dirent->fe.info_len);
Packit dd8086
  }
Packit dd8086
  return 2147483647L; /* Error. Non-error case handled above. */
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return true if the file is a directory.
Packit dd8086
*/
Packit dd8086
bool
Packit dd8086
udf_is_dir(const udf_dirent_t *p_udf_dirent)
Packit dd8086
{
Packit dd8086
  return p_udf_dirent->b_dir;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*
Packit dd8086
 * Translate a file offset into a logical block and then into a physical
Packit dd8086
 * block.
Packit dd8086
 */
Packit dd8086
static lba_t
Packit dd8086
offset_to_lba(const udf_dirent_t *p_udf_dirent, off_t i_offset, 
Packit dd8086
	      /*out*/ lba_t *pi_lba, /*out*/ uint32_t *pi_max_size)
Packit dd8086
{
Packit dd8086
  udf_t *p_udf = p_udf_dirent->p_udf;
Packit dd8086
  const udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) 
Packit dd8086
    &p_udf_dirent->fe;
Packit dd8086
  const udf_icbtag_t *p_icb_tag = &p_udf_fe->icb_tag;
Packit dd8086
  const uint16_t strat_type= uint16_from_le(p_icb_tag->strat_type);
Packit dd8086
Packit dd8086
  if (i_offset < 0) {
Packit dd8086
    cdio_warn("Negative offset value");
Packit dd8086
    return CDIO_INVALID_LBA;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  switch (strat_type) {
Packit dd8086
  case 4096:
Packit dd8086
    cdio_warn("Cannot deal with strategy4096 yet!");
Packit dd8086
    return CDIO_INVALID_LBA;
Packit dd8086
    break;
Packit dd8086
  case ICBTAG_STRATEGY_TYPE_4:
Packit dd8086
    {
Packit dd8086
      off_t icblen = 0;
Packit dd8086
      uint64_t lsector;
Packit dd8086
      int ad_offset, ad_num = 0;
Packit dd8086
      uint16_t addr_ilk = uint16_from_le(p_icb_tag->flags&ICBTAG_FLAG_AD_MASK);
Packit dd8086
      
Packit dd8086
      switch (addr_ilk) {
Packit dd8086
      case ICBTAG_FLAG_AD_SHORT: 
Packit dd8086
	{
Packit dd8086
	  udf_short_ad_t *p_icb;
Packit dd8086
	  /*
Packit dd8086
	   * The allocation descriptor field is filled with short_ad's.
Packit dd8086
	   * If the offset is beyond the current extent, look for the
Packit dd8086
	   * next extent.
Packit dd8086
	   */
Packit dd8086
	  do {
Packit dd8086
	    i_offset -= icblen;
Packit dd8086
	    ad_offset = sizeof(udf_short_ad_t) * ad_num;
Packit dd8086
	    if (ad_offset > uint32_from_le(p_udf_fe->i_alloc_descs)) {
Packit dd8086
	      cdio_warn("File offset out of bounds");
Packit dd8086
	      return CDIO_INVALID_LBA;
Packit dd8086
	    }
Packit dd8086
	    p_icb = (udf_short_ad_t *) 
Packit dd8086
	      GETICB( uint32_from_le(p_udf_fe->i_extended_attr) 
Packit dd8086
		      + ad_offset );
Packit dd8086
	    icblen = p_icb->len;
Packit dd8086
	    ad_num++;
Packit dd8086
	  } while(i_offset >= icblen);
Packit dd8086
	  
Packit dd8086
	  lsector = (i_offset / UDF_BLOCKSIZE) + p_icb->pos;
Packit dd8086
	  
Packit dd8086
	  *pi_max_size = p_icb->len;
Packit dd8086
	}
Packit dd8086
	break;
Packit dd8086
      case ICBTAG_FLAG_AD_LONG: 
Packit dd8086
	{
Packit dd8086
	  /*
Packit dd8086
	   * The allocation descriptor field is filled with long_ad's
Packit dd8086
	   * If the i_offset is beyond the current extent, look for the
Packit dd8086
	   * next extent.
Packit dd8086
	   */
Packit dd8086
	  udf_long_ad_t *p_icb;
Packit dd8086
	  do {
Packit dd8086
	    i_offset -= icblen;
Packit dd8086
	    ad_offset = sizeof(udf_long_ad_t) * ad_num;
Packit dd8086
	    if (ad_offset > uint32_from_le(p_udf_fe->i_alloc_descs)) {
Packit dd8086
	      cdio_warn("File offset out of bounds");
Packit dd8086
	      return CDIO_INVALID_LBA;
Packit dd8086
	    }
Packit dd8086
	    p_icb = (udf_long_ad_t *) 
Packit dd8086
	      GETICB( uint32_from_le(p_udf_fe->i_extended_attr)
Packit dd8086
		      + ad_offset );
Packit dd8086
	    icblen = p_icb->len;
Packit dd8086
	    ad_num++;
Packit dd8086
	  } while(i_offset >= icblen);
Packit dd8086
	
Packit dd8086
	  lsector = (i_offset / UDF_BLOCKSIZE) +
Packit dd8086
	    uint32_from_le(((udf_long_ad_t *)(p_icb))->loc.lba);
Packit dd8086
	  
Packit dd8086
	  *pi_max_size = p_icb->len;
Packit dd8086
	}
Packit dd8086
	break;
Packit dd8086
      case ICBTAG_FLAG_AD_IN_ICB:
Packit dd8086
	/*
Packit dd8086
	 * This type means that the file *data* is stored in the
Packit dd8086
	 * allocation descriptor field of the file entry.
Packit dd8086
	 */
Packit dd8086
	*pi_max_size = 0;
Packit dd8086
	cdio_warn("Don't know how to data in ICB handle yet");
Packit dd8086
	return CDIO_INVALID_LBA;
Packit dd8086
      case ICBTAG_FLAG_AD_EXTENDED:
Packit dd8086
	cdio_warn("Don't know how to handle extended addresses yet");
Packit dd8086
	return CDIO_INVALID_LBA;
Packit dd8086
      default:
Packit dd8086
	cdio_warn("Unsupported allocation descriptor %d", addr_ilk);
Packit dd8086
	return CDIO_INVALID_LBA;
Packit dd8086
      }
Packit dd8086
Packit dd8086
      *pi_lba = (lba_t)lsector + p_udf->i_part_start;
Packit dd8086
      if (*pi_lba < 0) {
Packit dd8086
	cdio_warn("Negative LBA value");
Packit dd8086
	return CDIO_INVALID_LBA;
Packit dd8086
      }
Packit dd8086
      return *pi_lba;
Packit dd8086
    }
Packit dd8086
  default:
Packit dd8086
    cdio_warn("Unknown strategy type %d", strat_type);
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Attempts to read up to count bytes from UDF directory entry
Packit dd8086
  p_udf_dirent into the buffer starting at buf. buf should be a
Packit dd8086
  multiple of UDF_BLOCKSIZE bytes. Reading continues after the point
Packit dd8086
  at which we last read or from the beginning the first time.
Packit dd8086
Packit dd8086
  If count is zero, read() returns zero and has no other results. If
Packit dd8086
  count is greater than SSIZE_MAX, the result is unspecified.
Packit dd8086
Packit dd8086
  It is the caller's responsibility to ensure that count is less
Packit dd8086
  than the number of blocks recorded via p_udf_dirent.
Packit dd8086
Packit dd8086
  If there is an error, cast the result to driver_return_code_t for 
Packit dd8086
  the specific error code.
Packit dd8086
*/
Packit dd8086
ssize_t
Packit dd8086
udf_read_block(const udf_dirent_t *p_udf_dirent, void * buf, size_t count)
Packit dd8086
{
Packit dd8086
  if (count == 0) return 0;
Packit dd8086
  else {
Packit dd8086
    driver_return_code_t ret;
Packit dd8086
    uint32_t i_max_size=0;
Packit dd8086
    udf_t *p_udf = p_udf_dirent->p_udf;
Packit dd8086
    lba_t i_lba = offset_to_lba(p_udf_dirent, p_udf->i_position, &i_lba, 
Packit dd8086
				&i_max_size);
Packit dd8086
    if (i_lba != CDIO_INVALID_LBA) {
Packit dd8086
      uint32_t i_max_blocks = CEILING(i_max_size, UDF_BLOCKSIZE);
Packit dd8086
      if ( i_max_blocks < count ) {
Packit dd8086
	  cdio_warn("read count %u is larger than %u extent size.",
Packit dd8086
		  (unsigned int)count, i_max_blocks);
Packit dd8086
	  cdio_warn("read count truncated to %u", (unsigned int)count);
Packit dd8086
	  count = i_max_blocks;
Packit dd8086
      }
Packit dd8086
      ret = udf_read_sectors(p_udf, buf, i_lba, count);
Packit dd8086
      if (DRIVER_OP_SUCCESS == ret) {
Packit dd8086
	ssize_t i_read_len = MIN(i_max_size, count * UDF_BLOCKSIZE);
Packit dd8086
	p_udf->i_position += i_read_len;
Packit dd8086
	return i_read_len;
Packit dd8086
      }
Packit dd8086
      return ret;
Packit dd8086
    } else {
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
}