First commit after CVS conversion. Should be just administrative changes.
This commit is contained in:
7
lib/udf/.cvsignore
Normal file
7
lib/udf/.cvsignore
Normal file
@@ -0,0 +1,7 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
.libs
|
||||
.deps
|
||||
*.lo
|
||||
libudf.la
|
||||
|
||||
3
lib/udf/.gitignore
vendored
Normal file
3
lib/udf/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/.deps
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
58
lib/udf/Makefile.am
Normal file
58
lib/udf/Makefile.am
Normal 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
291
lib/udf/filemode.c
Normal 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
29
lib/udf/libudf.sym
Normal 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
124
lib/udf/udf.c
Normal 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
251
lib/udf/udf_file.c
Normal 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
694
lib/udf/udf_fs.c
Normal 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
39
lib/udf/udf_fs.h
Normal 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
78
lib/udf/udf_private.h
Normal 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
255
lib/udf/udf_time.c
Normal 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user