First commit after CVS conversion. Should be just administrative changes.

This commit is contained in:
R. Bernstein
2008-11-29 00:56:26 -05:00
parent 4ea407f746
commit 95f087cdc3
413 changed files with 86786 additions and 86 deletions

7
lib/udf/.cvsignore Normal file
View File

@@ -0,0 +1,7 @@
Makefile
Makefile.in
.libs
.deps
*.lo
libudf.la

3
lib/udf/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/.deps
/Makefile
/Makefile.in

58
lib/udf/Makefile.am Normal file
View File

@@ -0,0 +1,58 @@
# $Id: Makefile.am,v 1.9 2008/06/25 08:01:54 rocky Exp $
#
# Copyright (C) 2003, 2004, 2006, 2008 Rocky Bernstein <rocky@gnu.org>
#
# This program 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.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
########################################################
# Things to make the libudf library
########################################################
#
# From libtool documentation amended with guidance from N. Boullis:
#
# 1. Start with version information of `0:0:0' for each libtool library.
#
# 2. It is probably not a good idea to update the version information
# several times between public releases, but rather once per public
# release. (This seems to be more an aesthetic consideration than
# a hard technical one.)
#
# 3. If the library source code has changed at all since the last
# update, then increment REVISION (`C:R:A' becomes `C:R+1:A').
#
# 4. If any interfaces have been added, removed, or changed since the
# last update, increment CURRENT, and set REVISION to 0.
#
# 5. If any interfaces have been added since the last public release,
# then increment AGE.
#
# 6. If any interfaces have been removed or changed since the last
# public release, then set AGE to 0. A changed interface means an
# incompatibility with previous versions.
libudf_la_CURRENT = 0
libudf_la_REVISION = 0
libudf_la_AGE = 0
EXTRA_DIST = libudf.sym
noinst_HEADERS = udf_fs.h udf_private.h
lib_LTLIBRARIES = libudf.la
libudf_la_SOURCES = udf.c udf_file.c udf_fs.c udf_time.c filemode.c
libudf_la_LIBADD = @LIBCDIO_LIBS@ @LT_NO_UNDEFINED@
INCLUDES = $(LIBCDIO_CFLAGS)

291
lib/udf/filemode.c Normal file
View File

