summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext2fs/inode.c552
1 files changed, 552 insertions, 0 deletions
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
new file mode 100644
index 00000000..387738b4
--- /dev/null
+++ b/ext2fs/inode.c
@@ -0,0 +1,552 @@
+/* Inode management routines
+ Copyright (C) 1994, 1995 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. */
+
+#include "ext2fs.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(ino) ((ino)&(INOHSZ-1))
+#else
+#define INOHASH(ino) (((unsigned)(ino))%INOHSZ)
+#endif
+
+static struct node *nodehash[INOHSZ];
+static error_t read_disknode (struct node *np);
+
+spin_lock_t gennumberlock = SPIN_LOCK_INITIALIZER;
+
+/* Initialize the inode hash table. */
+void
+inode_init ()
+{
+ int n;
+ for (n = 0; n < INOHSZ; n++)
+ nodehash[n] = 0;
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+ gain one user reference and lock the node. */
+error_t
+iget (ino_t inum, struct node **npp)
+{
+ struct disknode *dn;
+ struct node *np;
+ error_t err;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->dn->number != inum)
+ continue;
+
+ np->references++;
+ spin_unlock (&diskfs_node_refcnt_lock);
+ mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
+
+ dn = malloc (sizeof (struct disknode));
+
+ dn->number = inum;
+ dn->dirents = 0;
+
+ rwlock_init (&dn->allocptrlock);
+ dn->dirty = 0;
+ dn->fileinfo = 0;
+
+ np = diskfs_make_node (dn);
+ mutex_lock (&np->lock);
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = read_disknode (np);
+
+ if (lblkno (sblock, np->dn_stat.st_size) < NDADDR)
+ np->allocsize = fragroundup (sblock, np->dn_stat.st_size);
+ else
+ np->allocsize = blkroundup (sblock, np->dn_stat.st_size);
+
+ if (!diskfs_readonly && !np->dn_stat.st_gen)
+ {
+ spin_lock (&gennumberlock);
+ if (++nextgennumber < diskfs_mtime->seconds)
+ nextgennumber = diskfs_mtime->seconds;
+ np->dn_stat.st_gen = nextgennumber;
+ spin_unlock (&gennumberlock);
+ np->dn_set_ctime = 1;
+ }
+
+ if (err)
+ return err;
+ else
+ {
+ *npp = np;
+ return 0;
+ }
+}
+
+/* Lookup node INUM (which must have a reference already) and return it
+ without allocating any new references. */
+struct node *
+ifind (ino_t inum)
+{
+ struct node *np;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->dn->number != inum)
+ continue;
+
+ assert (np->references);
+ spin_unlock (&diskfs_node_refcnt_lock);
+ return np;
+ }
+ assert (0);
+}
+
+/* The last reference to a node has gone away; drop
+ it from the hash table and clean all state in the dn structure. */
+void
+diskfs_node_norefs (struct node *np)
+{
+ *np->dn->hprevp = np->dn->hnext;
+ if (np->dn->hnext)
+ np->dn->hnext->dn->hprevp = np->dn->hprevp;
+ if (np->dn->dirents)
+ free (np->dn->dirents);
+ 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);
+}
+
+/* The last hard reference to a node has gone away. */
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+#ifdef notanymore
+ struct port_info *pi;
+ struct pager *p;
+
+ /* Check and see if there is a pager which has only
+ one reference (ours). If so, then drop that reference,
+ breaking the cycle. The complexity in this routine
+ is all due to this cycle. */
+
+ if (np->dn->fileinfo)
+ {
+ spin_lock (&_libports_portrefcntlock);
+ pi = (struct port_info *) np->dn->fileinfo->p;
+ if (pi->refcnt == 1)
+ {
+
+ /* The only way to get a new reference to the pager
+ in this state is to call diskfs_get_filemap; this
+ can't happen as long as we hold NP locked. So
+ we can safely unlock _libports_portrefcntlock for
+ the following call. */
+ spin_unlock (&_libports_portrefcntlock);
+
+ /* Right now the node is locked with no hard refs;
+ this is an anomolous situation. Before messing with
+ the reference count on the file pager, we have to
+ give ourselves a reference back so that we are really
+ allowed to hold the lock. Then we can do the
+ unreference. */
+ p = np->dn->fileinfo->p;
+ np->dn->fileinfo = 0;
+ diskfs_nref (np);
+ pager_unreference (p);
+
+ assert (np->references == 1 && np->light_references == 0);
+
+ /* This will do the real deallocate. Whew. */
+ diskfs_nput (np);
+ }
+ else
+ spin_unlock (&_libports_portrefcntlock);
+ }
+#endif
+}
+
+/* A new hard reference to a node has been created; it's now OK to
+ have unused weak references. */
+void
+diskfs_new_hardrefs (struct node *np)
+{
+ allow_pager_softrefs (np);
+}
+
+/* Read stat information out of the dinode. */
+static error_t
+read_disknode (struct node *np)
+{
+ static int fsid, fsidset;
+ struct stat *st = &np->dn_stat;
+ struct dinode *di = dino (np->dn->number);
+ error_t err;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ np->istranslated = !! di->di_trans;
+
+ if (!fsidset)
+ {
+ fsid = getpid ();
+ fsidset = 1;
+ }
+
+ st->st_fstype = FSTYPE_EXT2FS;
+ st->st_fsid = fsid;
+ st->st_ino = np->dn->number;
+ st->st_gen = di->di_gen;
+ st->st_rdev = di->di_rdev;
+ st->st_mode = di->di_model | (di->di_modeh << 16);
+ st->st_nlink = di->di_nlink;
+ st->st_size = di->di_size;
+#ifdef notyet
+ st->st_atimespec = di->di_atime;
+ st->st_mtimespec = di->di_mtime;
+ st->st_ctimespec = di->di_ctime;
+#else
+ st->st_atime = di->di_atime.ts_sec;
+ st->st_atime_usec = di->di_atime.ts_nsec / 1000;
+ st->st_mtime = di->di_mtime.ts_sec;
+ st->st_mtime_usec = di->di_mtime.ts_nsec / 1000;
+ st->st_ctime = di->di_ctime.ts_sec;
+ st->st_ctime_usec = di->di_ctime.ts_nsec / 1000;
+#endif
+ st->st_blksize = sblock->fs_bsize;
+ st->st_blocks = di->di_blocks;
+ st->st_flags = di->di_flags;
+
+ if (sblock->fs_inodefmt < FS_44INODEFMT)
+ {
+ st->st_uid = di->di_ouid;
+ st->st_gid = di->di_ogid;
+ st->st_author = 0;
+ }
+ else
+ {
+ st->st_uid = di->di_uid;
+ st->st_gid = di->di_gid;
+ st->st_author = di->di_author;
+ if (st->st_author == -1)
+ st->st_author = st->st_uid;
+ }
+
+ diskfs_end_catch_exception ();
+ if (!S_ISBLK (st->st_mode) && !S_ISCHR (st->st_mode))
+ st->st_rdev = 0;
+ return 0;
+}
+
+static void
+write_node (struct node *np)
+{
+ struct stat *st = &np->dn_stat;
+ struct dinode *di = dino (np->dn->number);
+ error_t err;
+
+ assert (!np->dn_set_ctime && !np->dn_set_atime && !np->dn_set_mtime);
+ if (np->dn_stat_dirty)
+ {
+ assert (!diskfs_readonly);
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return;
+
+ di->di_gen = st->st_gen;
+
+ if (S_ISBLK (st->st_mode) || S_ISCHR (st->st_mode))
+ di->di_rdev = st->st_rdev;
+
+ /* We happen to know that the stat mode bits are the same
+ as the ext2fs mode bits. */
+
+ if (compat_mode == COMPAT_GNU)
+ {
+ di->di_model = st->st_mode & 0xffff;
+ di->di_modeh = (st->st_mode >> 16) & 0xffff;
+ }
+ else
+ {
+ di->di_model = st->st_mode & 0xffff;
+ di->di_modeh = 0;
+ }
+
+ if (compat_mode != COMPAT_BSD42)
+ {
+ di->di_uid = st->st_uid;
+ di->di_gid = st->st_gid;
+ }
+
+ if (sblock->fs_inodefmt < FS_44INODEFMT)
+ {
+ di->di_ouid = st->st_uid & 0xffff;
+ di->di_ogid = st->st_gid & 0xffff;
+ }
+ else if (compat_mode == COMPAT_GNU)
+ di->di_author = st->st_author;
+
+ di->di_nlink = st->st_nlink;
+ di->di_size = st->st_size;
+#ifdef notyet
+ di->di_atime = st->st_atimespec;
+ di->di_mtime = st->st_mtimespec;
+ di->di_ctime = st->st_ctimespec;
+#else
+ di->di_atime.ts_sec = st->st_atime;
+ di->di_atime.ts_nsec = st->st_atime_usec * 1000;
+ di->di_mtime.ts_sec = st->st_mtime;
+ di->di_mtime.ts_nsec = st->st_mtime_usec * 1000;
+ di->di_ctime.ts_sec = st->st_ctime;
+ di->di_ctime.ts_nsec = st->st_ctime_usec * 1000;
+#endif
+ di->di_blocks = st->st_blocks;
+ di->di_flags = st->st_flags;
+
+ diskfs_end_catch_exception ();
+ np->dn_stat_dirty = 0;
+ record_poke (di, sizeof (struct dinode));
+ }
+}
+
+/* See if we should create a symlink by writing it directly into
+ the block pointer array. Returning EINVAL tells diskfs to do it
+ the usual way. */
+static error_t
+create_symlink_hook (struct node *np, char *target)
+{
+ int len = strlen (target);
+ error_t err;
+ struct dinode *di;
+
+ if (!direct_symlink_extension)
+ return EINVAL;
+
+ assert (compat_mode != COMPAT_BSD42);
+
+ if (len >= sblock->fs_maxsymlinklen)
+ return EINVAL;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino (np->dn->number);
+ bcopy (target, di->di_shortlink, len);
+ np->dn_stat.st_size = len;
+ np->dn_set_ctime = 1;
+ np->dn_set_mtime = 1;
+ record_poke (di, sizeof (struct dinode));
+
+ diskfs_end_catch_exception ();
+ return 0;
+}
+error_t (*diskfs_create_symlink_hook)(struct node *, char *)
+ = create_symlink_hook;
+
+/* Check if this symlink is stored directly in the block pointer array.
+ Returning EINVAL tells diskfs to do it the usual way. */
+static error_t
+read_symlink_hook (struct node *np,
+ char *buf)
+{
+ error_t err;
+
+ if (!direct_symlink_extension
+ || np->dn_stat.st_size >= sblock->fs_maxsymlinklen)
+ return EINVAL;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ bcopy ((dino (np->dn->number))->di_shortlink, buf, np->dn_stat.st_size);
+ np->dn_set_atime = 1;
+
+ diskfs_end_catch_exception ();
+ return 0;
+}
+error_t (*diskfs_read_symlink_hook)(struct node *, char *)
+ = read_symlink_hook;
+
+
+/* Write all active disknodes into the dinode pager. */
+void
+write_all_disknodes ()
+{
+ int n;
+ struct node *np;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ for (n = 0; n < INOHSZ; n++)
+ for (np = nodehash[n]; np; np = np->dn->hnext)
+ {
+ diskfs_set_node_times (np);
+ write_node (np);
+ }
+ spin_unlock (&diskfs_node_refcnt_lock);
+}
+
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+ write_node (np);
+ sync_dinode (np->dn->number, wait);
+}
+
+/* Implement the diskfs_set_statfs callback from the diskfs library;
+ see <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_set_statfs (struct fsys_statfsbuf *st)
+{
+ st->fsys_stb_type = FSTYPE_EXT2FS;
+ st->fsys_stb_bsize = sblock->fs_bsize;
+ st->fsys_stb_fsize = sblock->fs_fsize;
+ st->fsys_stb_blocks = sblock->fs_dsize;
+ st->fsys_stb_bfree = (sblock->fs_cstotal.cs_nbfree * sblock->fs_frag
+ + sblock->fs_cstotal.cs_nffree);
+ st->fsys_stb_bavail = ((sblock->fs_dsize * (100 - sblock->fs_minfree) / 100)
+ - (sblock->fs_dsize - st->fsys_stb_bfree));
+ st->fsys_stb_files = sblock->fs_ncg * sblock->fs_ipg - 2; /* not 0 or 1 */
+ st->fsys_stb_ffree = sblock->fs_cstotal.cs_nifree;
+ st->fsys_stb_fsid = getpid ();
+ return 0;
+}
+
+/* Implement the diskfs_set_translator callback from the diskfs
+ library; see <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_set_translator (struct node *np, char *name, u_int namelen,
+ struct protid *cred)
+{
+ daddr_t blkno;
+ error_t err;
+ char buf[sblock->fs_bsize];
+ struct dinode *di;
+
+ if (compat_mode != COMPAT_GNU)
+ return EOPNOTSUPP;
+
+ if (namelen + sizeof (u_int) > sblock->fs_bsize)
+ return ENAMETOOLONG;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino (np->dn->number);
+ blkno = di->di_trans;
+
+ if (namelen && !blkno)
+ {
+ /* Allocate block for translator */
+ err = ffs_alloc (np, 0, 0, sblock->fs_bsize, &blkno, cred);
+ if (err)
+ {
+ diskfs_end_catch_exception ();
+ return err;
+ }
+ di->di_trans = blkno;
+ record_poke (di, sizeof (struct dinode));
+ np->dn_set_ctime = 1;
+ }
+ else if (!namelen && blkno)
+ {
+ /* Clear block for translator going away. */
+ ffs_blkfree (np, blkno, sblock->fs_bsize);
+ di->di_trans = 0;
+ record_poke (di, sizeof (struct dinode));
+ np->dn_stat.st_blocks -= btodb (sblock->fs_bsize);
+ np->istranslated = 0;
+ np->dn_set_ctime = 1;
+ }
+
+ if (namelen)
+ {
+ bcopy (&namelen, buf, sizeof (u_int));
+ bcopy (name, buf + sizeof (u_int), namelen);
+
+ bcopy (buf, disk_image + fsaddr (sblock, blkno), sblock->fs_bsize);
+ sync_disk_blocks (blkno, sblock->fs_bsize, 1);
+
+ np->istranslated = 1;
+ np->dn_set_ctime = 1;
+ }
+
+ diskfs_end_catch_exception ();
+ return err;
+}
+
+/* Implement the diskfs_get_translator callback from the diskfs library.
+ See <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_get_translator (struct node *np, char **namep, u_int *namelen)
+{
+ error_t err;
+ daddr_t blkno;
+ u_int datalen;
+ void *transloc;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ blkno = (dino (np->dn->number))->di_trans;
+ assert (blkno);
+ transloc = disk_image + fsaddr (sblock, blkno);
+
+ datalen = *(u_int *)transloc;
+ if (datalen > *namelen)
+ vm_allocate (mach_task_self (), (vm_address_t *) namep, datalen, 1);
+ bcopy (transloc + sizeof (u_int), *namep, datalen);
+
+ diskfs_end_catch_exception ();
+
+ *namelen = datalen;
+ return 0;
+}
+
+/* Called when all hard ports have gone away. */
+void
+diskfs_shutdown_soft_ports ()
+{
+ /* Should initiate termination of internally held pager ports
+ (the only things that should be soft) XXX */
+}
+