diff options
-rw-r--r-- | isofs/ChangeLog | 0 | ||||
-rw-r--r-- | isofs/Makefile | 29 | ||||
-rw-r--r-- | isofs/ext.c | 60 | ||||
-rw-r--r-- | isofs/inode.c | 550 | ||||
-rw-r--r-- | isofs/iso9660.h | 125 | ||||
-rw-r--r-- | isofs/isofs.h | 88 | ||||
-rw-r--r-- | isofs/lookup.c | 448 | ||||
-rw-r--r-- | isofs/main.c | 178 | ||||
-rw-r--r-- | isofs/pager.c | 341 | ||||
-rw-r--r-- | isofs/rr.c | 600 | ||||
-rw-r--r-- | isofs/rr.h | 213 |
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 *); |