@@ -0,0 +1,291 @@
/*
$Id: filemode.c,v 1.4 2008/04/18 16:02:10 karl Exp $
filemode.c -- make a string describing file modes
Copyright (C) 2005, 2008 Rocky Bernstein <rocky@gnu.org>
Copyright (C) 1985, 1990, 1993, 1998-2000 Free Software Foundation, Inc.
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <cdio/udf.h>
#if !S_IRUSR
# if S_IREAD
# define S_IRUSR S_IREAD
# else
# define S_IRUSR 00400
# endif
#endif
#if !S_IWUSR
# if S_IWRITE
# define S_IWUSR S_IWRITE
# else
# define S_IWUSR 00200
# endif
#endif
#if !S_IXUSR
# if S_IEXEC
# define S_IXUSR S_IEXEC
# else
# define S_IXUSR 00100
# endif
#endif
#if !S_IRGRP
# define S_IRGRP (S_IRUSR >> 3)
#endif
#if !S_IWGRP
# define S_IWGRP (S_IWUSR >> 3)
#endif
#if !S_IXGRP
# define S_IXGRP (S_IXUSR >> 3)
#endif
#if !S_IROTH
# define S_IROTH (S_IRUSR >> 6)
#endif
#if !S_IWOTH
# define S_IWOTH (S_IWUSR >> 6)
#endif
#if !S_IXOTH
# define S_IXOTH (S_IXUSR >> 6)
#endif
#ifdef STAT_MACROS_BROKEN
# undef S_ISBLK
# undef S_ISCHR
# undef S_ISDIR
# undef S_ISFIFO
# undef S_ISLNK
# undef S_ISMPB
# undef S_ISMPC
# undef S_ISNWK
# undef S_ISREG
# undef S_ISSOCK
#endif /* STAT_MACROS_BROKEN. */
#if !defined S_ISBLK && defined S_IFBLK
# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#endif
#if !defined S_ISCHR && defined S_IFCHR
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#endif
#if !defined S_ISDIR && defined S_IFDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#if !defined S_ISREG && defined S_IFREG
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#if !defined S_ISFIFO && defined S_IFIFO
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#endif
#if !defined S_ISLNK && defined S_IFLNK
# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#endif
#if !defined S_ISSOCK && defined S_IFSOCK
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
#endif
#if !defined S_ISMPB && defined S_IFMPB /* V7 */
# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
#endif
#if !defined S_ISNWK && defined S_IFNWK /* HP/UX */
# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
#endif
#if !defined S_ISDOOR && defined S_IFDOOR /* Solaris 2.5 and up */
# define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR)
#endif
#if !defined S_ISCTG && defined S_IFCTG /* MassComp */
# define S_ISCTG(m) (((m) & S_IFMT) == S_IFCTG)
#endif
/* Set the 's' and 't' flags in file attributes string CHARS,
according to the file mode BITS. */
static void
setst (mode_t bits, char *chars)
{
#ifdef S_ISUID
if (bits & S_ISUID)
{
if (chars[3] != 'x')
/* Set-uid, but not executable by owner. */
chars[3] = 'S';
else
chars[3] = 's';
}
#endif
#ifdef S_ISGID
if (bits & S_ISGID)
{
if (chars[6] != 'x')
/* Set-gid, but not executable by group. */
chars[6] = 'S';
else
chars[6] = 's';
}
#endif
#ifdef S_ISVTX
if (bits & S_ISVTX)
{
if (chars[9] != 'x')
/* Sticky, but not executable by others. */
chars[9] = 'T';
else
chars[9] = 't';
}
#endif
}
/* Return a character indicating the type of file described by
file mode BITS:
'd' for directories
'D' for doors
'b' for block special files
'c' for character special files
'n' for network special files
'm' for multiplexor files
'M' for an off-line (regular) file
'l' for symbolic links
's' for sockets
'p' for fifos
'C' for contigous data files
'-' for regular files
'?' for any other file type. */
static char
ftypelet (mode_t bits)
{
#ifdef S_ISBLK
if (S_ISBLK (bits))
return 'b';
#endif
if (S_ISCHR (bits))
return 'c';
if (S_ISDIR (bits))
return 'd';
if (S_ISREG (bits))
return '-';
#ifdef S_ISFIFO
if (S_ISFIFO (bits))
return 'p';
#endif
#ifdef S_ISLNK
if (S_ISLNK (bits))
return 'l';
#endif
#ifdef S_ISSOCK
if (S_ISSOCK (bits))
return 's';
#endif
#ifdef S_ISMPC
if (S_ISMPC (bits))
return 'm';
#endif
#ifdef S_ISNWK
if (S_ISNWK (bits))
return 'n';
#endif
#ifdef S_ISDOOR
if (S_ISDOOR (bits))
return 'D';
#endif
#ifdef S_ISCTG
if (S_ISCTG (bits))
return 'C';
#endif
/* The following two tests are for Cray DMF (Data Migration
Facility), which is a HSM file system. A migrated file has a
`st_dm_mode' that is different from the normal `st_mode', so any
tests for migrated files should use the former. */
#ifdef S_ISOFD
if (S_ISOFD (bits))
/* off line, with data */
return 'M';
#endif
#ifdef S_ISOFL
/* off line, with no data */
if (S_ISOFL (bits))
return 'M';
#endif
return '?';
}
/*! udf_mode_string - fill in string STR with an ls-style ASCII
representation of the st_mode field of file stats block STATP.
10 characters are stored in STR; no terminating null is added.
The characters stored in STR are:
0 File type. 'd' for directory, 'c' for character
special, 'b' for block special, 'm' for multiplex,
'l' for symbolic link, 's' for socket, 'p' for fifo,
'-' for regular, '?' for any other file type
1 'r' if the owner may read, '-' otherwise.
2 'w' if the owner may write, '-' otherwise.
3 'x' if the owner may execute, 's' if the file is
set-user-id, '-' otherwise.
'S' if the file is set-user-id, but the execute
bit isn't set.
4 'r' if group members may read, '-' otherwise.
5 'w' if group members may write, '-' otherwise.
6 'x' if group members may execute, 's' if the file is
set-group-id, '-' otherwise.
'S' if it is set-group-id but not executable.
7 'r' if any user may read, '-' otherwise.
8 'w' if any user may write, '-' otherwise.
9 'x' if any user may execute, 't' if the file is "sticky"
(will be retained in swap space after execution), '-'
otherwise.
'T' if the file is sticky but not executable. */
char *
udf_mode_string (mode_t i_mode, char *psz_str)
{
psz_str[ 0] = ftypelet (i_mode);
psz_str[ 1] = i_mode & S_IRUSR ? 'r' : '-';
psz_str[ 2] = i_mode & S_IWUSR ? 'w' : '-';
psz_str[ 3] = i_mode & S_IXUSR ? 'x' : '-';
psz_str[ 4] = i_mode & S_IRGRP ? 'r' : '-';
psz_str[ 5] = i_mode & S_IWGRP ? 'w' : '-';
psz_str[ 6] = i_mode & S_IXGRP ? 'x' : '-';
psz_str[ 7] = i_mode & S_IROTH ? 'r' : '-';
psz_str[ 8] = i_mode & S_IWOTH ? 'w' : '-';
psz_str[ 9] = i_mode & S_IXOTH ? 'x' : '-';
psz_str[10] = '\0';
setst (i_mode, psz_str);
return psz_str;
}

29
lib/udf/libudf.sym Normal file
View File

@@ -0,0 +1,29 @@
debug_ecma_167_enums1
debug_ecma_167_timezone_enum
debug_file_characteristics
debug_icbtag_file_type_enum
debug_tagid
debug_udf_enums1
debug_udf_time_enum
VSD_STD_ID_BEA01
VSD_STD_ID_BOOT2
VSD_STD_ID_CD001
VSD_STD_ID_CDW01
VSD_STD_ID_NSR03
VSD_STD_ID_TEA01
udf_close
udf_dirent_free
udf_get_file_entry
udf_get_file_length
udf_get_fileid_descriptor
udf_get_filename
udf_get_link_count
udf_get_part_number
udf_get_posix_filemode
udf_opendir
udf_readdir
udf_is_dir
udf_open
udf_read_sectors
udf_stamp_to_time
udf_time_to_stamp

124
lib/udf/udf.c Normal file
View File

