summaryrefslogtreecommitdiff
path: root/isofs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'isofs/inode.c')
-rw-r--r--isofs/inode.c550
1 files changed, 550 insertions, 0 deletions
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;
+}
+