From 2f11a30fb36ab05c35382543bdc8003c8bc1a85e Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 13 Feb 2005 22:03:00 +0000 Subject: [PATCH] Merge in more Rock Ridge code. Not working yet. Hopefully not much breakage. (But there may be some especially on less-POSIX OS's.) --- include/cdio/iso9660.h | 26 ++- include/cdio/rock.h | 111 +++++++++---- lib/iso9660/Makefile.am | 3 +- lib/iso9660/iso9660_fs.c | 7 +- lib/iso9660/rock.c | 347 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 451 insertions(+), 43 deletions(-) create mode 100644 lib/iso9660/rock.c diff --git a/include/cdio/iso9660.h b/include/cdio/iso9660.h index 034ec905..3f62854d 100644 --- a/include/cdio/iso9660.h +++ b/include/cdio/iso9660.h @@ -1,5 +1,5 @@ /* - $Id: iso9660.h,v 1.64 2005/02/13 00:20:05 rocky Exp $ + $Id: iso9660.h,v 1.65 2005/02/13 22:03:00 rocky Exp $ Copyright (C) 2000 Herbert Valerio Riedel Copyright (C) 2003, 2004, 2005 Rocky Bernstein @@ -255,6 +255,8 @@ struct iso9660_ltime_s { } GNUC_PACKED; typedef struct iso9660_ltime_s iso9660_ltime_t; +typedef struct iso9660_dir_s iso9660_dir_t; +typedef struct iso9660_stat_s iso9660_stat_t; #include @@ -302,8 +304,6 @@ struct iso9660_dir_s { char filename[EMPTY_ARRAY_SIZE]; } GNUC_PACKED; -typedef struct iso9660_dir_s iso9660_dir_t; - /*! \brief ISO-9660 Primary Volume Descriptor. */ @@ -521,6 +521,23 @@ PRAGMA_END_PACKED @see iso9660_dir */ struct iso9660_stat_s { /* big endian!! */ + bool b_rock; /**< has Rock Ridge extension. + If not true then the next 7 feilds + aren't used. + */ + mode_t st_mode; /**< protection */ + nlink_t st_nlinks; /**< number of hard links */ + uid_t st_uid; /**< user ID of owner */ + gid_t st_gid; /**< group ID of owner */ + uint8_t s_rock_offset; + int i_size; +#if 0 + time_t st_atime; /**< time of last access */ + time_t st_mtime; /**< time of last modification */ + time_t st_ctime; /**< time of last change */ +#endif + + /* Non Ridge-specific fields */ struct tm tm; /**< time on entry */ lsn_t lsn; /**< start logical sector number */ uint32_t size; /**< total size in bytes */ @@ -530,9 +547,6 @@ struct iso9660_stat_s { /* big endian!! */ char filename[EMPTY_ARRAY_SIZE]; /**< filename */ }; -typedef struct iso9660_stat_s iso9660_stat_t; - - /** A mask used in iso9660_ifs_read_vd which allows what kinds of extensions we allow, eg. Joliet, Rock Ridge, etc. */ typedef uint8_t iso_extension_mask_t; diff --git a/include/cdio/rock.h b/include/cdio/rock.h index 5b58fc67..72c92b44 100644 --- a/include/cdio/rock.h +++ b/include/cdio/rock.h @@ -1,12 +1,10 @@ /* - $Id: rock.h,v 1.1 2005/02/13 00:20:05 rocky Exp $ + $Id: rock.h,v 1.2 2005/02/13 22:03:00 rocky Exp $ Copyright (C) 2005 Rocky Bernstein - See also rock.c by Eric Youngdale (1993) from GNU/Linux and in cdrtools. - This is - - Copyright 1993 Yggdrasil Computing, Incorporated + See also rock.c by Eric Youngdale (1993) from GNU/Linux + This is Copyright 1993 Yggdrasil Computing, Incorporated it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -70,16 +68,42 @@ extern enum cdio_rock_enums { PRAGMA_BEGIN_PACKED +/*! The next two structs are used by the system-use-sharing protocol + (SUSP), in which the Rock Ridge extensions are embedded. It is + quite possible that other extensions are present on the disk, and + this is fine as long as they all use SUSP. */ + +/*! system-use-sharing protocol */ +typedef struct iso_su_sp_s{ + unsigned char magic[2]; + uint8_t skip; +} GNUC_PACKED iso_su_sp_t; + +/*! system-use extension record */ +typedef struct iso_su_er_s { + iso711_t len_id; /**< Identifier length. Value 10?. */ + unsigned char len_des; + unsigned char len_src; + iso711_t ext_ver; /**< Extension version. Value 1? */ + char data[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED iso_su_er_t; + +typedef struct iso_su_ce_s { + char extent[8]; + char offset[8]; + char size[8]; +} iso_su_ce_t; + /*! POSIX file attributes, PX. See Section 4.1.2 */ typedef struct iso_rock_px_s { iso733_t st_mode; /*! file mode permissions; same as st_mode of POSIX:5.6.1 */ - iso733_t st_links; /*! number of links to file; same as st_links + iso733_t st_nlinks; /*! number of links to file; same as st_nlinks of POSIX:5.6.1 */ iso733_t st_uid; /*! user id owner of file; same as st_uid of POSIX:5.6.1 */ - iso733_t gid; /*! group id of file; same as st_gid of - of POSIX:5.6.1 */ + iso733_t st_gid; /*! group id of file; same as st_gid of + of POSIX:5.6.1 */ } GNUC_PACKED iso_rock_px_t ; /*! POSIX device number, PN. A PN is mandatory if the file type @@ -97,11 +121,29 @@ typedef struct iso_rock_pn_s { 7.2.3 encoded */ } GNUC_PACKED iso_rock_pn_t ; -/*! Symbolic link. See Section 4.1.3 */ -typedef struct iso_rock_sl_s { +/*! These are the bits and their meanings for flags in the SL structure. */ +typedef enum { + ISO_ROCK_SL_CONTINUE = 1, + ISO_ROCK_SL_CURRENT = 2, + ISO_ROCK_SL_PARENT = 4, + ISO_ROCK_SL_ROOT = 8 +} iso_rock_sl_flag_t; + +#define ISO_ROCK_SL_CONTINUE 1 +#define ISO_ROCK_SL_CURRENT 2 +#define ISO_ROCK_SL_PARENT 4 +#define ISO_ROCK_SL_ROOT 8 + +typedef struct iso_rock_sl_part_s { unsigned char flags; unsigned char len; char text[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED iso_rock_sl_part_t ; + +/*! Symbolic link. See Section 4.1.3 */ +typedef struct iso_rock_sl_s { + unsigned char flags; + iso_rock_sl_part_t link; } GNUC_PACKED iso_rock_sl_t ; /*! Alternate name. See Section 4.1.4 */ @@ -120,7 +162,7 @@ typedef struct iso_rock_pl_s { char location[1]; } GNUC_PACKED iso_rock_pl_t ; -/* These are the bits and their meanings for flags in the TF structure. */ +/*! These are the bits and their meanings for flags in the TF structure. */ typedef enum { ISO_ROCK_TF_CREATE = 1, ISO_ROCK_TF_MODIFY = 2, @@ -155,35 +197,38 @@ typedef struct iso_rock_sf_s { uint8_t table_depth; } GNUC_PACKED iso_rock_sf_t ; -/*! GNU/Linux-specific extension for transparent decompression. */ -typedef struct iso_rock_zf_s { - char algorithm[2]; - char parms[2]; - char real_size[8]; -} GNUC_PACKED iso_rock_zf_t ; - -typedef struct iso_rock_ridge_s { - char signature[2]; /**< signature word; either 'SP', 'CE', 'ER', 'RR', +typedef struct iso_extension_record_s { + char signature[2]; /**< signature word; either 'SP', 'CE', 'ER', 'RR', 'PX', 'PN', 'SL', 'NM', 'CL', 'PL', 'TF', or 'ZF' */ - iso711_t len; /** length of system-user area - 44 for PX - 20 for PN, 5+strlen(text) for SL. */ - iso711_t version; /** version number - value 1 */ + iso711_t len; /**< length of system-user area - 44 for PX + 20 for PN, 5+strlen(text) for SL, 21 for + SF, etc. */ + iso711_t version; /**< version number - value 1 */ union{ - iso_rock_px_t PX; - iso_rock_pn_t PN; - iso_rock_sl_t SL; - iso_rock_nm_t NM; - iso_rock_cl_t CL; - iso_rock_pl_t PL; - iso_rock_tf_t TF; - iso_rock_zf_t SF; - iso_rock_zf_t ZF; + iso_su_sp_t SP; /**< system-use-sharing protocol - not + strictly part of Rock Ridge */ + iso_su_er_t ER; /**< system-use extension packet - not + strictly part of Rock Ridge */ + iso_su_ce_t CE; /**< system-use - strictly part of Rock Ridge */ + iso_rock_px_t PX; /**< Rock Ridge POSIX file attributes */ + iso_rock_pn_t PN; /**< Rock Ridge POSIX device number */ + iso_rock_sl_t SL; /**< Rock Ridge symbolic link */ + iso_rock_nm_t NM; /**< Rock Ridge alternate name */ + iso_rock_cl_t CL; /**< Rock Ridge child link */ + iso_rock_pl_t PL; /**< Rock Ridge parent link */ + iso_rock_tf_t TF; /**< Rock Ridge timestamp(s) for a file */ } u; -} GNUC_PACKED iso_rock_ridge_t; +} GNUC_PACKED iso_extension_record_t; PRAGMA_END_PACKED +/*! return length of name field; 0: not found, -1: to be ignored */ +int get_rock_ridge_filename(iso9660_dir_t * de, /*out*/ char * retname, + /*out*/ iso9660_stat_t *p_stat); + +int parse_rock_ridge_stat(iso9660_dir_t *de, /*out*/ iso9660_stat_t *p_stat); + #endif /* __ISO_ROCK_H__ */ /* diff --git a/lib/iso9660/Makefile.am b/lib/iso9660/Makefile.am index 9812ae27..44661f78 100644 --- a/lib/iso9660/Makefile.am +++ b/lib/iso9660/Makefile.am @@ -1,4 +1,4 @@ -# $Id: Makefile.am,v 1.4 2005/01/27 03:26:37 rocky Exp $ +# $Id: Makefile.am,v 1.5 2005/02/13 22:03:00 rocky Exp $ # # Copyright (C) 2003, 2004, 2005 Rocky Bernstein # @@ -56,6 +56,7 @@ libiso9660_la_SOURCES = \ iso9660.c \ iso9660_private.h \ iso9660_fs.c \ + rock.c \ xa.c libiso9660_la_LIBADD = @LIBCDIO_LIBS@ diff --git a/lib/iso9660/iso9660_fs.c b/lib/iso9660/iso9660_fs.c index fbed03fe..5741940a 100644 --- a/lib/iso9660/iso9660_fs.c +++ b/lib/iso9660/iso9660_fs.c @@ -1,5 +1,5 @@ /* - $Id: iso9660_fs.c,v 1.12 2005/02/12 10:23:18 rocky Exp $ + $Id: iso9660_fs.c,v 1.13 2005/02/13 22:03:00 rocky Exp $ Copyright (C) 2001 Herbert Valerio Riedel Copyright (C) 2003, 2004, 2005 Rocky Bernstein @@ -18,6 +18,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* iso9660 filesystem-based routines */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -51,7 +52,7 @@ #include -static const char _rcsid[] = "$Id: iso9660_fs.c,v 1.12 2005/02/12 10:23:18 rocky Exp $"; +static const char _rcsid[] = "$Id: iso9660_fs.c,v 1.13 2005/02/13 22:03:00 rocky Exp $"; /* Implementation of iso9660_t type */ struct _iso9660_s { @@ -517,7 +518,7 @@ bool iso9660_ifs_get_volumeset_id(iso9660_t *p_iso, return true; } #endif /*HAVE_JOLIET*/ - *p_psz_volumeset_id = iso9660_get_volume_id( &(p_iso->pvd) ); + *p_psz_volumeset_id = iso9660_get_volumeset_id( &(p_iso->pvd) ); return *p_psz_volumeset_id != NULL && strlen(*p_psz_volumeset_id); } diff --git a/lib/iso9660/rock.c b/lib/iso9660/rock.c new file mode 100644 index 00000000..71e1e881 --- /dev/null +++ b/lib/iso9660/rock.c @@ -0,0 +1,347 @@ +/* + $Id: rock.c,v 1.1 2005/02/13 22:03:00 rocky Exp $ + + Copyright (C) 2005 Rocky Bernstein + Adapted from GNU/Linux fs/isofs/rock.c (C) 1992, 1993 Eric Youngdale + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* Rock Ridge Extensions to iso9660 */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_STDLIB_H +# include +#endif + +#include +#include +#include + +/* These functions are designed to read the system areas of a directory record + * and extract relevant information. There are different functions provided + * depending upon what information we need at the time. One function fills + * out an inode structure, a second one extracts a filename, a third one + * returns a symbolic link name, and a fourth one returns the extent number + * for the file. */ + +#define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */ + + +/* This is a way of ensuring that we have something in the system + use fields that is compatible with Rock Ridge */ +#define CHECK_SP(FAIL) \ + if(rr->u.SP.magic[0] != 0xbe) FAIL; \ + if(rr->u.SP.magic[1] != 0xef) FAIL; \ + p_stat->s_rock_offset = rr->u.SP.skip; +/* We define a series of macros because each function must do exactly the + same thing in certain places. We use the macros to ensure that everything + is done correctly */ + +#define CONTINUE_DECLS \ + int cont_extent = 0, cont_offset = 0, cont_size = 0; \ + void *buffer = NULL + +#define CHECK_CE \ + {cont_extent = from_733(*rr->u.CE.extent); \ + cont_offset = from_733(*rr->u.CE.offset); \ + cont_size = from_733(*rr->u.CE.size);} + +#define SETUP_ROCK_RIDGE(DE,CHR,LEN) \ + { \ + LEN= sizeof(iso9660_dir_t) + DE->filename_len; \ + if(LEN & 1) LEN++; \ + CHR = ((unsigned char *) DE) + LEN; \ + LEN = *((unsigned char *) DE) - LEN; \ + if (0xff != p_stat->s_rock_offset) \ + { \ + LEN -= p_stat->s_rock_offset; \ + CHR += p_stat->s_rock_offset; \ + if (LEN<0) LEN=0; \ + } \ + } + +/*! return length of name field; 0: not found, -1: to be ignored */ +int +get_rock_ridge_filename(iso9660_dir_t * de, /*out*/ char * retname, + /*out*/ iso9660_stat_t *p_stat) +{ + int len; + unsigned char *chr; + CONTINUE_DECLS; + int retnamlen = 0, truncate=0; + + if (!p_stat || !p_stat->b_rock) return 0; + *retname = 0; + + SETUP_ROCK_RIDGE(de, chr, len); + /* repeat:*/ + { + iso_extension_record_t * rr; + int sig; + + while (len > 1){ /* There may be one byte for padding somewhere */ + rr = (iso_extension_record_t *) chr; + if (rr->len == 0) goto out; /* Something got screwed up here */ + sig = from_721(*chr); + chr += rr->len; + len -= rr->len; + + switch(sig){ + case SIG('S','P'): + CHECK_SP(goto out); + break; + case SIG('C','E'): + CHECK_CE; + break; + case SIG('N','M'): + if (truncate) break; + /* + * If the flags are 2 or 4, this indicates '.' or '..'. + * We don't want to do anything with this, because it + * screws up the code that calls us. We don't really + * care anyways, since we can just use the non-RR + * name. + */ + if (rr->u.NM.flags & 6) { + break; + } + + if (rr->u.NM.flags & ~1) { + cdio_info("Unsupported NM flag settings (%d)",rr->u.NM.flags); + break; + } + if((strlen(retname) + rr->len - 5) >= 254) { + truncate = 1; + break; + } + strncat(retname, rr->u.NM.name, rr->len - 5); + retnamlen += rr->len - 5; + break; + case SIG('R','E'): + free(buffer); + return -1; + default: + break; + } + } + } + free(buffer); + return retnamlen; /* If 0, this file did not have a NM field */ + out: + free(buffer); + return 0; +} + +static int +parse_rock_ridge_stat_internal(iso9660_dir_t *de, + iso9660_stat_t *p_stat, int regard_xa) +{ + int len; + unsigned char * chr; + int symlink_len = 0; + CONTINUE_DECLS; + + if (!p_stat->b_rock) return 0; + + SETUP_ROCK_RIDGE(de, chr, len); + if (regard_xa) + { + chr+=14; + len-=14; + if (len<0) len=0; + } + + /* repeat:*/ + { + int sig; + iso_extension_record_t * rr; + int rootflag; + + while (len > 1){ /* There may be one byte for padding somewhere */ + rr = (iso_extension_record_t *) chr; + if (rr->len == 0) goto out; /* Something got screwed up here */ + sig = from_721(*chr); + chr += rr->len; + len -= rr->len; + + switch(sig){ + case SIG('S','P'): + CHECK_SP(goto out); + break; + case SIG('C','E'): + CHECK_CE; + break; + case SIG('E','R'): + p_stat->b_rock = 1; + cdio_debug("ISO 9660 Extensions: "); + { int p; + for(p=0;pu.ER.len_id;p++) cdio_debug("%c",rr->u.ER.data[p]); + } + break; + case SIG('P','X'): + p_stat->st_mode = from_733(rr->u.PX.st_mode); + p_stat->st_nlinks = from_733(rr->u.PX.st_nlinks); + p_stat->st_uid = from_733(rr->u.PX.st_uid); + p_stat->st_gid = from_733(rr->u.PX.st_gid); + break; +#ifdef DEV_FINISHED + case SIG('P','N'): + { int high, low; + high = from_733(*rr->u.PN.dev_high); + low = from_733(*rr->u.PN.dev_low); + /* + * The Rock Ridge standard specifies that if sizeof(dev_t) <= 4, + * then the high field is unused, and the device number is completely + * stored in the low field. Some writers may ignore this subtlety, + * and as a result we test to see if the entire device number is + * stored in the low field, and use that. + */ + if((low & ~0xff) && high == 0) { + p_stat->i_rdev = MKDEV(low >> 8, low & 0xff); + } else { + p_stat->i_rdev = MKDEV(high, low); + } + } + break; +#endif +#if TIME_FINISHED + case SIG('T','F'): + { + int cnt = 0; /* Rock ridge never appears on a High Sierra disk */ + /* Some RRIP writers incorrectly place ctime in the + ISO_ROCK_TF_CREATE field. Try to handle this correctly for + either case. */ + if(rr->u.TF.flags & ISO_ROCK_TF_CREATE) { + p_stat->i_ctime.tv_sec = iso_date(rr->u.TF.times[cnt++].time, 0); + p_stat->i_ctime.tv_nsec = 0; + } + if(rr->u.TF.flags & ISO_ROCK_TF_MODIFY) { + p_stat->i_mtime.tv_sec = iso_date(rr->u.TF.times[cnt++].time, 0); + p_stat->i_mtime.tv_nsec = 0; + } + if(rr->u.TF.flags & ISO_ROCK_TF_ACCESS) { + p_stat->i_atime.tv_sec = iso_date(rr->u.TF.times[cnt++].time, 0); + p_stat->i_atime.tv_nsec = 0; + } + if(rr->u.TF.flags & ISO_ROCK_TF_ATTRIBUTES) { + p_stat->i_ctime.tv_sec = iso_date(rr->u.TF.times[cnt++].time, 0); + p_stat->i_ctime.tv_nsec = 0; + } + break; + } +#endif /* TIME_FINISHED */ + case SIG('S','L'): + {int slen; + iso_rock_sl_part_t *slp; + iso_rock_sl_part_t *oldslp; + slen = rr->len - 5; + slp = &rr->u.SL.link; + p_stat->i_size = symlink_len; + while (slen > 1){ + rootflag = 0; + switch(slp->flags &~1){ + case 0: + p_stat->i_size += slp->len; + break; + case 2: + p_stat->i_size += 1; + break; + case 4: + p_stat->i_size += 2; + break; + case 8: + rootflag = 1; + p_stat->i_size += 1; + break; + default: + cdio_info("Symlink component flag not implemented"); + } + slen -= slp->len + 2; + oldslp = slp; + slp = (iso_rock_sl_part_t *) (((char *) slp) + slp->len + 2); + + if(slen < 2) { + if( ((rr->u.SL.flags & 1) != 0) + && ((oldslp->flags & 1) == 0) ) p_stat->i_size += 1; + break; + } + + /* + * If this component record isn't continued, then append a '/'. + */ + if (!rootflag && (oldslp->flags & 1) == 0) + p_stat->i_size += 1; + } + } + symlink_len = p_stat->i_size; + break; + case SIG('R','E'): + cdio_warn("Attempt to read p_stat for relocated directory"); + goto out; +#if FINISHED + case SIG('C','L'): + { + iso9660_stat_t * reloc; + ISOFS_I(p_stat)->i_first_extent = from_733(rr->u.CL.location); + reloc = isofs_iget(p_stat->i_sb, p_stat->i_first_extent, 0); + if (!reloc) + goto out; + p_stat->st_mode = reloc->st_mode; + p_stat->st_nlinks = reloc->st_nlinks; + p_stat->st_uid = reloc->st_uid; + p_stat->st_gid = reloc->st_gid; + p_stat->i_rdev = reloc->i_rdev; + p_stat->i_size = reloc->i_size; + p_stat->i_blocks = reloc->i_blocks; + p_stat->i_atime = reloc->i_atime; + p_stat->i_ctime = reloc->i_ctime; + p_stat->i_mtime = reloc->i_mtime; + iput(reloc); + } + break; +#endif + default: + break; + } + } + } + out: + free(buffer); + return 0; +} + +int +parse_rock_ridge_stat(iso9660_dir_t *de, /*out*/ iso9660_stat_t *p_stat) +{ + int result; + + if (!p_stat) return 0; + + result = parse_rock_ridge_stat_internal(de, p_stat, 0); + /* if Rock-Ridge flag was reset and we didn't look for attributes + * behind eventual XA attributes, have a look there */ + if (0xFF == p_stat->s_rock_offset && p_stat->b_rock) { + result = parse_rock_ridge_stat_internal(de, p_stat, 14); + } + return result; +}