@@ -0,0 +1,124 @@
/*
$Id: udf.c,v 1.13 2008/04/24 07:28:00 rocky Exp $
Copyright (C) 2005, 2008 Rocky Bernstein <rocky@gnu.org>
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Access routines */
#include <cdio/bytesex.h>
#include "udf_private.h"
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
/** The below variables are trickery to force enum symbol values to be
recorded in debug symbol tables. They are used to allow one to refer
to the enumeration value names in the typedefs above in a debugger
and debugger expressions
*/
tag_id_t debug_tagid;
file_characteristics_t debug_file_characteristics;
icbtag_file_type_enum_t debug_icbtag_file_type_enum;
icbtag_flag_enum_t debug_flag_enum;
ecma_167_enum1_t debug_ecma_167_enum1;
ecma_167_timezone_enum_t debug_ecma_167_timezone_enum;
udf_enum1_t debug_udf_enum1;
/*!
Returns POSIX mode bitstring for a given file.
*/
mode_t
udf_get_posix_filemode(const udf_dirent_t *p_udf_dirent)
{
udf_file_entry_t udf_fe;
mode_t mode = 0;
if (udf_get_file_entry(p_udf_dirent, &udf_fe)) {
uint16_t i_flags;
uint32_t i_perms;
i_perms = uint32_from_le(udf_fe.permissions);
i_flags = uint16_from_le(udf_fe.icb_tag.flags);
if (i_perms & FE_PERM_U_READ) mode |= S_IRUSR;
if (i_perms & FE_PERM_U_WRITE) mode |= S_IWUSR;
if (i_perms & FE_PERM_U_EXEC) mode |= S_IXUSR;
#ifdef S_IRGRP
if (i_perms & FE_PERM_G_READ) mode |= S_IRGRP;
if (i_perms & FE_PERM_G_WRITE) mode |= S_IWGRP;
if (i_perms & FE_PERM_G_EXEC) mode |= S_IXGRP;
#endif
#ifdef S_IROTH
if (i_perms & FE_PERM_O_READ) mode |= S_IROTH;
if (i_perms & FE_PERM_O_WRITE) mode |= S_IWOTH;
if (i_perms & FE_PERM_O_EXEC) mode |= S_IXOTH;
#endif
switch (udf_fe.icb_tag.file_type) {
case ICBTAG_FILE_TYPE_DIRECTORY:
mode |= S_IFDIR;
break;
case ICBTAG_FILE_TYPE_REGULAR:
mode |= S_IFREG;
break;
#ifdef S_IFLNK
case ICBTAG_FILE_TYPE_SYMLINK:
mode |= S_IFLNK;
break;
#endif
case ICBTAG_FILE_TYPE_CHAR:
mode |= S_IFCHR;
break;
#ifdef S_IFSOCK
case ICBTAG_FILE_TYPE_SOCKET:
mode |= S_IFSOCK;
break;
#endif
case ICBTAG_FILE_TYPE_BLOCK:
mode |= S_IFBLK;
break;
default: ;
};
#ifdef S_ISUID
if (i_flags & ICBTAG_FLAG_SETUID) mode |= S_ISUID;
if (i_flags & ICBTAG_FLAG_SETGID) mode |= S_ISGID;
if (i_flags & ICBTAG_FLAG_STICKY) mode |= S_ISVTX;
#endif
}
return mode;
}
/*!
Return the partition number of the the opened udf handle. -1
Is returned if we have an error.
*/
int16_t udf_get_part_number(const udf_t *p_udf)
{
if (!p_udf) return -1;
return p_udf->i_partition;
}

251
lib/udf/udf_file.c Normal file
View File

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

694
lib/udf/udf_fs.c Normal file
View File

