summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-07-18 20:39:01 +0000
committerThomas Bushnell <thomas@gnu.org>1997-07-18 20:39:01 +0000
commit274eaee48a9dd0a3b5ad0bb1dc44e5ed02daf388 (patch)
tree39b54ed3227af1a431ca9c261feca3490b0a93e8
parentab9c836772ab3dfdd28e769dfd53d16999900b7a (diff)
Initial versions.
-rw-r--r--isofs/ChangeLog0
-rw-r--r--isofs/Makefile29
-rw-r--r--isofs/ext.c60
-rw-r--r--isofs/inode.c550
-rw-r--r--isofs/iso9660.h125
-rw-r--r--isofs/isofs.h88
-rw-r--r--isofs/lookup.c448
-rw-r--r--isofs/main.c178
-rw-r--r--isofs/pager.c341
-rw-r--r--isofs/rr.c600
-rw-r--r--isofs/rr.h213
11 files changed, 2632 insertions, 0 deletions
diff --git a/isofs/ChangeLog b/isofs/ChangeLog
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/isofs/ChangeLog
diff --git a/isofs/Makefile b/isofs/Makefile
new file mode 100644
index 00000000..5e9c9f24
--- /dev/null
+++ b/isofs/Makefile
@@ -0,0 +1,29 @@
+
+# Copyright (C) 1997 Free Software Foundation
+#
+# 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ufs
+makemode := server
+
+target = isofs
+SRCS = inode.c main.c lookup.c pager.c rr.c
+LCLHDRS = iso9660.h isofs.h rr.h susp.h
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = diskfs iohelp fshelp store pager ports threads ihash shouldbeinlibc
+
+include ../Makeconf
+
diff --git a/isofs/ext.c b/isofs/ext.c
new file mode 100644
index 00000000..80379b7b
--- /dev/null
+++ b/isofs/ext.c
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+/* Extensions to ISO 9660 we support */
+
+#include "isofs.h"
+
+struct susp_field susp_extension[] =
+{
+ { 'C', 'E', 1, process_su_ce },
+ { 'P', 'D', 1, process_su_pd },
+ { 'S', 'P', 1, process_su_sp },
+ { 'E', 'R', 1, process_su_er },
+ { 'S', 'T', 1, process_su_st },
+ { 0, 0, 0, 0 },
+};
+
+struct susp_field rr_extension[] =
+{
+ { 'P', 'X', 1, process_rr_px },
+ { 'P', 'N', 1, process_rr_pn },
+ { 'S', 'L', 1, process_rr_sl },
+ { 'N', 'M', 1, process_rr_nm },
+ { 'C', 'L', 1, process_rr_cl },
+ { 'P', 'L', 1, process_rr_pl },
+ { 'R', 'E', 1, process_rr_re },
+ { 'T', 'F', 1, process_rr_tf },
+ { 'S', 'F', 1, process_rr_sf },
+ { 0, 0, 0, 0 },
+};
+
+struct susp_ext extensions[] =
+{
+ { "RRIP_1991A", 1,
+ "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS",
+ "ROCK RIDGE SPECIFICATION VERSION 1 REVISION 1.10 JULY 13 1993",
+ rr_extensions
+ },
+ { 0, 0, 0, 0, susp_extensions },
+ { 0, 0, 0, 0, 0 },
+}
+
+
diff --git a/isofs/inode.c b/isofs/inode.c
new file mode 100644
index 00000000..48079caf
--- /dev/null
+++ b/isofs/inode.c
@@ -0,0 +1,550 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#include <string.h>
+#include <stdio.h>
+#include "isofs.h"
+
+
+/* There is no such thing as an inode in this format, all such information
+ being recorded in the directory entry. So we report inode numbers as
+ absolute offsets from DISK_IMAGE. */
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(ino) ((ino>>8)&(INOHSZ-1))
+#else
+#define INOHASH(ino) (((unsigned)(ino>>8))%INOHSZ)
+#endif
+
+struct node_cache
+{
+ struct dirrect *dr; /* somewhere in disk_image */
+
+ off_t file_start; /* UNIQUE start of file */
+
+ struct node *np; /* if live */
+};
+
+static int node_cache_size = 0;
+static int node_cache_alloced = 0;
+struct node_cache *node_cache = 0;
+
+/* Forward */
+static error_t read_disknode (struct node *,
+ struct dirrect *, struct rrip_lookup *);
+
+
+/* See if node with file start FILE_START is in the cache. If so,
+ return it, with one additional reference. diskfs_node_refcnt_lock must
+ be held on entry to the call, and will be released iff the node
+ was found in the cache. */
+void
+inode_cache_find (off_t file_start, struct node **npp)
+{
+ int i;
+
+ for (i = 0; i < node_cache_size; i++)
+ if (node_cache[i].file_start == file_start
+ && node_cache[i].np)
+ {
+ *npp = node_cache[i].np;
+ (*npp)->references++;
+ spin_unlock (&diskfs_node_refcnt_lock);
+ mutex_lock (&(*npp)->lock);
+ return;
+ }
+ *npp = 0;
+}
+
+/* Enter NP into the cache. The directory entry we used
+ DR. diskfs_node_refcnt_lock must be held. */
+void
+cache_inode (struct dirrect *dr, struct node *np)
+{
+ int i;
+ struct node_cache *c = 0;
+
+ /* First see if there's already an entry. */
+ for (i = 0; i < node_cache_size; i++)
+ if (node_cache[i].file_start == np->dn->file_start)
+ break;
+
+ if (i == node_cache_size)
+ {
+ if (node_cache_size >= node_cache_alloced)
+ {
+ if (!node_cache_alloced)
+ {
+ /* Initialize */
+ node_cache_alloced = 10;
+ node_cache = malloc (sizeof (struct node_cache) * 10);
+ }
+ else
+ {
+ node_cache_alloced *= 2;
+ node_cache = realloc (node_cache,
+ sizeof (struct node_cache)
+ * node_cache_alloced);
+ }
+ assert (node_cache);
+ }
+ node_cache_size++;
+ }
+
+ c = &node_cache[i];
+ c->dr = dr;
+ c->file_start = np->dn->file_start;
+ c->np = np;
+
+ /* PLUS 1 so that we don't store zero cache ID's (not allowed by diskfs) */
+ np->cache_id = i + 1;
+}
+
+/* Fetch inode with cache id ID; set *NPP to the node structure;
+ gain one user reference and lock the node. */
+error_t
+diskfs_cached_lookup (int id, struct node **npp)
+{
+ struct node *np;
+ error_t err;
+
+ /* Cache ID's are incremented when presented to diskfs
+ to avoid presenting zero cache ID's. */
+ id--;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ assert (id < node_cache_size);
+
+ np = node_cache[id].np;
+
+ if (!np)
+ {
+ struct node_cache *c = &node_cache[id];
+ struct rrip_lookup rr;
+ struct disknode *dn;
+
+ rrip_lookup (node_cache[id].dr, &rr, 1);
+
+ /* We should never cache the wrong directory entry */
+ assert (!(rr.valid & VALID_CL));
+
+ dn = malloc (sizeof (struct disknode));
+ dn->fileinfo = 0;
+ dn->file_start = c->file_start;
+ np = diskfs_make_node (dn);
+ np->cache_id = id + 1; /* see above for rationale for increment */
+ mutex_lock (&np->lock);
+ c->np = np;
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = read_disknode (np, node_cache[id].dr, &rr);
+ if (!err)
+ *npp = np;
+
+ release_rrip (&rr);
+
+ return err;
+ }
+
+
+ np->references++;
+ spin_unlock (&diskfs_node_refcnt_lock);
+ mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+}
+
+
+/* Return Epoch-based time from a seven byte according to 9.1.5 */
+char *
+isodate_915 (char *c, struct timespec *ts)
+{
+ struct tm tm;
+ signed char tz;
+
+ /* Copy into a struct TM. */
+ tm.tm_year = *c++;
+ tm.tm_mon = *c++ - 1;
+ tm.tm_mday = *c++;
+ tm.tm_hour = *c++;
+ tm.tm_min = *c++;
+ tm.tm_sec = *c++;
+ tz = *c++;
+
+ tm.tm_isdst = 0;
+ ts->tv_sec = timegm (&tm);
+ ts->tv_nsec = 0;
+
+ /* Only honor TZ offset if it makes sense */
+ if (-48 <= tz && tz <= 52)
+ ts->tv_sec -= 15 * 60 * tz; /* TZ is in fifteen minute chunks */
+
+ return c;
+}
+
+/* Return Epoch-based time from a seventeen byte according to 8.4.26.1 */
+char *
+isodate_84261 (char *c, struct timespec *ts)
+{
+ struct tm tm;
+ int hsec;
+ signed char tz;
+
+ sscanf (c, "%4d%2d%2d%2d%2d%2d%2d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+ &hsec);
+
+ /* Convert to appropriate units */
+ ts->tv_nsec = hsec * 10000000;
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+
+ tm.tm_isdst = 0;
+ ts->tv_sec = timegm (&tm);
+
+ tz = c[16];
+
+ /* Only honor TZ offset if it makes sense */
+ if (-48 <= tz && tz <= 52)
+ ts->tv_sec -= 15 * 60 * tz; /* TZ is in fifteen minute chunks */
+
+ return c + 17;
+}
+
+/* Calculate the file start (in store blocks) of the file at RECORD. */
+error_t
+calculate_file_start (struct dirrect *record, off_t *file_start,
+ struct rrip_lookup *rr)
+{
+ error_t err;
+
+ if (rr && (rr->valid & VALID_CL))
+ {
+ *file_start = (void *) rr->realdirent - (void *)disk_image;
+ *file_start >>= store->log2_block_size;
+ }
+ else if (rr && (rr->valid & VALID_PL))
+ *file_start = rr->realfilestart;
+ else
+ {
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ *file_start = ((isonum_733 (record->extent) + record->ext_attr_len)
+ * (logical_block_size / store->block_size));
+
+ diskfs_end_catch_exception ();
+ }
+ return 0;
+}
+
+
+/* Load the inode with directory entry RECORD and cached Rock-Rodge info RR
+ into NP. The directory entry is at OFFSET in BLOCK. */
+error_t
+load_inode (struct node **npp, struct dirrect *record,
+ struct rrip_lookup *rr)
+{
+ error_t err;
+ off_t file_start;
+ struct disknode *dn;
+ struct node *np;
+
+ err = calculate_file_start (record, &file_start, rr);
+ if (err)
+ return err;
+ if (rr->valid & VALID_CL)
+ record = rr->realdirent;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+
+ /* First check the cache */
+ inode_cache_find (file_start, npp);
+ if (*npp)
+ return 0;
+
+ /* Create a new node */
+ dn = malloc (sizeof (struct disknode));
+ dn->fileinfo = 0;
+ dn->file_start = file_start;
+
+ np = diskfs_make_node (dn);
+
+ mutex_lock (&np->lock);
+
+ cache_inode (record, np);
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = read_disknode (np, record, rr);
+ *npp = np;
+ return err;
+}
+
+
+/* Read stat information from the directory entry at DR and the
+ contents of RL. */
+static error_t
+read_disknode (struct node *np, struct dirrect *dr,
+ struct rrip_lookup *rl)
+{
+ error_t err;
+ struct stat *st = &np->dn_stat;
+
+ st->st_fstype = 9660; /* xxx */
+ st->st_fsid = getpid ();
+ st->st_ino = np->dn->file_start;
+ st->st_gen = 0;
+ st->st_rdev = 0;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ if (rl->valid & VALID_PX)
+ {
+ st->st_mode = rl->mode;
+ st->st_nlink = rl->nlink;
+ st->st_uid = rl->uid;
+ st->st_gid = rl->gid;
+ }
+ else
+ {
+ /* If there are no periods, it's a directory. */
+ if (((rl->valid & VALID_NM) && !index (rl->name, '.'))
+ || (!(rl->valid & VALID_NM) && !memchr (dr->name, '.', dr->namelen)))
+ st->st_mode = S_IFDIR | 0777;
+ else
+ st->st_mode = S_IFREG | 0666;
+
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ }
+
+ st->st_author = st->st_gid;
+
+ st->st_size = isonum_733 (dr->size);
+
+ if ((rl->valid & VALID_PN)
+ && (S_ISCHR (st->st_mode) || S_ISBLK (st->st_mode)))
+ st->st_rdev = rl->rdev;
+ else
+ st->st_rdev = 0;
+
+ if (dr->ileave)
+ /* XXX ??? */
+ st->st_size = 0;
+
+ /* Calculate these if we'll need them */
+ if (!(rl->valid & VALID_TF)
+ || ((rl->tfflags & (TF_CREATION|TF_ACCESS|TF_MODIFY))
+ != (TF_CREATION|TF_ACCESS|TF_MODIFY)))
+ {
+ struct timespec ts;
+ isodate_915 (dr->date, &ts);
+ st->st_ctime = st->st_mtime = st->st_atime = ts.tv_sec;
+ st->st_ctime_usec = st->st_mtime_usec = st->st_atime_usec
+ = ts.tv_nsec * 1000;
+ }
+
+ /* Override what we have better info for */
+ if (rl->valid & VALID_TF)
+ {
+ if (rl->tfflags & TF_CREATION)
+ {
+ st->st_ctime = rl->ctime.tv_sec;
+ st->st_ctime_usec = rl->ctime.tv_nsec * 1000;
+ }
+
+ if (rl->tfflags & TF_ACCESS)
+ {
+ st->st_atime = rl->atime.tv_sec;
+ st->st_atime_usec = rl->atime.tv_nsec * 1000;
+ }
+
+ if (rl->tfflags & TF_MODIFY)
+ {
+ st->st_mtime = rl->mtime.tv_sec;
+ st->st_mtime_usec = rl->mtime.tv_nsec * 1000;
+ }
+ }
+
+ st->st_blksize = logical_block_size;
+ st->st_blocks = (st->st_size - 1) / logical_block_size + 1;
+ st->st_flags = 0;
+
+ if (S_ISLNK (st->st_mode))
+ {
+ if (rl->valid & VALID_SL)
+ {
+ np->dn->link_target = rl->name;
+ rl->name = 0;
+ st->st_size = strlen (np->dn->link_target);
+ }
+ else
+ {
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFREG;
+ }
+ }
+
+ diskfs_end_catch_exception ();
+
+ return 0;
+}
+
+/* Symlink targets are never stored in files, so always use this. */
+static error_t
+read_symlink_hook (struct node *np, char *buf)
+{
+ bcopy (np->dn->link_target, buf, np->dn_stat.st_size);
+ return 0;
+}
+error_t (*diskfs_read_symlink_hook) (struct node *, char *)
+ = read_symlink_hook;
+
+
+/* The last reference to NP has gone away; drop it from the cache
+ and clean all state in the dn structure. */
+void
+diskfs_node_norefs (struct node *np)
+{
+ assert (node_cache[np->cache_id - 1].np == np);
+ node_cache[np->cache_id - 1].np = 0;
+
+ assert (!np->dn->fileinfo);
+ free (np->dn);
+ free (np);
+}
+
+/* The last hard reference to a node has gone away; arrange to have
+ all the weak references dropped that can be. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+ drop_pager_softrefs (np);
+}
+
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+}
+
+void
+diskfs_new_hardrefs (struct node *np)
+{
+ allow_pager_softrefs (np);
+}
+
+error_t
+diskfs_truncate (struct node *np, off_t length)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_grow (struct node *np, off_t end, struct protid *cred)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_set_translator (struct node *np, char *name, u_int namelen,
+ struct protid *cred)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_get_translator (struct node *np, char **namep, u_int *namelen)
+{
+ return EOPNOTSUPP;
+}
+
+void
+diskfs_shutdown_soft_ports ()
+{
+ /* Should initiate termination of internally held pager ports
+ (the only things that should be soft) XXX */
+}
+
+error_t
+diskfs_node_reload (struct node *node)
+{
+ /* Never necessary on a read-only medium */
+ return 0;
+}
+
+error_t
+diskfs_validate_author_change (struct node *np, uid_t author)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_node_iterate (error_t (*fun)(struct node *))
+{
+ /* We never actually have to do anything, because this function
+ is only used for things that have to do with read-write media. */
+ return 0;
+}
+
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+}
+
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ /* XXX return something useful */
+ bzero (st, sizeof *st);
+ return 0;
+}
+
+error_t
+diskfs_S_file_get_storage_info (struct protid *cred,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ /* XXX */
+ return EOPNOTSUPP;
+}
+
+void
+diskfs_free_node (struct node *no, mode_t mode)
+{
+ abort ();
+}
+
+error_t
+diskfs_alloc_node (struct node *dp, mode_t mode, struct node **np)
+{
+ return EROFS;
+}
+
diff --git a/isofs/iso9660.h b/isofs/iso9660.h
new file mode 100644
index 00000000..2fd8cc2b
--- /dev/null
+++ b/isofs/iso9660.h
@@ -0,0 +1,125 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+/* Specification of ISO 9660 format */
+
+/* Volume descriptor */
+
+struct voldesc
+{
+ unsigned char type;
+ unsigned char id[5];
+ unsigned char version;
+ unsigned char data[0];
+};
+
+/* Volume descriptor types */
+#define VOLDESC_PRIMARY 1
+#define VOLDESC_END 255
+
+/* We don't support any other types */
+
+/* Expected ID */
+#define ISO_STANDARD_ID "CD001"
+
+/* Primary descriptor */
+struct sblock
+{
+ unsigned char type;
+ unsigned char id[5];
+ unsigned char version;
+ unsigned char skip1;
+ unsigned char sysid[32];
+ unsigned char volid[32];
+ unsigned char skip2[8];
+ unsigned char vol_sp_size[8]; /* total number of logical blocks */
+ unsigned char skip[32];
+ unsigned char vol_set_size[4];
+ unsigned char vol_seqno[4];
+ unsigned char blksize[4]; /* logical block size */
+ unsigned char ptsize[8];
+ unsigned char type_l_pt[4];
+ unsigned char opt_type_l_pt[4];
+ unsigned char type_m_pt[4];
+ unsigned char opt_type_m_pt[4];
+ unsigned char root[34];
+ unsigned char volset_id[128];
+ unsigned char pub_id[128];
+ unsigned char prep_id[128];
+ unsigned char app_id[128];
+ unsigned char copyr_id[37];
+ unsigned char abstr_id[37];
+ unsigned char biblio_id[37];
+ unsigned char creation_time[17];
+ unsigned char mod_time[17];
+ unsigned char expir_time[17];
+ unsigned char effect_time[17];
+ unsigned char file_structure;
+ unsigned char skip4;
+ unsigned char appl_data[512];
+ unsigned char skip5[652];
+};
+
+/* Directory record */
+struct dirrect
+{
+ unsigned char len;
+ unsigned char ext_attr_len;
+ unsigned char extent[8];
+ unsigned char size[8];
+ unsigned char date[7];
+ unsigned char flags;
+ unsigned char file_unit_size;
+ unsigned char ileave;
+ unsigned char vol_seqno[4];
+ unsigned char namelen;
+ unsigned char name[0];
+};
+
+
+
+/* Numeric conversions for these fields */
+
+#include <endian.h>
+
+static inline unsigned int
+isonum_733 (unsigned char *addr)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(unsigned int *)addr;
+#elif BYTE_ORDER == BIG_ENDIAN
+ return *(unsigned int *)(addr + 4);
+#else
+ return
+ addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24);
+#endif
+}
+
+static inline unsigned int
+isonum_723 (unsigned char *addr)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(unsigned short *)addr;
+#elif BYTE_ORDER == BIG_ENDIAN
+ return *(unsigned short *)addr + 2;
+#else
+ return addr[0] | (addr[1] << 8);
+#endif
+}
diff --git a/isofs/isofs.h b/isofs/isofs.h
new file mode 100644
index 00000000..25c449a6
--- /dev/null
+++ b/isofs/isofs.h
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+
+#include <sys/types.h>
+#include <hurd/diskfs.h>
+#include <hurd/diskfs-pager.h>
+#include <hurd/store.h>
+
+#include "rr.h"
+
+/* There is no such thing as an inode in this format, all such informatio n
+ being recorded in the directory entry. So we report inode numbers as
+ absolute offsets from DISK_IMAGE. */
+
+struct disknode
+{
+ off_t file_start; /* In store->block_size units */
+
+ struct user_pager_info *fileinfo;
+
+ char *link_target; /* for S_ISLNK */
+};
+
+struct user_pager_info
+{
+ struct node *np;
+ enum pager_type
+ {
+ DISK,
+ FILE_DATA,
+ } type;
+ struct pager *p;
+};
+
+/* The physical media */
+extern struct store *store;
+
+char *host_name;
+
+/* Name we are mounted on, with trailing slash */
+char *mounted_on;
+
+/* Mapped image of disk */
+void *disk_image;
+
+/* Processed sblock info */
+
+/* Block size of pointers etc. on disk (6.2.2). */
+size_t logical_block_size;
+
+/* Size of "logical sectors" (6.1.2). These are 2048 or the
+ largest power of two that will fit in a physical sector, whichever is
+ greater. I don't know how to fetch the physical sector size; so
+ we'll just use a constant. */
+#define logical_sector_size 2048
+
+/* Unprocessed superblock */
+struct sblock *sblock;
+
+
+
+void drop_pager_softrefs (struct node *);
+void allow_pager_softrefs (struct node *);
+void create_disk_pager (void);
+
+error_t load_inode (struct node **, struct dirrect *, struct rrip_lookup *);
+error_t calculate_file_start (struct dirrect *, off_t *, struct rrip_lookup *);
+
+char *isodate_915 (char *, struct timespec *);
+char *isodate_84261 (char *, struct timespec *);
diff --git a/isofs/lookup.c b/isofs/lookup.c
new file mode 100644
index 00000000..faed5d28
--- /dev/null
+++ b/isofs/lookup.c
@@ -0,0 +1,448 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#include <string.h>
+#include <dirent.h>
+#include "isofs.h"
+
+/* Forward */
+static error_t
+dirscanblock (void *, char *, size_t, struct dirrect **, struct rrip_lookup *);
+
+static int
+isonamematch (char *dirname, size_t dnamelen, char *username, size_t unamelen)
+{
+ /* Special representations for `.' and `..' */
+ if (dnamelen == 1 && dirname[0] == '\0')
+ return unamelen == 1 && username[0] == '.';
+
+ if (dnamelen == 1 && dirname[0] == '\1')
+ return unamelen == 2 && username[0] == '.' && username[1] == '.';
+
+ if (unamelen > dnamelen)
+ return 0;
+
+ if (!strncasecmp (dirname, username, unamelen))
+ {
+ /* A prefix has matched. Check if it's acceptable. */
+ if (dnamelen == unamelen)
+ return 1;
+
+ /* User has ommitted the version number */
+ if (dirname[unamelen] == ';')
+ return 1;
+
+ /* User has ommitted an empty extension */
+ if (dirname[unamelen] == '.'
+ && (dirname[unamelen+1] == '\0' || dirname[unamelen+1] == ';'))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Implement the diskfs_lookup callback from the diskfs library. See
+ <hurd/diskfs.h> for the interface specification. */
+error_t
+diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type,
+ struct node **npp, struct dirstat *ds, struct protid *cred)
+{
+ error_t err = 0;
+ struct dirrect *record;
+ int namelen;
+ int spec_dotdot;
+ void *buf;
+ void *blockaddr;
+ struct rrip_lookup rr;
+
+ if ((type == REMOVE) || (type == RENAME))
+ assert (npp);
+
+ if (npp)
+ *npp = 0;
+
+ spec_dotdot = type & SPEC_DOTDOT;
+ type &= ~SPEC_DOTDOT;
+
+ namelen = strlen (name);
+
+ /* This error is constant, but the errors for CREATE and REMOVE depend
+ on whether NAME exists. */
+ if (type == RENAME)
+ return EROFS;
+
+ buf = disk_image + (dp->dn->file_start << store->log2_block_size);
+
+ for (blockaddr = buf;
+ blockaddr < buf + dp->dn_stat.st_size;
+ blockaddr += logical_sector_size)
+ {
+ err = dirscanblock (blockaddr, name, namelen, &record, &rr);
+
+ if (!err)
+ break;
+
+ if (err != ENOENT)
+ return err;
+ }
+
+ if ((!err && type == REMOVE)
+ || (err == ENOENT && type == CREATE))
+ err = EROFS;
+
+ if (err)
+ return err;
+
+ /* Load the inode */
+ if (namelen == 2 && name[0] == '.' && name[1] == '.')
+ {
+ if (dp == diskfs_root_node)
+ err = EAGAIN;
+ else if (spec_dotdot)
+ {
+ /* renames and removes can't get this far. */
+ assert (type == LOOKUP);
+ diskfs_nput (dp);
+ err = load_inode (npp, record, &rr);
+ }
+ else
+ {
+ /* We don't have to do the normal rigamarole, because
+ we are permanently read-only, so things are necessarily
+ quiescent. Just be careful to honor the locking order. */
+ mutex_unlock (&dp->lock);
+ err = load_inode (npp, record, &rr);
+ mutex_lock (&dp->lock);
+ }
+ }
+ else if (namelen == 1 && name[0] == '.')
+ {
+ *npp = dp;
+ diskfs_nref (dp);
+ }
+ else
+ err = load_inode (npp, record, &rr);
+
+ release_rrip (&rr);
+ return err;
+}
+
+
+/* Scan one logical sector of directory contents (at address BLKADDR)
+ for NAME of length NAMELEN. Return its address in *RECORD. */
+static error_t
+dirscanblock (void *blkaddr, char *name, size_t namelen,
+ struct dirrect **record, struct rrip_lookup *rr)
+{
+ struct dirrect *entry;
+ void *currentoff;
+ size_t reclen;
+ size_t entry_namelen;
+ int matchrr;
+ int matchnormal;
+
+ for (currentoff = blkaddr;
+ currentoff < blkaddr + logical_sector_size;
+ currentoff += reclen)
+ {
+ entry = (struct dirrect *) currentoff;
+
+ reclen = entry->len;
+
+ /* Validate reclen */
+ if (reclen == 0
+ || reclen < sizeof (struct dirrect)
+ || currentoff + reclen > blkaddr + logical_sector_size)
+ break;
+
+ entry_namelen = entry->namelen;
+
+ /* More validation */
+ if (reclen < sizeof (struct dirrect) + entry_namelen)
+ break;
+
+ /* Check to see if the name maches the directory entry. */
+ if (isonamematch (entry->name, entry_namelen, name, namelen))
+ matchnormal = 1;
+ else
+ matchnormal = 0;
+
+ /* Now scan for RRIP fields */
+ matchrr = rrip_match_lookup (entry, name, namelen, rr);
+
+ /* Ignore RE entries */
+ if (rr->valid & VALID_RE)
+ {
+ release_rrip (rr);
+ continue;
+ }
+
+ /* See if the name matches */
+ if (((rr->valid & VALID_NM) && matchrr)
+ || (!(rr->valid & VALID_NM) && matchnormal))
+ {
+ /* We've got it. Return success */
+ *record = entry;
+ return 0;
+ }
+
+ release_rrip (rr);
+ }
+
+ /* Wasn't there. */
+ *record = 0;
+ return ENOENT;
+}
+
+error_t
+diskfs_get_directs (struct node *dp,
+ int entry,
+ int nentries,
+ char **data,
+ u_int *datacnt,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ volatile vm_size_t allocsize;
+ struct dirrect *ep;
+ struct dirent *userp;
+ int i;
+ void *dirbuf, *bufp;
+ char *datap;
+ volatile int ouralloc = 0;
+ error_t err;
+
+ /* Allocate some space to hold the returned data. */
+ allocsize = bufsiz ? round_page (bufsiz) : vm_page_size * 4;
+ if (allocsize > *datacnt)
+ {
+ vm_allocate (mach_task_self (), (vm_address_t *) data, allocsize, 1);
+ ouralloc = 1;
+ }
+
+ err = diskfs_catch_exception ();
+ if (err)
+ {
+ if (ouralloc)
+ vm_deallocate (mach_task_self (), (vm_address_t)*data, allocsize);
+ return err;
+ }
+
+ /* Skip to ENTRY */
+ dirbuf = disk_image + (dp->dn->file_start << store->log2_block_size);
+ bufp = dirbuf;
+ for (i = 0; i < entry; i ++)
+ {
+ struct rrip_lookup rr;
+
+ ep = (struct dirrect *) bufp;
+ rrip_lookup (ep, &rr, 0);
+
+ /* Ignore and skip RE entries */
+ if (rr.valid & VALID_RE)
+ {
+ bufp = bufp + ep->len;
+ release_rrip (&rr);
+ continue;
+ }
+
+ if (bufp - dirbuf >= dp->dn_stat.st_size)
+ {
+ /* Not that many entries in the directory; return nothing. */
+ if (allocsize > *datacnt)
+ vm_deallocate (mach_task_self (), (vm_address_t) data, allocsize);
+ *datacnt = 0;
+ *amt = 0;
+ return 0;
+ }
+ bufp = bufp + ep->len;
+
+ /* If BUFP points at a null, then we have hit the last
+ record in this logical sector. In that case, skip up to
+ the next logical sector. */
+ if (*(char *)bufp == '\0')
+ bufp = (void *) (((long) bufp & ~(logical_sector_size - 1))
+ + logical_sector_size);
+ }
+
+ /* Now copy entries one at a time */
+ i = 0;
+ datap = *data;
+ while (((nentries == -1) || (i < nentries))
+ && (!bufsiz || datap - *data < bufsiz)
+ && ((void *) bufp - dirbuf < dp->dn_stat.st_size))
+ {
+ struct rrip_lookup rr;
+ char *name;
+ size_t namlen, reclen;
+ off_t file_start;
+
+ ep = (struct dirrect *) bufp;
+
+ /* Fetch Rock-Ridge information for this file */
+ rrip_lookup (ep, &rr, 0);
+
+ /* Ignore and skip RE entries */
+ if (rr.valid & VALID_RE)
+ {
+ bufp = bufp + ep->len;
+ release_rrip (&rr);
+ continue;
+ }
+
+ /* See if there's room to hold this one */
+ name = rr.valid & VALID_NM ? rr.name : ep->name;
+ namlen = rr.valid & VALID_NM ? strlen (name) : ep->namelen;
+
+ /* Name frobnication */
+ if (!(rr.valid & VALID_NM))
+ {
+ if (namlen == 1 && name[0] == '\0')
+ {
+ name = ".";
+ namlen = 1;
+ }
+ else if (namlen == 1 && name[0] == '\1')
+ {
+ name = "..";
+ namlen = 2;
+ }
+ /* Perhaps downcase it too? */
+ }
+
+ reclen = sizeof (struct dirent) + namlen;
+ reclen = (reclen + 3) & ~3;
+
+ /* Expand buffer if necessary */
+ if (datap - *data + reclen > allocsize)
+ {
+ vm_address_t newdata;
+
+ vm_allocate (mach_task_self (), &newdata,
+ (ouralloc
+ ? (allocsize *= 2)
+ : (allocsize = vm_page_size * 2)), 1);
+ bcopy ((void *) *data, (void *)newdata, datap - *data);
+
+ if (ouralloc)
+ vm_deallocate (mach_task_self (), (vm_address_t) *data,
+ allocsize / 2);
+
+ datap = (char *) newdata + (datap - *data);
+ *data = (char *) newdata;
+ ouralloc = 1;
+ }
+
+ userp = (struct dirent *) datap;
+
+ /* Fill in entry */
+
+ err = calculate_file_start (ep, &file_start, &rr);
+ if (err)
+ {
+ diskfs_end_catch_exception ();
+ if (ouralloc)
+ vm_deallocate (mach_task_self (), (vm_address_t) *data, allocsize);
+ return err;
+ }
+
+ userp->d_fileno = file_start;
+ userp->d_type = DT_UNKNOWN;
+ userp->d_reclen = reclen;
+ userp->d_namlen = namlen;
+ bcopy (name, userp->d_name, namlen);
+ userp->d_name[namlen] = '\0';
+
+ /* And move along */
+ release_rrip (&rr);
+ datap = datap + reclen;
+ bufp = bufp + ep->len;
+ i++;
+
+ /* If BUFP points at a null, then we have hit the last
+ record in this logical sector. In that case, skip up to
+ the next logical sector. */
+ if (*(char *)bufp == '\0')
+ bufp = (void *) (((long) bufp & ~(logical_sector_size - 1))
+ + logical_sector_size);
+ }
+
+ diskfs_end_catch_exception ();
+
+ /* If we didn't use all the pages of a buffer we allocated, free
+ the excess. */
+ if (ouralloc
+ && round_page (datap - *data) < round_page (allocsize))
+ vm_deallocate (mach_task_self (), round_page (datap),
+ round_page (allocsize) - round_page (datap - *data));
+
+ /* Return */
+ *amt = i;
+ *datacnt = datap - *data;
+ return 0;
+}
+
+/* We have no dirstats at all. */
+size_t diskfs_dirstat_size = 0;
+
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+}
+
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ return 0;
+}
+
+/* These should never be called. */
+
+error_t
+diskfs_direnter_hard(struct node *dp,
+ char *name,
+ struct node *np,
+ struct dirstat *ds,
+ struct protid *cred)
+{
+ abort ();
+}
+
+error_t
+diskfs_dirremove_hard(struct node *dp,
+ struct dirstat *ds)
+{
+ abort ();
+}
+
+error_t
+diskfs_dirrewrite_hard(struct node *dp,
+ struct node *np,
+ struct dirstat *ds)
+{
+ abort ();
+}
+
+int
+diskfs_dirempty(struct node *dp,
+ struct protid *cred)
+{
+ abort ();
+}
diff --git a/isofs/main.c b/isofs/main.c
new file mode 100644
index 00000000..dcefd51d
--- /dev/null
+++ b/isofs/main.c
@@ -0,0 +1,178 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#include <string.h>
+#include <error.h>
+#include <argp.h>
+#include <version.h>
+#include <limits.h>
+#include "isofs.h"
+
+struct node *diskfs_root_node;
+struct store *store = 0;
+struct store_parsed *store_parsed = 0;
+char *diskfs_disk_name = 0;
+
+char *diskfs_server_name = "isofs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_extra_version = "GNU Hurd";
+int diskfs_synchronous = 0;
+
+int diskfs_link_max = INT_MAX;
+int diskfs_maxsymlinks = 8;
+
+int diskfs_readonly = 1;
+
+/* Fetch the root node */
+static void
+fetch_root ()
+{
+ struct rrip_lookup rl;
+ struct dirrect *dr;
+ error_t err;
+
+ dr = (struct dirrect *) sblock->root;
+
+ /* First check for SUSP and all relevant extensions */
+ rrip_initialize (dr);
+
+ /* Now rescan the node for real */
+ rrip_lookup (dr, &rl, 1);
+
+ /* And fetch the node. */
+ err = load_inode (&diskfs_root_node, dr, &rl);
+ assert_perror (err);
+
+ mutex_unlock (&diskfs_root_node->lock);
+}
+
+
+/* Find and read the superblock. */
+static void
+read_sblock ()
+{
+ struct voldesc *vd;
+ error_t err;
+ struct sblock * volatile sb = 0;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ error (4, err, "reading superblock");
+
+ /* Start at logical sector 16 and keep going until
+ we find a matching superblock */
+ for (vd = disk_image + (logical_sector_size * 16);
+ (void *) vd < disk_image + (logical_sector_size * 500); /* for sanity */
+ vd = (void *) vd + logical_sector_size)
+ {
+ if (vd->type == VOLDESC_END)
+ break;
+
+ if (vd->type == VOLDESC_PRIMARY
+ && !memcmp (ISO_STANDARD_ID, vd->id, 5)
+ && vd->version == 1)
+ {
+ /* Here's a valid primary descriptor. */
+ sb = (struct sblock *) vd;
+ break;
+ }
+ }
+
+ if (!sb)
+ error (1, 0, "Could not find valid superblock");
+
+ sblock = malloc (sizeof (struct sblock));
+ bcopy (sb, sblock, sizeof (struct sblock));
+ diskfs_end_catch_exception ();
+
+ /* Parse some important bits of this */
+ logical_block_size = isonum_723 (sblock->blksize);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct store_argp_params store_params = { 0 };
+
+ argp_parse (&diskfs_store_startup_argp, argc, argv, 0, 0, &store_params);
+ store_parsed = store_params.result;
+
+ err = store_parsed_name (store_parsed, &diskfs_disk_name);
+ if (err)
+ error (2, err, "store_parsed_name");
+
+ diskfs_console_stdio ();
+
+ if (diskfs_boot_flags)
+ bootstrap = MACH_PORT_NULL;
+ else
+ {
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+ }
+
+ err = diskfs_init_diskfs ();
+ if (err)
+ error (4, err, "init");
+
+ err = store_parsed_open (store_parsed, STORE_READONLY, &store);
+ if (err)
+ error (3, err, "%s", diskfs_disk_name);
+
+ diskfs_readonly = diskfs_hard_readonly = 1;
+
+ create_disk_pager ();
+
+ diskfs_spawn_first_thread ();
+
+ read_sblock ();
+
+ fetch_root ();
+
+ diskfs_startup_diskfs (bootstrap, 0);
+
+ cthread_exit (0);
+
+ return 0;
+}
+
+/* Nothing to do for read-only medium */
+error_t
+diskfs_reload_global_state ()
+{
+ return 0;
+}
+
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ return 0;
+}
+
+void
+diskfs_readonly_changed (int readonly)
+{
+ /* We should never get here because we define our own diskfs_set_readonly
+ above. */
+ abort ();
+}
diff --git a/isofs/pager.c b/isofs/pager.c
new file mode 100644
index 00000000..f02d2f35
--- /dev/null
+++ b/isofs/pager.c
@@ -0,0 +1,341 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#include <string.h>
+#include "isofs.h"
+
+spin_lock_t node2pagelock = SPIN_LOCK_INITIALIZER;
+
+struct port_bucket *pager_bucket;
+
+/* Mapped image of the disk */
+void *disk_image;
+
+
+/* Implement the pager_read_page callback from the pager library. See
+ <hurd/pager.h> for the interface definition. */
+error_t
+pager_read_page (struct user_pager_info *upi,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *writelock)
+{
+ error_t err;
+ daddr_t addr;
+ struct node *np = upi->np;
+ size_t read = 0;
+ size_t overrun = 0;
+
+ /* This is a read-only medium */
+ *writelock = 1;
+
+ if (upi->type == FILE_DATA)
+ {
+ addr = np->dn->file_start + (page >> store->log2_block_size);
+
+ if (page >= np->dn_stat.st_size)
+ {
+ vm_allocate (mach_task_self (), buf, vm_page_size, 1);
+ return 0;
+ }
+
+ if (page + vm_page_size > np->dn_stat.st_size)
+ overrun = page + vm_page_size - np->dn_stat.st_size;
+ }
+ else
+ {
+ assert (upi->type == DISK);
+ addr = page >> store->log2_block_size;
+ }
+
+ err = store_read (store, addr, vm_page_size, (void **) buf, &read);
+ if (read != vm_page_size)
+ return EIO;
+
+ if (overrun)
+ bzero ((void *) *buf + vm_page_size - overrun, overrun);
+
+ return 0;
+}
+
+/* This function should never be called. */
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ assert (0);
+}
+
+/* Never permit unlocks to succeed. */
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ return EROFS;
+}
+
+/* Tell how big the file is. */
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ *offset = 0;
+ *size = pager->np->dn_stat.st_size;
+ return 0;
+}
+
+/* Implement the pager_clear_user_data callback from the pager library. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ if (upi->type == FILE_DATA)
+ {
+ spin_lock (&node2pagelock);
+ if (upi->np->dn->fileinfo == upi)
+ upi->np->dn->fileinfo = 0;
+ spin_unlock (&node2pagelock);
+ diskfs_nrele_light (upi->np);
+ }
+ free (upi);
+}
+
+void
+pager_dropweak (struct user_pager_info *upi)
+{
+}
+
+
+/* Create the disk pager */
+void
+create_disk_pager (void)
+{
+ struct user_pager_info *upi = malloc (sizeof (struct user_pager_info));
+
+ upi->type = DISK;
+ upi->np = 0;
+ pager_bucket = ports_create_bucket ();
+ diskfs_start_disk_pager (upi, pager_bucket, 1, store->size, &disk_image);
+ upi->p = diskfs_disk_pager;
+}
+
+/* This need not do anything */
+void
+diskfs_file_update (struct node *np,
+ int wait)
+{
+}
+
+/* Create a FILE_DATA pager for the specified node */
+mach_port_t
+diskfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ struct user_pager_info *upi;
+ mach_port_t right;
+
+ assert (S_ISDIR (np->dn_stat.st_mode)
+ || S_ISREG (np->dn_stat.st_mode)
+ || S_ISLNK (np->dn_stat.st_mode));
+
+ spin_lock (&node2pagelock);
+
+ do
+ if (!np->dn->fileinfo)
+ {
+ upi = malloc (sizeof (struct user_pager_info));
+ upi->type = FILE_DATA;
+ upi->np = np;
+ diskfs_nref_light (np);
+ upi->p = pager_create (upi, pager_bucket, 1, MEMORY_OBJECT_COPY_DELAY);
+ np->dn->fileinfo = upi;
+ right = pager_get_port (np->dn->fileinfo->p);
+ ports_port_deref (np->dn->fileinfo->p);
+ }
+ else
+ {
+ /* Because NP->dn->fileinfo->p is not a real reference,
+ this might be nearly deallocated. If that's so, then
+ the port right will be null. In that case, clear here
+ and loop. The deallocation will complete separately. */
+ right = pager_get_port (np->dn->fileinfo->p);
+ if (right == MACH_PORT_NULL)
+ np->dn->fileinfo = 0;
+ }
+ while (right == MACH_PORT_NULL);
+
+ spin_unlock (&node2pagelock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ return right;
+}
+
+/* Call this when we should turn off caching so that unused memory
+ object ports get freed. */
+void
+drop_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 0, MEMORY_OBJECT_COPY_DELAY, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+/* Call this when we should turn on caching because it's no longer
+ important for unused memory object ports to get freed. */
+void
+allow_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+
+static void
+block_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1);
+ return 0;
+ }
+
+ /* Loop through the pagers and turn off caching one by one,
+ synchronously. That should cause termination of each pager. */
+ ports_bucket_iterate (pager_bucket, block_cache);
+}
+
+static void
+enable_caching ()
+{
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ struct user_pager_info *upi = pager_get_upi (p);
+
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+
+ /* It's possible that we didn't have caching on before, because
+ the user here is the only reference to the underlying node
+ (actually, that's quite likely inside this particular
+ routine), and if that node has no links. So dinkle the node
+ ref counting scheme here, which will cause caching to be
+ turned off, if that's really necessary. */
+ if (upi->type == FILE_DATA)
+ {
+ diskfs_nref (upi->np);
+ diskfs_nrele (upi->np);
+ }
+
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, enable_cache);
+}
+
+
+/* Tell diskfs if there are pagers exported, and if none, then
+ prevent any new ones from showing up. */
+int
+diskfs_pager_users ()
+{
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (npagers <= 1)
+ return 0;
+
+ block_caching ();
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ npagers = ports_count_bucket (pager_bucket);
+ if (npagers <= 1)
+ return 0;
+
+ /* Darn, there are actual honest users. Turn caching back on,
+ and return failure. */
+ enable_caching ();
+
+ ports_enable_bucket (pager_bucket);
+
+ return 1;
+}
+
+/* Return the bitwise or of the maximum prot parameter (the second arg to
+ diskfs_get_filemap) for all active user pagers. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ /* We never allow writing, so there's no need to carefully check it. */
+ return VM_PROT_READ | VM_PROT_EXECUTE;
+}
+
+/* Call this to find out the struct pager * corresponding to the
+ FILE_DATA pager of inode IP. This should be used *only* as a subsequent
+ argument to register_memory_fault_area, and will be deleted when
+ the kernel interface is fixed. NP must be locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *np)
+{
+ /* This is safe because fileinfo can't be cleared; there must be
+ an active mapping for this to be called. */
+ return np->dn->fileinfo->p;
+}
+
+/* Shutdown all the pagers. */
+void
+diskfs_shutdown_pager ()
+{
+ /* Because there's no need to ever sync, we don't have to do anything
+ here. */
+}
+
+/* Sync all the pagers. */
+void
+diskfs_sync_everything (int wait)
+{
+ /* ditto */
+}
diff --git a/isofs/rr.c b/isofs/rr.c
new file mode 100644
index 00000000..666eb6ec
--- /dev/null
+++ b/isofs/rr.c
@@ -0,0 +1,600 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+/* Parse Rock-Ridge and related SUSP conformant extensions. */
+
+#include <stdio.h>
+#include <string.h>
+#include "isofs.h"
+
+/* These tell whether the specified extensions are on or not. */
+int susp_live = 0;
+int rock_live = 0;
+
+/* How far to skip when reading SUSP fields. */
+int susp_skip = 0;
+
+void
+release_rrip (struct rrip_lookup *rr)
+{
+ if ((rr->valid & VALID_NM) && rr->name)
+ free (rr->name);
+ if ((rr->valid & VALID_SL) && rr->target)
+ free (rr->target);
+}
+
+
+/* Work function combining the three interfaces below. */
+static int
+rrip_work (struct dirrect *dr, struct rrip_lookup *rr,
+ char *match_name, char match_name_len,
+ int initializing, int ignorenm)
+{
+ void *bp, *terminus;
+ void *slbuf, *nmbuf;
+ size_t slbufsize, nmbufsize;
+ int nomorenm, nomoresl;
+
+ /* Initialize RR */
+ rr->valid = 0;
+ rr->target = rr->name = 0;
+
+ if (!susp_live && !initializing)
+ return 0;
+
+ /* The only extensions we currently support are rock-ridge, so
+ this test will cut a lot of useless work. */
+ if (!rock_live && !initializing)
+ return 0;
+
+ /* Initialized the name buffers */
+ nmbuf = slbuf = 0;
+ nmbufsize = slbufsize = 0;
+ nomorenm = nomoresl = 0;
+
+ /* Find the beginning and end of the SUSP area */
+
+ /* If this is the root node, from the root directory, then
+ we look in a special place. This only happens during the two
+ calls in fetch_root, and we know exactly what the value passed
+ is there. */
+
+ if (dr == (struct dirrect *)sblock->root)
+ {
+ struct dirrect *p;
+ off_t filestart;
+ char *c;
+ error_t err;
+
+ /* Look at the first directory entry in root. */
+ err = calculate_file_start (dr, &filestart, 0);
+ if (err)
+ return 0; /* give up */
+ p = disk_image + (filestart << store->log2_block_size);
+
+ /* Set C to the system use area. */
+ c = p->name + p->namelen;
+ if ((int)c & 1)
+ c++;
+
+ /* There needs to be an SUSP SP field right here; make sure there is */
+ if (!bcmp (c, "SP\7\1\276\357", 6))
+ bp = c;
+ else if (!bcmp (c + 15, "SP\7\1\276\357", 6))
+ /* Detect CD-ROM XA disk */
+ bp = c + 15;
+ else
+ /* No SUSP, give up. */
+ return 0;
+
+ terminus = (char *) p + p->len;
+ }
+ else
+ {
+ /* It's in the normal place. */
+ bp = dr->name + dr->namelen;
+ if ((int) bp & 1)
+ bp++; /* must be even */
+ bp += susp_skip; /* skip to start of susp area */
+ terminus = (char *) dr + dr->len;
+ }
+
+
+ /* Loop across all the fields, processing them one at a time. */
+
+ while (bp < terminus)
+ {
+ struct su_header *susp = bp;
+ void *body;
+
+ /* Make sure the whole thing fits */
+ if (bp + sizeof (struct su_header) > terminus
+ || bp + susp->len > terminus)
+ break;
+
+ body = (char *) susp + sizeof (struct su_header);
+
+ /* CE means that further extension fields are elsewhere on
+ the disk. We just reset the pointers and keep going. */
+ if (susp->sig[0] == 'C'
+ && susp->sig[1] == 'E'
+ && susp->version == 1)
+ {
+ int offset;
+ int location;
+ int size;
+ struct su_ce *ce = body;
+
+ offset = isonum_733 (ce->offset);
+ location = isonum_733 (ce->continuation);
+ size = isonum_733 (ce->size);
+
+ /* Reset pointers */
+ bp = disk_image + (location * logical_block_size) + offset;
+ terminus = bp + size;
+
+ /* NOT goto next_field */
+ continue;
+ }
+
+ /* Only on the root node; SP signals that the sharing protocol
+ is in use. */
+ if (initializing
+ && susp->sig[0] == 'S'
+ && susp->sig[1] == 'P'
+ && susp->version == 1)
+ {
+ /* Sharing Protocol */
+ struct su_sp *sp = body;
+
+ /* Verify magic numbers */
+ if (sp->check[0] == SU_SP_CHECK_0
+ && sp->check[1] == SU_SP_CHECK_1)
+ susp_live = 1;
+
+ susp_skip = sp->skip;
+
+ goto next_field;
+ }
+
+ /* Only on the root node; ER signals that a specified extension
+ is present. We implement and check for only the Rock Ridge
+ extension. */
+ if (initializing
+ && susp->sig[0] == 'E'
+ && susp->sig[1] == 'R'
+ && susp->version == 1)
+ {
+ /* Extension Reference */
+ struct su_er *er = body;
+ char *c;
+
+ /* The only extension we currently support is Rock-Ridge. */
+
+ /* Make sure the ER field is valid */
+ if ((void *) er->more + er->len_id + er->len_des + er->len_src
+ < terminus)
+ goto nomatch;
+
+ if (er->ext_ver != ROCK_VERS)
+ goto nomatch;
+
+ c = er->more;
+ if (memcmp (ROCK_ID, c, er->len_id))
+ goto nomatch;
+
+ /* At this point we know we have Rock-Ridge, but
+ we check these and moan about it if they are wrong. */
+
+ c += er->len_id;
+ if (memcmp (ROCK_DES, c, er->len_des))
+ fprintf (stderr, "isofs warning: Rock-Ridge extension description is not standard: %s\n", c);
+
+ c += er->len_des;
+ if (memcmp (ROCK_SRC, c, er->len_src))
+ fprintf (stderr, "isofs warning: Rock-Ridge extensions source is not standard: %s\n", c);
+
+ rock_live = 1;
+
+ nomatch:
+ goto next_field;
+ }
+
+ /* PD fields are padding and just get ignored. */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'D'
+ && susp->version == 1)
+ goto next_field;
+
+ /* ST fields mean that there are no more SUSP fields to be processed. */
+ if (susp->sig[0] == 'S'
+ && susp->sig[1] == 'T'
+ && susp->version == 1)
+ /* All done */
+ break;
+
+ /* The rest are Rock-Ridge, and are not interesting if we are doing
+ setup. */
+
+ if (initializing || !rock_live)
+ goto next_field;
+
+ /* RE is present in a directory entry to mean that the node
+ is specified by a CL field elsewhere. So this entry needs
+ to be ignored by anyone who understands CL fields. */
+ if (susp->sig[0] == 'R'
+ && susp->sig[1] == 'E'
+ && susp->version == 1)
+ {
+ rr->valid |= VALID_RE;
+
+ /* No point in parsing anything else now. */
+ break;
+ }
+
+ /* NM identifies the real name of the file; it overrides
+ the name in the directory. */
+ if (susp->sig[0] == 'N'
+ && susp->sig[1] == 'M'
+ && susp->version == 1
+ && !ignorenm)
+ {
+ struct rr_nm *nm = body;
+ size_t nmlen = susp->len - 5;
+ char *name;
+ size_t namelen;
+
+ if (nomorenm)
+ goto next_field;
+
+ if (nm->flags & NAME_DOT)
+ {
+ name = ".";
+ namelen = 1;
+ goto finalize_nm;
+ }
+ else if (nm->flags & NAME_DOTDOT)
+ {
+ name = "..";
+ namelen = 2;
+ goto finalize_nm;
+ }
+ else if (nm->flags & NAME_HOST)
+ {
+ name = host_name;
+ namelen = strlen (host_name);
+ goto finalize_nm;
+ }
+
+ /* Add this component to the list. */
+
+ /* We don't store a trailing null here, but we always leave
+ room for it. The null gets stored in the finalization
+ code below. */
+ if (!nmbuf)
+ nmbuf = malloc ((nmbufsize = nmlen) + 1);
+ else
+ nmbuf = realloc (nmbuf, (nmbufsize += nmlen) + 1);
+ assert (nmbuf);
+
+ bcopy (nm->name, nmbuf + nmbufsize - nmlen, nmlen);
+
+ if (nm->flags & NAME_CONTINUE)
+ goto next_field;
+
+ name = nmbuf;
+ namelen = nmbufsize;
+
+ finalize_nm:
+ nomorenm = 1;
+
+ /* Is this a failed match? */
+ if (match_name && (match_name_len != namelen
+ || memcmp (match_name, name, match_name_len)))
+ return 0;
+
+ /* Store the name */
+ rr->valid |= VALID_NM;
+ if (name != nmbuf)
+ {
+ rr->name = malloc (namelen + 1);
+ strcpy (rr->name, name);
+ }
+ else
+ {
+ rr->name = name;
+ name[namelen] = '\0';
+ }
+
+ if (rr->valid & VALID_CL)
+ /* Finalize CL processing. */
+ goto clrecurse;
+
+ goto next_field;
+ }
+
+ /* PX gives mode, nlink, uid, and gid posix-style attributes. */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'X'
+ && susp->version == 1)
+ {
+ struct rr_px *px = body;
+
+ rr->valid |= VALID_PX;
+
+ rr->mode = isonum_733 (px->mode);
+ rr->nlink = isonum_733 (px->nlink);
+ rr->uid = isonum_733 (px->uid);
+ rr->gid = isonum_733 (px->gid);
+
+ goto next_field;
+ }
+
+ /* PN, for S_ISCHR and S_ISDEV devices gives the magic numbers */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'N'
+ && susp->version == 1)
+ {
+#define makedev(maj,min) ((((maj)&0xFF)<<8)+((min)&0xFF))
+ struct rr_pn *pn = body;
+
+ rr->valid |= VALID_PN;
+ rr->rdev = makedev (isonum_733 (pn->high), isonum_733 (pn->low));
+
+ goto next_field;
+ }
+
+ /* SL tells, for a symlink, what the target of the link is */
+ if (susp->sig[0] == 'S'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct rr_sl *sl = body;
+ size_t crlen = susp->len - 5;
+ struct rr_sl_comp *comp;
+ void *cp;
+ size_t targalloced, targused;
+
+ void add_comp (char *cname, size_t cnamelen)
+ {
+ if (rr->target == 0)
+ {
+ rr->target = malloc (cnamelen * 2);
+ targused = cnamelen;
+ targalloced = cnamelen * 2;
+ }
+ else if (targused + cnamelen > targalloced)
+ rr->target = realloc (rr->target, targalloced *= 2);
+ assert (rr->target);
+
+ bcopy (cname, rr->target + targused, cnamelen);
+ targused += cnamelen;
+ }
+
+ if (nomoresl)
+ goto next_field;
+
+ /* Append the component use fields to the records we are saving
+ up */
+
+ if (!slbuf)
+ slbuf = malloc (slbufsize = crlen);
+ else
+ slbuf = realloc (slbuf, slbufsize += crlen);
+ assert (slbuf);
+
+ bcopy (sl->data, slbuf + slbufsize - crlen, crlen);
+
+ if (sl->flags & 1)
+ /* We'll finish later. */
+ goto next_field;
+
+ /* Do the symlink translation */
+ for (cp = slbuf; cp < slbuf + slbufsize; cp += comp->len)
+ {
+ comp = (struct rr_sl_comp *)cp;
+ nomoresl = 1;
+
+ /* Put in a slash after each component as we go,
+ unless it's a "continuation" component. */
+
+ if (comp->flags & NAME_DOT)
+ add_comp ("./", 2);
+ else if (comp->flags & NAME_DOTDOT)
+ add_comp ("../", 3);
+ else if (comp->flags & NAME_ROOT)
+ {
+ targused = 0;
+ add_comp ("/", 1);
+ }
+ else if (comp->flags & NAME_VOLROOT)
+ {
+ targused = 0;
+ add_comp (mounted_on, strlen (mounted_on));
+ }
+ else if (comp->flags & NAME_HOST)
+ {
+ add_comp (host_name, strlen (host_name));
+ add_comp ("/", 1);
+ }
+ else
+ {
+ add_comp (comp->name, comp->len);
+ if (!(comp->flags & NAME_CONTINUE))
+ add_comp ("/", 1);
+ }
+ }
+
+ /* And turn the final character, if it's a slash, into a null.
+ Otherwise, add a null. */
+ if (rr->target[targused - 1] == '/')
+ rr->target[targused - 1] = '\0';
+ else
+ add_comp ("", 1);
+
+ free (slbuf);
+ goto next_field;
+ }
+
+ /* TF gives atime, mtime, ctime (and others we don't care about);
+ this overrides the time specified in the directory. */
+ if (susp->sig[0] == 'T'
+ && susp->sig[1] == 'F'
+ && susp->version == 1)
+ {
+ char *(*convert)(char *, struct timespec *);
+ struct rr_tf *tf = body;
+ char *c;
+
+ if (tf->flags & TF_LONG_FORM)
+ convert = isodate_84261;
+ else
+ convert = isodate_915;
+
+ rr->valid |= VALID_TF;
+ rr->tfflags = tf->flags;
+ c = tf->data;
+
+ if (rr->tfflags & TF_CREATION)
+ c = (*convert) (c, &rr->ctime);
+ if (rr->tfflags & TF_MODIFY)
+ c = (*convert) (c, &rr->mtime);
+ if (rr->tfflags & TF_ACCESS)
+ c = (*convert) (c, &rr->atime);
+
+ goto next_field;
+ }
+
+ /* CL means that this entry is a relocated directory. We ignore
+ the attributes in this directory entry (except for NM); they
+ are fetched from the "." entry of the directory itself. The
+ CL field identifies the location of the directory, overriding
+ the location given in the present directory. This directory
+ is listed somewhere else too (to keep the format ISO 9660 compliant),
+ but there's an RE entry on that one so that we ignore it. */
+ if (susp->sig[0] == 'C'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct rr_cl *cl = body;
+
+ rr->realdirent
+ = disk_image + (isonum_733 (cl->loc) * logical_block_size);
+ rr->valid |= VALID_CL;
+
+ if (rr->valid & VALID_NM)
+ {
+ /* We've gotten all we care about from this node.
+ Remember the NM name, and load all the contents
+ from the new location. */
+ char *savename;
+ struct dirrect *realdir;
+
+ clrecurse:
+ /* It might look like VALID_NM is alway set here, but if
+ we got here from the exit point of the function, then
+ VALID_NM is actually clear. */
+
+ /* Save these, becuase rrip_work will clear them. */
+ savename = (rr->valid & VALID_NM) ? rr->name : 0;
+ realdir = rr->realdirent;
+
+ rrip_work (realdir, rr, 0, 0, 0, 1);
+
+ rr->valid |= VALID_CL;
+ rr->realdirent = realdir;
+ if (savename)
+ {
+ rr->valid |= VALID_NM;
+ rr->name = savename;
+ }
+
+ /* If there's an NM field, then we must have matched
+ if we got here. */
+ return (rr->valid & VALID_NM) ? 1 : 0;
+ }
+
+ /* We must keep looking for an NM record. When we find one,
+ the NM code will goto the above piece of code. */
+ goto next_field;
+ }
+
+ /* PL is found in the ".." entry of a relocated directory.
+ The present directory entry points to the fictitious parent
+ (the one that holds the fictitious RE link here); the PL
+ field identifies the real parent (the one that has the CL
+ entry). */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct rr_pl *pl = body;
+
+ rr->realfilestart = (isonum_733 (pl->loc)
+ * (logical_block_size
+ >> store->log2_block_size));
+ rr->valid |= VALID_PL;
+ goto next_field;
+ }
+
+ next_field:
+ bp = bp + susp->len;
+ }
+
+ if (rr->valid & VALID_CL)
+ goto clrecurse;
+
+ /* If we saw an NM field, then it matched; otherwise we
+ didn't see one. */
+ return rr->valid & VALID_NM ? 1 : 0;
+}
+
+/* Parse extensions for directory entry DR. If we encounter an NM
+ record, and it does not match NAME (length NAMELEN), then stop
+ immediately (but do note the NM file in RR->valid) and return zero.
+ If we encounter no NM record at all, then process all the fields
+ normally and return zero. If we encounter an NM field which matches
+ the provided name, then process all the fields and return 1. In any
+ case, fill RR with information corresponding to the fields we do
+ encounter. */
+int
+rrip_match_lookup (struct dirrect *dr, char *name, size_t namelen,
+ struct rrip_lookup *rr)
+{
+ return rrip_work (dr, rr, name, namelen, 0, 0);
+}
+
+/* Parse extensions for dirrect DR and store the results in RR.
+ If IGNORENM, then do not bother with NM records. */
+void
+rrip_lookup (struct dirrect *dr, struct rrip_lookup *rr, int ignorenm)
+{
+ rrip_work (dr, rr, 0, 0, 0, ignorenm);
+}
+
+/* Scan extensions on dirrect DR looking for the tags that are supposed
+ to be on the root directory. */
+void
+rrip_initialize (struct dirrect *dr)
+{
+ struct rrip_lookup rr;
+ rrip_work (dr, &rr, 0, 0, 1, 1);
+ release_rrip (&rr);
+}
diff --git a/isofs/rr.h b/isofs/rr.h
new file mode 100644
index 00000000..a3995be5
--- /dev/null
+++ b/isofs/rr.h
@@ -0,0 +1,213 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#include "iso9660.h"
+
+/* The results of an rrip_scan_lookup call are one of these */
+struct rrip_lookup
+{
+ /* PX */
+ mode_t mode;
+ nlink_t nlink;
+ uid_t uid;
+ gid_t gid;
+
+ /* PN */
+ dev_t rdev;
+
+ /* SL */
+ char *target;
+
+ /* NM */
+ char *name; /* name of this entry if changed (malloced) */
+
+ /* CL */
+ off_t newloc; /* relocated directory */
+
+ /* PL */
+ off_t parloc; /* parent of relocated directory */
+
+ /* TF */
+ int tfflags;
+ struct timespec atime, mtime, ctime; /* file times */
+
+ /* CL */
+ struct dirrect *realdirent; /* actual directory entry for attributes */
+
+ /* RL */
+ off_t realfilestart; /* override file start in dir entry */
+
+ int valid;
+};
+
+/* VALID in one of these is from the following bits */
+#define VALID_PX 0x0001
+#define VALID_PN 0x0002
+#define VALID_SL 0x0004
+#define VALID_NM 0x0008
+#define VALID_CL 0x0010
+#define VALID_PL 0x0020
+#define VALID_TF 0x0040
+#define VALID_RE 0x0080
+
+
+/* Definitions for System Use Sharing Protocol.
+ Version 1. Revision 1.10. Dated July 16, 1993. */
+
+/* A system use field begins with the following header */
+struct su_header
+{
+ char sig[2];
+ unsigned char len;
+ char version;
+};
+
+/* The body of a CE (Continuation Area) field */
+struct su_ce
+{
+ char continuation[8];
+ char offset[8];
+ char size[8];
+};
+
+/* The body of a SP (Sharing Protocol Indicator) field */
+struct su_sp
+{
+ unsigned char check[2];
+ u_char skip;
+};
+
+#define SU_SP_CHECK_0 0xbe
+#define SU_SP_CHECK_1 0xef
+
+/* The body of a ER (Extension Reference) field */
+struct su_er
+{
+ u_char len_id;
+ u_char len_des;
+ u_char len_src;
+ u_char ext_ver;
+ char more[0];
+};
+
+
+
+
+/* Definitions for Rock Ridge extensions.
+ Version 1. Revision 1.10. Dated July 13, 1993. */
+
+/* These are the ER values to indicate the presence of Rock-Ridge
+ extensions. */
+#define ROCK_VERS 1
+#define ROCK_ID "RRIP_1991A"
+#define ROCK_DES \
+ "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS"
+#define ROCK_SRC \
+ "ROCK RIDGE SPECIFICATION VERSION 1 REVISION 1.10 JULY 13 1993"
+
+/* The body of a PX (Posix Attributes) field. */
+struct rr_px
+{
+ char mode[8];
+ char nlink[8];
+ char uid[8];
+ char gid[8];
+};
+
+/* The body of a PN (Posix Device Node) field. */
+struct rr_pn
+{
+ char high[8];
+ char low[8];
+};
+
+/* The body of a SL (Symbolic Link) field. */
+struct rr_sl
+{
+ u_char flags;
+ char data[0];
+};
+
+/* Each component in the DATA is: */
+struct rr_sl_comp
+{
+ u_char flags;
+ u_char len;
+ char name[0];
+};
+
+/* The body of a NM (Alternate Name) field. */
+struct rr_nm
+{
+ u_char flags;
+ char name[0];
+};
+
+/* Flags for SL and NM components */
+#define NAME_CONTINUE 0x01
+#define NAME_DOT 0x02
+#define NAME_DOTDOT 0x04
+#define NAME_ROOT 0x08
+#define NAME_VOLROOT 0x10
+#define NAME_HOST 0x20
+
+/* The body of a CL (Child Directory Location) field. */
+struct rr_cl
+{
+ char loc[8];
+};
+
+/* The body of a PL (Parent Directory Location) field. */
+struct rr_pl
+{
+ char loc[8];
+};
+
+/* The body of a TF (Time Stamp) field. */
+struct rr_tf
+{
+ u_char flags;
+ char data[0];
+};
+
+/* Flags for a TF */
+#define TF_CREATION 0x01
+#define TF_MODIFY 0x02
+#define TF_ACCESS 0x04
+#define TF_ATTRIBUTES 0x08
+#define TF_BACKUP 0x10
+#define TF_EXPIRATION 0x20
+#define TF_EFFECTIVE 0x40
+#define TF_LONG_FORM 0x80
+
+
+/* The body of a SF (Sparse File) field. */
+struct rr_sf
+{
+ char size[8];
+};
+
+
+/* Rock-Ridge related functions. */
+
+int rrip_match_lookup (struct dirrect *, char *, size_t, struct rrip_lookup *);
+void rrip_lookup (struct dirrect *, struct rrip_lookup *, int);
+void rrip_initialize (struct dirrect *);
+void release_rrip (struct rrip_lookup *);