@@ -0,0 +1,694 @@
/*
$Id: udf_fs.c,v 1.22 2008/04/18 16:02:10 karl Exp $
Copyright (C) 2005, 2006, 2008 Rocky Bernstein <rocky@gnu.org>
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Portions copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
/* These definitions are also to make debugging easy. Note that they
have to come *before* #include <cdio/ecma_167.h> which sets
#defines for these.
*/
const char VSD_STD_ID_BEA01[] = {'B', 'E', 'A', '0', '1'};
const char VSD_STD_ID_BOOT2[] = {'B', 'O', 'O', 'T', '2'};
const char VSD_STD_ID_CD001[] = {'C', 'D', '0', '0', '1'};
const char VSD_STD_ID_CDW01[] = {'C', 'D', 'W', '0', '2'};
const char VSD_STD_ID_NSR03[] = {'N', 'S', 'R', '0', '3'};
const char VSD_STD_ID_TEA01[] = {'T', 'E', 'A', '0', '1'};
#include <cdio/bytesex.h>
#include "udf_private.h"
#include "udf_fs.h"
/*
* The UDF specs are pretty clear on how each data structure is made
* up, but not very clear on how they relate to each other. Here is
* the skinny... This demostrates a filesystem with one file in the
* root directory. Subdirectories are treated just as normal files,
* but they have File Id Descriptors of their children as their file
* data. As for the Anchor Volume Descriptor Pointer, it can exist in
* two of the following three places: sector 256, sector n (the max
* sector of the disk), or sector n - 256. It's a pretty good bet
* that one will exist at sector 256 though. One caveat is unclosed
* CD media. For that, sector 256 cannot be written, so the Anchor
* Volume Descriptor Pointer can exist at sector 512 until the media
* is closed.
*
* Sector:
* 256:
* n: Anchor Volume Descriptor Pointer
* n - 256: |
* |
* |-->Main Volume Descriptor Sequence
* | |
* | |
* | |-->Logical Volume Descriptor
* | |
* |-->Partition Descriptor |
* | |
* | |
* |-->Fileset Descriptor
* |
* |
* |-->Root Dir File Entry
* |
* |
* |-->File data:
* File Id Descriptor
* |
* |
* |-->File Entry
* |
* |
* |-->File data
*/
static udf_dirent_t *
udf_new_dirent(udf_file_entry_t *p_udf_fe, udf_t *p_udf,
const char *psz_name, bool b_dir, bool b_parent);
/**
* Check the descriptor tag for both the correct id and correct checksum.
* Return zero if all is good, -1 if not.
*/
int
udf_checktag(const udf_tag_t *p_tag, udf_Uint16_t tag_id)
{
uint8_t *itag;
uint8_t i;
uint8_t cksum = 0;
itag = (uint8_t *)p_tag;
if (p_tag->id != tag_id)
return -1;
for (i = 0; i < 15; i++)
cksum = cksum + itag[i];
cksum = cksum - itag[4];
if (cksum == p_tag->cksum)
return 0;
return -1;
}
bool
udf_get_lba(const udf_file_entry_t *p_udf_fe,
/*out*/ uint32_t *start, /*out*/ uint32_t *end)
{
if (! p_udf_fe->i_alloc_descs)
return false;
switch (p_udf_fe->icb_tag.flags & ICBTAG_FLAG_AD_MASK) {
case ICBTAG_FLAG_AD_SHORT:
{
/* The allocation descriptor field is filled with short_ad's. */
udf_short_ad_t *p_ad = (udf_short_ad_t *)
(p_udf_fe->ext_attr + p_udf_fe->i_extended_attr);
*start = uint32_from_le(p_ad->pos);
*end = *start +
((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE;
return true;
}
break;
case ICBTAG_FLAG_AD_LONG:
{
/* The allocation descriptor field is filled with long_ad's */
udf_long_ad_t *p_ad = (udf_long_ad_t *)
(p_udf_fe->ext_attr + p_udf_fe->i_extended_attr);
*start = uint32_from_le(p_ad->loc.lba); /* ignore partition number */
*end = *start +
((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE;
return true;
}
break;
case ICBTAG_FLAG_AD_EXTENDED:
{
udf_ext_ad_t *p_ad = (udf_ext_ad_t *)
(p_udf_fe->ext_attr + p_udf_fe->i_extended_attr);
*start = uint32_from_le(p_ad->ext_loc.lba); /* ignore partition number */
*end = *start +
((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE;
return true;
}
break;
default:
return false;
}
return false;
}
#define udf_PATH_DELIMITERS "/\\"
/* Searches p_udf_dirent a directory entry called psz_token.
Note p_udf_dirent is continuously updated. If the entry is
not found p_udf_dirent is useless and thus the caller should
not use it afterwards.
*/
static
udf_dirent_t *
udf_ff_traverse(udf_dirent_t *p_udf_dirent, char *psz_token)
{
while (udf_readdir(p_udf_dirent)) {
if (strcmp(psz_token, p_udf_dirent->psz_name) == 0) {
char *next_tok = strtok(NULL, udf_PATH_DELIMITERS);
if (!next_tok)
return p_udf_dirent; /* found */
else if (p_udf_dirent->b_dir) {
udf_dirent_t * p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent2) {
udf_dirent_t * p_udf_dirent3 =
udf_ff_traverse(p_udf_dirent2, next_tok);
/* if p_udf_dirent3 is null p_udf_dirent2 is free'd. */
return p_udf_dirent3;
}
}
}
}
free(p_udf_dirent->psz_name);
return NULL;
}
/* FIXME! */
#define udf_MAX_PATHLEN 2048
udf_dirent_t *
udf_fopen(udf_dirent_t *p_udf_root, const char *psz_name)
{
udf_dirent_t *p_udf_file = NULL;
if (p_udf_root) {
char tokenline[udf_MAX_PATHLEN];
char *psz_token;
strncpy(tokenline, psz_name, udf_MAX_PATHLEN);
psz_token = strtok(tokenline, udf_PATH_DELIMITERS);
if (psz_token) {
/*** FIXME??? udf_dirent can be variable size due to the
extended attributes and descriptors. Given that, is this
correct?
*/
udf_dirent_t *p_udf_dirent =
udf_new_dirent(&p_udf_root->fe, p_udf_root->p_udf,
p_udf_root->psz_name, p_udf_root->b_dir,
p_udf_root->b_parent);
p_udf_file = udf_ff_traverse(p_udf_dirent, psz_token);
udf_dirent_free(p_udf_dirent);
}
else if ( 0 == strncmp("/", psz_name, sizeof("/")) ) {
return udf_new_dirent(&p_udf_root->fe, p_udf_root->p_udf,
p_udf_root->psz_name, p_udf_root->b_dir,
p_udf_root->b_parent);
}
}
return p_udf_file;
}
/* Convert unicode16 to 8-bit char by dripping MSB.
Wonder if iconv can be used here
*/
static int
unicode16_decode( const uint8_t *data, int i_len, char *target )
{
int p = 1, i = 0;
if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */
if( p < i_len ) {
target[ i++ ] = data[ p++ ];
}
} while( p < i_len );
target[ i ] = '\0';
return 0;
}
static udf_dirent_t *
udf_new_dirent(udf_file_entry_t *p_udf_fe, udf_t *p_udf,
const char *psz_name, bool b_dir, bool b_parent)
{
const unsigned int i_alloc_size = p_udf_fe->i_alloc_descs
+ p_udf_fe->i_extended_attr;
udf_dirent_t *p_udf_dirent = (udf_dirent_t *)
calloc(1, sizeof(udf_dirent_t) + i_alloc_size);
if (!p_udf_dirent) return NULL;
p_udf_dirent->psz_name = strdup(psz_name);
p_udf_dirent->b_dir = b_dir;
p_udf_dirent->b_parent = b_parent;
p_udf_dirent->p_udf = p_udf;
p_udf_dirent->i_part_start = p_udf->i_part_start;
p_udf_dirent->dir_left = uint64_from_le(p_udf_fe->info_len);
memcpy(&(p_udf_dirent->fe), p_udf_fe,
sizeof(udf_file_entry_t) + i_alloc_size);
udf_get_lba( p_udf_fe, &(p_udf_dirent->i_loc),
&(p_udf_dirent->i_loc_end) );
return p_udf_dirent;
}
/*!
Seek to a position i_start and then read i_blocks. Number of blocks read is
returned. One normally expects the return to be equal to i_blocks.
*/
driver_return_code_t
udf_read_sectors (const udf_t *p_udf, void *ptr, lsn_t i_start,
long int i_blocks)
{
driver_return_code_t ret;
long int i_read;
long int i_byte_offset;
if (!p_udf) return 0;
i_byte_offset = (i_start * UDF_BLOCKSIZE);
if (p_udf->b_stream) {
ret = cdio_stream_seek (p_udf->stream, i_byte_offset, SEEK_SET);
if (DRIVER_OP_SUCCESS != ret) return ret;
i_read = cdio_stream_read (p_udf->stream, ptr, UDF_BLOCKSIZE, i_blocks);
if (i_read) return DRIVER_OP_SUCCESS;
return DRIVER_OP_ERROR;
} else {
return cdio_read_data_sectors(p_udf->cdio, ptr, i_start, UDF_BLOCKSIZE,
i_blocks);
}
}
/*!
Open an UDF for reading. Maybe in the future we will have
a mode. NULL is returned on error.
Caller must free result - use udf_close for that.
*/
udf_t *
udf_open (const char *psz_path)
{
udf_t *p_udf = (udf_t *) calloc(1, sizeof(udf_t)) ;
uint8_t data[UDF_BLOCKSIZE];
if (!p_udf) return NULL;
p_udf->cdio = cdio_open(psz_path, DRIVER_UNKNOWN);
if (!p_udf->cdio) {
/* Not a CD-ROM drive or CD Image. Maybe it's a UDF file not
encapsulated as a CD-ROM Image (e.g. often .UDF or (sic) .ISO)
*/
p_udf->stream = cdio_stdio_new( psz_path );
if (!p_udf->stream)
goto error;
p_udf->b_stream = true;
}
/*
* Look for an Anchor Volume Descriptor Pointer at sector 256.
*/
if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, &data, 256, 1) )
goto error;
memcpy(&(p_udf->anchor_vol_desc_ptr), &data, sizeof(anchor_vol_desc_ptr_t));
if (udf_checktag((udf_tag_t *)&(p_udf->anchor_vol_desc_ptr), TAGID_ANCHOR))
goto error;
/*
* Then try to find a reference to a Primary Volume Descriptor.
*/
{
const anchor_vol_desc_ptr_t *p_avdp = &p_udf->anchor_vol_desc_ptr;
const uint32_t mvds_start =
uint32_from_le(p_avdp->main_vol_desc_seq_ext.loc);
const uint32_t mvds_end = mvds_start +
(uint32_from_le(p_avdp->main_vol_desc_seq_ext.len) - 1) / UDF_BLOCKSIZE;
uint32_t i_lba;
for (i_lba = mvds_start; i_lba < mvds_end; i_lba++) {
udf_pvd_t *p_pvd = (udf_pvd_t *) &data;
if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_pvd, i_lba, 1) )
goto error;
if (!udf_checktag(&p_pvd->tag, TAGID_PRI_VOL)) {
p_udf->pvd_lba = i_lba;
break;
}
}
/*
* If we couldn't find a reference, bail out.
*/
if (i_lba == mvds_end)
goto error;
}
return p_udf;
error:
free(p_udf);
return NULL;
}
/**
* Gets the Volume Identifier string, in 8bit unicode (latin-1)
* psz_volid, place to put the string
* i_volid_size, size of the buffer volid points to
* returns the size of buffer needed for all data
*/
int
udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid, unsigned int i_volid)
{
uint8_t data[UDF_BLOCKSIZE];
const udf_pvd_t *p_pvd = (udf_pvd_t *) &data;
unsigned int volid_len;
/* get primary volume descriptor */
if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) )
return 0;
volid_len = p_pvd->vol_ident[UDF_VOLID_SIZE-1];
if(volid_len > UDF_VOLID_SIZE-1) {
/* this field is only UDF_VOLID_SIZE bytes something is wrong */
volid_len = UDF_VOLID_SIZE-1;
}
if(i_volid > volid_len) {
i_volid = volid_len;
}
unicode16_decode((uint8_t *) p_pvd->vol_ident, i_volid, psz_volid);
return volid_len;
}
/**
* Gets the Volume Set Identifier, as a 128-byte dstring (not decoded)
* WARNING This is not a null terminated string
* volsetid, place to put the data
* volsetid_size, size of the buffer volsetid points to
* the buffer should be >=128 bytes to store the whole volumesetidentifier
* returns the size of the available volsetid information (128)
* or 0 on error
*/
int
udf_get_volumeset_id(udf_t *p_udf, /*out*/ uint8_t *volsetid,
unsigned int i_volsetid)
{
uint8_t data[UDF_BLOCKSIZE];
const udf_pvd_t *p_pvd = (udf_pvd_t *) &data;
/* get primary volume descriptor */
if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) )
return 0;
if (i_volsetid > UDF_VOLSET_ID_SIZE) {
i_volsetid = UDF_VOLSET_ID_SIZE;
}
memcpy(volsetid, p_pvd->volset_id, i_volsetid);
return UDF_VOLSET_ID_SIZE;
}
/*!
Get the root in p_udf. If b_any_partition is false then
the root must be in the given partition.
NULL is returned if the partition is not found or a root is not found or
there is on error.
Caller must free result - use udf_file_free for that.
*/
udf_dirent_t *
udf_get_root (udf_t *p_udf, bool b_any_partition, partition_num_t i_partition)
{
const anchor_vol_desc_ptr_t *p_avdp = &p_udf->anchor_vol_desc_ptr;
const uint32_t mvds_start =
uint32_from_le(p_avdp->main_vol_desc_seq_ext.loc);
const uint32_t mvds_end = mvds_start +
(uint32_from_le(p_avdp->main_vol_desc_seq_ext.len) - 1) / UDF_BLOCKSIZE;
uint32_t i_lba;
uint8_t data[UDF_BLOCKSIZE];
/*
Now we have the joy of finding the Partition Descriptor and the
Logical Volume Descriptor for the Main Volume Descriptor
Sequence. Once we've got that, we use the Logical Volume
Descriptor to get a Fileset Descriptor and that has the Root
Directory File Entry.
*/
for (i_lba = mvds_start; i_lba < mvds_end; i_lba++) {
uint8_t data[UDF_BLOCKSIZE];
partition_desc_t *p_partition = (partition_desc_t *) &data;
if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_partition, i_lba, 1) )
return NULL;
if (!udf_checktag(&p_partition->tag, TAGID_PARTITION)) {
const partition_num_t i_partition_check
= uint16_from_le(p_partition->number);
if (b_any_partition || i_partition_check == i_partition) {
/* Squirrel away some data regarding partition */
p_udf->i_partition = uint16_from_le(p_partition->number);
p_udf->i_part_start = uint32_from_le(p_partition->start_loc);
if (p_udf->lvd_lba) break;
}
} else if (!udf_checktag(&p_partition->tag, TAGID_LOGVOL)) {
/* Get fileset descriptor */
logical_vol_desc_t *p_logvol = (logical_vol_desc_t *) &data;
bool b_valid =
UDF_BLOCKSIZE == uint32_from_le(p_logvol->logical_blocksize);
if (b_valid) {
p_udf->lvd_lba = i_lba;
p_udf->fsd_offset =
uint32_from_le(p_logvol->lvd_use.fsd_loc.loc.lba);
if (p_udf->i_part_start) break;
}
}
}
if (p_udf->lvd_lba && p_udf->i_part_start) {
udf_fsd_t *p_fsd = (udf_fsd_t *) &data;
driver_return_code_t ret =
udf_read_sectors(p_udf, p_fsd, p_udf->i_part_start + p_udf->fsd_offset,
1);
if (DRIVER_OP_SUCCESS == ret && !udf_checktag(&p_fsd->tag, TAGID_FSD)) {
udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &data;
const uint32_t parent_icb = uint32_from_le(p_fsd->root_icb.loc.lba);
/* Check partition numbers match of last-read block? */
ret = udf_read_sectors(p_udf, p_udf_fe,
p_udf->i_part_start + parent_icb, 1);
if (ret == DRIVER_OP_SUCCESS &&
!udf_checktag(&p_udf_fe->tag, TAGID_FILE_ENTRY)) {
/* Check partition numbers match of last-read block? */
/* We win! - Save root directory information. */
return udf_new_dirent(p_udf_fe, p_udf, "/", true, false );
}
}
}
return NULL;
}
#define free_and_null(x) \
free(x); \
x=NULL
/*!
Close UDF and free resources associated with p_udf.
*/
bool
udf_close (udf_t *p_udf)
{
if (!p_udf) return true;
if (p_udf->b_stream) {
cdio_stdio_destroy(p_udf->stream);
} else {
cdio_destroy(p_udf->cdio);
}
/* Get rid of root directory if allocated. */
free_and_null(p_udf);
return true;
}
udf_dirent_t *
udf_opendir(const udf_dirent_t *p_udf_dirent)
{
if (p_udf_dirent->b_dir && !p_udf_dirent->b_parent && p_udf_dirent->fid) {
udf_t *p_udf = p_udf_dirent->p_udf;
uint8_t data[UDF_BLOCKSIZE];
udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &data;
driver_return_code_t i_ret =
udf_read_sectors(p_udf, p_udf_fe, p_udf->i_part_start
+ p_udf_dirent->fid->icb.loc.lba, 1);
if (DRIVER_OP_SUCCESS == i_ret
&& !udf_checktag(&p_udf_fe->tag, TAGID_FILE_ENTRY)) {
if (ICBTAG_FILE_TYPE_DIRECTORY == p_udf_fe->icb_tag.file_type) {
udf_dirent_t *p_udf_dirent_new =
udf_new_dirent(p_udf_fe, p_udf, p_udf_dirent->psz_name, true, true);
return p_udf_dirent_new;
}
}
}
return NULL;
}
udf_dirent_t *
udf_readdir(udf_dirent_t *p_udf_dirent)
{
udf_t *p_udf;
if (p_udf_dirent->dir_left <= 0) {
udf_dirent_free(p_udf_dirent);
return NULL;
}
p_udf = p_udf_dirent->p_udf;
if (p_udf_dirent->fid) {
/* advance to next File Identifier Descriptor */
/* FIXME: need to advance file entry (fe) as well. */
uint32_t ofs = 4 *
((sizeof(*(p_udf_dirent->fid)) + p_udf_dirent->fid->i_imp_use
+ p_udf_dirent->fid->i_file_id + 3) / 4);
p_udf_dirent->fid =
(udf_fileid_desc_t *)((uint8_t *)p_udf_dirent->fid + ofs);
}
if (!p_udf_dirent->fid) {
uint32_t i_sectors =
(p_udf_dirent->i_loc_end - p_udf_dirent->i_loc + 1);
uint32_t size = UDF_BLOCKSIZE * i_sectors;
driver_return_code_t i_ret;
if (!p_udf_dirent->sector)
p_udf_dirent->sector = (uint8_t*) malloc(size);
i_ret = udf_read_sectors(p_udf, p_udf_dirent->sector,
p_udf_dirent->i_part_start+p_udf_dirent->i_loc,
i_sectors);
if (DRIVER_OP_SUCCESS == i_ret)
p_udf_dirent->fid = (udf_fileid_desc_t *) p_udf_dirent->sector;
else
p_udf_dirent->fid = NULL;
}
if (p_udf_dirent->fid && !udf_checktag(&(p_udf_dirent->fid->tag), TAGID_FID))
{
uint32_t ofs =
4 * ((sizeof(*p_udf_dirent->fid) + p_udf_dirent->fid->i_imp_use
+ p_udf_dirent->fid->i_file_id + 3) / 4);
p_udf_dirent->dir_left -= ofs;
p_udf_dirent->b_dir =
(p_udf_dirent->fid->file_characteristics & UDF_FILE_DIRECTORY) != 0;
p_udf_dirent->b_parent =
(p_udf_dirent->fid->file_characteristics & UDF_FILE_PARENT) != 0;
{
const unsigned int i_len = p_udf_dirent->fid->i_file_id;
uint8_t data[UDF_BLOCKSIZE] = {0};
udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &data;
udf_read_sectors(p_udf, p_udf_fe, p_udf->i_part_start
+ p_udf_dirent->fid->icb.loc.lba, 1);
memcpy(&(p_udf_dirent->fe), p_udf_fe,
sizeof(udf_file_entry_t) + p_udf_fe->i_alloc_descs
+ p_udf_fe->i_extended_attr );
if (strlen(p_udf_dirent->psz_name) < i_len)
p_udf_dirent->psz_name = (char *)
realloc(p_udf_dirent->psz_name, sizeof(char)*i_len+1);
unicode16_decode(p_udf_dirent->fid->imp_use
+ p_udf_dirent->fid->i_imp_use,
i_len, p_udf_dirent->psz_name);
}
return p_udf_dirent;
}
return NULL;
}
/*!
free free resources associated with p_udf_dirent.
*/
bool
udf_dirent_free(udf_dirent_t *p_udf_dirent)
{
if (p_udf_dirent) {
p_udf_dirent->fid = NULL;
free_and_null(p_udf_dirent->psz_name);
free_and_null(p_udf_dirent->sector);
free_and_null(p_udf_dirent);
}
return true;
}

39
lib/udf/udf_fs.h Normal file
View File

@@ -0,0 +1,39 @@
/*
$Id: udf_fs.h,v 1.3 2008/04/18 16:02:10 karl Exp $
Copyright (C) 2006, 2008 Rocky Bernstein <rockyb@gnu.org>
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CDIO_UDF_FS_H__
#define __CDIO_UDF_FS_H__
#include <cdio/ecma_167.h>
/**
* Check the descriptor tag for both the correct id and correct checksum.
* Return zero if all is good, -1 if not.
*/
int udf_checktag(const udf_tag_t *p_tag, udf_Uint16_t tag_id);
#endif /* __CDIO_UDF_FS_H__ */
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

78
lib/udf/udf_private.h Normal file
View File

@@ -0,0 +1,78 @@
/*
$Id: udf_private.h,v 1.12 2008/04/18 16:02:10 karl Exp $
Copyright (C) 2005, 2006, 2008 Rocky Bernstein <rocky@gnu.org>
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CDIO_UDF_PRIVATE_H__
#define __CDIO_UDF_PRIVATE_H__
#if defined(HAVE_CONFIG_H) && !defined(LIBCDIO_CONFIG_H)
#include "config.h"
#endif
#include <cdio/types.h>
#include <cdio/ecma_167.h>
#include <cdio/udf.h>
#include "_cdio_stdio.h"
/* Implementation of opaque types */
struct udf_s {
bool b_stream; /* Use stream pointer, else use
p_cdio. */
ssize_t i_position; /* Position in file if positive. */
CdioDataSource_t *stream; /* Stream pointer if stream */
CdIo_t *cdio; /* Cdio pointer if read device */
anchor_vol_desc_ptr_t anchor_vol_desc_ptr;
uint32_t pvd_lba; /* sector of Primary Volume Descriptor */
partition_num_t i_partition; /* partition number */
uint32_t i_part_start; /* start of Partition Descriptor */
uint32_t lvd_lba; /* sector of Logical Volume Descriptor */
uint32_t fsd_offset; /* lba of fileset descriptor */
};
struct udf_dirent_s
{
char *psz_name;
bool b_dir; /* true if this entry is a directory. */
bool b_parent; /* True if has parent directory (e.g. not root
directory). If not set b_dir will probably
be true. */
udf_t *p_udf;
uint32_t i_part_start;
uint32_t i_loc, i_loc_end;
uint64_t dir_left;
uint8_t *sector;
udf_fileid_desc_t *fid;
/* This field has to come last because it is variable in length. */
udf_file_entry_t fe;
};
bool udf_get_lba(const udf_file_entry_t *p_udf_fe,
/*out*/ uint32_t *start, /*out*/ uint32_t *end);
#endif /* __CDIO_UDF_PRIVATE_H__ */
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

255
lib/udf/udf_time.c Normal file
View File

@@ -0,0 +1,255 @@
/*
$Id: udf_time.c,v 1.10 2008/04/24 07:28:00 rocky Exp $
Copyright (C) 2005, 2008 Rocky Bernstein <rocky@gnu.org>
Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
Modified From part of the GNU C Library.
Contributed by Paul Eggert.
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Some history from the GNU/Linux kernel from which this is also taken...
dgb 10/02/98: ripped this from glibc source to help convert
timestamps to unix time
10/04/98: added new table-based lookup after seeing how ugly the
gnu code is
blf 09/27/99: ripped out all the old code and inserted new table from
John Brockmeyer (without leap second corrections)
rewrote udf_stamp_to_time and fixed timezone
accounting in udf_timespec_to_stamp.
*/
/*
* We don't take into account leap seconds. This may be correct or incorrect.
* For more NIST information (especially dealing with leap seconds), see:
* http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef NEED_TIMEZONEVAR
#define timezonevar 1
#endif
#include "udf_private.h"
#include <cdio/udf.h>
/**
Imagine the below enum values as #define'd or constant values
rather than distinct values of an enum.
*/
enum {
HOURS_PER_DAY = 24,
SECS_PER_MINUTE = 60,
MAX_YEAR_SECONDS = 69,
DAYS_PER_YEAR = 365, /* That is, in most of the years. */
EPOCH_YEAR = 1970,
SECS_PER_HOUR = (60 * SECS_PER_MINUTE),
SECS_PER_DAY = SECS_PER_HOUR * HOURS_PER_DAY
} debug_udf_time_enum;
#ifndef __isleap
/* Nonzero if YEAR is a leap year (every 4 years,
except every 100th isn't, and every 400th is). */
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif
/* How many days come before each month (0-12). */
static const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, DAYS_PER_YEAR },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, DAYS_PER_YEAR+1 }
};
#define SPY(y,l,s) (SECS_PER_DAY * (DAYS_PER_YEAR*y+l)+s) /* Seconds per year */
static time_t year_seconds[MAX_YEAR_SECONDS]= {
/*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0),
/*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0),
/*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0),
/*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0),
/*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0),
/*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0),
/*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0),
/*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0),
/*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0),
/*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0),
/*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0),
/*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0),
/*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0),
/*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0),
/*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0),
/*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0),
/*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0),
/*2038*/ SPY(68,17,0)
};
#ifdef HAVE_TIMEZONE_VAR
extern long timezone;
#endif
time_t *
udf_stamp_to_time(time_t *dest, long int *dest_usec,
const udf_timestamp_t src)
{
int yday;
uint8_t type = src.type_tz >> 12;
int16_t offset;
if (type == 1) {
offset = src.type_tz << 4;
/* sign extent offset */
offset = (offset >> 4);
if (offset == -2047) /* unspecified offset */
offset = 0;
}
else
offset = 0;
if ((src.year < EPOCH_YEAR) ||
(src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS))
{
*dest = -1;
*dest_usec = -1;
return NULL;
}
*dest = year_seconds[src.year - EPOCH_YEAR];
*dest -= offset * SECS_PER_MINUTE;
yday = ((__mon_yday[__isleap (src.year)]
[src.month-1]) + (src.day-1));
*dest += src.second +
( SECS_PER_MINUTE *
( ( (yday* HOURS_PER_DAY) + src.hour ) * 60 + src.minute ) );
*dest_usec = src.microseconds
+ (src.centiseconds * 10000)
+ (src.hundreds_of_microseconds * 100);
return dest;
}
#ifdef HAVE_STRUCT_TIMESPEC
/*!
Convert a UDF timestamp to a time_t. If microseconds are desired,
use dest_usec. The return value is the same as dest. */
udf_timestamp_t *
udf_timespec_to_stamp(const struct timespec ts, udf_timestamp_t *dest)
{
long int days, rem, y;
const unsigned short int *ip;
int16_t offset = 0;
int16_t tv_sec;
#ifdef HAVE_TIMEZONE_VAR
offset = -timezone;
#endif
if (!dest)
return dest;
dest->type_tz = 0x1000 | (offset & 0x0FFF);
tv_sec = ts.tv_sec + (offset * SECS_PER_MINUTE);
days = tv_sec / SECS_PER_DAY;
rem = tv_sec % SECS_PER_DAY;
dest->hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
dest->minute = rem / SECS_PER_MINUTE;
dest->second = rem % SECS_PER_MINUTE;
y = EPOCH_YEAR;
#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
while (days < 0 || days >= (__isleap(y) ? DAYS_PER_YEAR+1 : DAYS_PER_YEAR)) {
long int yg = y + days / DAYS_PER_YEAR - (days % DAYS_PER_YEAR < 0);
/* Adjust DAYS and Y to match the guessed year. */
days -= ((yg - y) * DAYS_PER_YEAR
+ LEAPS_THRU_END_OF (yg - 1)
- LEAPS_THRU_END_OF (y - 1));
y = yg;
}
dest->year = y;
ip = __mon_yday[__isleap(y)];
for (y = 11; days < (long int) ip[y]; --y)
continue;
days -= ip[y];
dest->month = y + 1;
dest->day = days + 1;
dest->centiseconds = ts.tv_nsec / 10000000;
dest->hundreds_of_microseconds = ( (ts.tv_nsec / 1000)
- (dest->centiseconds * 10000) ) / 100;
dest->microseconds = ( (ts.tv_nsec / 1000)
- (dest->centiseconds * 10000)
- (dest->hundreds_of_microseconds * 100) );
return dest;
}
#endif
/*!
Return the modification time of the file.
*/
time_t
udf_get_modification_time(const udf_dirent_t *p_udf_dirent)
{
if (p_udf_dirent) {
time_t ret_time;
long int usec;
udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.modification_time);
return ret_time;
}
return 0;
}
/*!
Return the access time of the file.
*/
time_t
udf_get_access_time(const udf_dirent_t *p_udf_dirent)
{
if (p_udf_dirent) {
time_t ret_time;
long int usec;
udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.access_time);
return ret_time;
}
return 0;
}
/*!
Return the attribute (most recent create or access) time of the file
*/
time_t
udf_get_attribute_time(const udf_dirent_t *p_udf_dirent)
{
if (p_udf_dirent) {
time_t ret_time;
long int usec;
udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.attribute_time);
return ret_time;
}
return 0;
}