linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Bob Copeland <me@bobcopeland.com>
To: linux-fsdevel@vger.kernel.org
Cc: Bob Copeland <me@bobcopeland.com>
Subject: [RFC][PATCH 3/7] omfs: directory routines
Date: Wed, 15 Mar 2006 22:01:44 -0500	[thread overview]
Message-ID: <11424781041688-git-send-email-me@bobcopeland.com> (raw)
In-Reply-To: <1142478104752-git-send-email-me@bobcopeland.com>

This patch adds directory-related functions to OMFS.  The filesystem
represents each directory on-disk as a hash table for lookups and 
stores the inode numbers of files and directories in the hash buckets.
---

 fs/omfs/dir.c |  310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 310 insertions(+), 0 deletions(-)
 create mode 100644 fs/omfs/dir.c

d6f496050db8c31ca150894ca9cc36ca2c3d09f2
diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c
new file mode 100644
index 0000000..e244d43
--- /dev/null
+++ b/fs/omfs/dir.c
@@ -0,0 +1,310 @@
+/*
+ * fs/omfs/dir.c
+ * OMFS (as used by RIO Karma) directory operations.
+ * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com>
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+#include <linux/buffer_head.h>
+#include "omfs.h"
+
+static int omfs_hash(const char *name, int namelen, int mod)
+{
+	int i, hash=0;
+	for (i=0; i<namelen; i++)
+		hash ^= tolower(name[i]) << (i % 24);
+	return hash % mod;
+}
+
+/* 
+ * Finds the bucket for a given name and reads the containing block; 
+ * *ofs is set to the offset of the first list entry.
+ */
+static struct buffer_head *omfs_get_bucket(struct inode *dir, 
+		const char *name, int namelen, int *ofs)
+{
+	int nbuckets = (dir->i_size - OMFS_DIR_START)/8;
+	int block = clus_to_blk(OMFS_SB(dir->i_sb), dir->i_ino);
+	int bucket = omfs_hash(name, namelen, nbuckets);
+
+	*ofs = OMFS_DIR_START + bucket * 8;
+	return sb_bread(dir->i_sb, block);
+}
+
+static struct buffer_head *omfs_find_entry(struct inode *dir,
+					   const char *name, int namelen)
+{
+	struct buffer_head *bh;
+	struct omfs_inode *oi;
+	int ofs;
+	u64 cluster;
+	bh = omfs_get_bucket(dir, name, namelen, &ofs);
+	if (!bh)
+		goto out;
+
+	cluster = be64_to_cpu(*((u64 *) & bh->b_data[ofs]));
+	brelse(bh);
+	while (cluster != ~0) {
+		bh = sb_bread(dir->i_sb, clus_to_blk(OMFS_SB(dir->i_sb), 
+			cluster));
+		if (!bh)
+			goto out;
+
+		oi = (struct omfs_inode *)bh->b_data;
+		if (strncmp(oi->i_name, name, namelen) == 0) {
+			return bh;
+		}
+		cluster = be64_to_cpu(oi->i_sibling);
+		brelse(bh);
+	}
+out:
+	return NULL;
+}
+
+#ifdef OMFS_WRITE
+int omfs_make_empty(struct inode *inode, struct super_block *sb)
+{
+	struct omfs_sb_info *sbi = OMFS_SB(sb);
+	int block = clus_to_blk(sbi, inode->i_ino);
+	struct buffer_head *bh;
+
+	bh = sb_bread(sb, block);
+	if (!bh)
+		return -ENOMEM;
+
+	if (inode->i_mode & S_IFDIR)
+	{
+		memset(bh->b_data, 0, OMFS_DIR_START);
+		memset(&bh->b_data[OMFS_DIR_START], 0xff, 
+			sbi->s_sys_blocksize - OMFS_DIR_START);
+	}
+	else
+		omfs_make_empty_table(bh, OMFS_EXTENT_START);
+	
+	mark_buffer_dirty(bh);
+	brelse(bh);
+	return 0;
+}
+
+int omfs_add_link(struct dentry *dentry, struct inode *inode)
+{
+	struct inode *dir = dentry->d_parent->d_inode;
+	const char *name = dentry->d_name.name;
+	int namelen = dentry->d_name.len;
+	struct omfs_inode *oi;
+	struct buffer_head *bh;
+	u64 cluster;
+	u64 *entry;
+	int ofs;
+
+	// just prepend to head of queue in proper bucket and we are done. 
+	bh = omfs_get_bucket(dir, name, namelen, &ofs);
+	if (!bh)
+		goto out;
+
+	entry = (u64 *) &bh->b_data[ofs];
+	cluster = be64_to_cpu(*entry);
+	*entry = cpu_to_be64(inode->i_ino);
+	mark_buffer_dirty(bh);
+	brelse(bh); 
+
+	// now set the sibling and parent pointers on the new inode
+	bh = sb_bread(dir->i_sb, clus_to_blk(OMFS_SB(dir->i_sb), inode->i_ino));
+	if (!bh)
+		goto out;
+
+	oi = (struct omfs_inode *) bh->b_data;
+	memcpy (oi->i_name, name, namelen);
+	memset (oi->i_name + namelen, 0, OMFS_NAMELEN - namelen);
+	oi->i_sibling = cpu_to_be64(cluster);
+	oi->i_parent = cpu_to_be64(dir->i_ino);
+	mark_buffer_dirty(bh);
+	brelse(bh);
+
+	dir->i_ctime = CURRENT_TIME_SEC;
+
+	// mark affected inodes dirty to rebuild checksums
+	mark_inode_dirty(dir);
+	mark_inode_dirty(inode);
+	return 0;
+out:
+	return -ENOMEM;
+}
+
+static int omfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int err = 0;
+	struct inode *inode;
+
+	mode |= S_IFDIR;
+
+	inode = omfs_new_inode(dir, mode);
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	if (dir->i_mode & S_ISGID) {
+		inode->i_gid = dir->i_gid;
+		if (S_ISDIR(mode))
+			inode->i_mode |= S_ISGID;
+	}
+
+	err = omfs_make_empty(inode, dir->i_sb);
+	if (err)
+		goto out;
+
+	err = omfs_add_link(dentry, inode); 
+	if (err)
+		goto out;
+
+	d_instantiate(dentry, inode);
+out:
+	return err;
+}
+
+static int omfs_create(struct inode * dir, struct dentry * dentry, int mode,
+		struct nameidata *nd)
+{
+	int err;
+	struct inode *inode;
+
+	mode |= S_IFREG;
+
+	inode = omfs_new_inode(dir, mode);
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	err = omfs_make_empty(inode, dir->i_sb);
+	if (err)
+		goto out;
+
+	err = omfs_add_link(dentry, inode); 
+	if (err)
+		goto out;
+	
+	d_instantiate(dentry, inode);
+out:
+	return err;
+}
+#endif
+
+static struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry,
+				  struct nameidata *nd)
+{
+	struct buffer_head *bh;
+	struct inode *inode = NULL;
+
+	if (dentry->d_name.len > OMFS_NAMELEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len);
+	if (bh) {
+		struct omfs_inode *oi = (struct omfs_inode *)bh->b_data;
+		ino_t ino = be64_to_cpu(oi->i_head.h_self);
+		brelse(bh);
+		inode = iget(dir->i_sb, ino);
+		if (!inode) {
+			return ERR_PTR(-EINVAL);	
+		}
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static int omfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *dir = filp->f_dentry->d_inode;
+	struct buffer_head *bh = NULL, *bh2;
+	struct omfs_inode *oi;
+	loff_t offset, res;
+	unsigned char d_type;
+	ino_t self;
+	unsigned int hchain, hindex;
+	u64 fsblock;
+	int nbuckets;
+
+	switch (filp->f_pos) {
+	case 0:
+		if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) 
+			goto success;
+		filp->f_pos++;
+		/* fall through */
+	case 1:
+		if (filldir(dirent, "..", 2, 1, parent_ino(filp->f_dentry), DT_DIR) < 0) 
+			goto success;
+		filp->f_pos = 0x01000000;
+		/* fall through */
+	}
+
+	nbuckets = (dir->i_size - OMFS_DIR_START) / 8;
+
+	/* high byte stores bucket + 1 and low 24 bits store hash index */
+	hchain = (filp->f_pos >> 24) -1;
+	hindex = filp->f_pos & 0xffffff;
+
+	bh = sb_bread(dir->i_sb, clus_to_blk(OMFS_SB(dir->i_sb), dir->i_ino));
+	if (!bh)
+		goto no_bh;
+
+	offset = OMFS_DIR_START + hchain * 8;
+
+	for (; hchain < nbuckets; hchain++, offset += 8) {
+		fsblock = be64_to_cpu(*((u64 *) & bh->b_data[offset]));
+
+		// follow chain in this bucket
+		while (fsblock != ~0) {
+			bh2 = sb_bread(dir->i_sb, 
+				clus_to_blk(OMFS_SB(dir->i_sb), fsblock));
+			if (!bh2)
+				goto no_bh2;
+
+			oi = (struct omfs_inode *)bh2->b_data;
+			self = fsblock;
+			fsblock = be64_to_cpu(oi->i_sibling);
+
+			// skip visited nodes
+			if (hindex) { 
+				hindex--;
+				brelse(bh2);
+				continue;
+			}
+
+			if (oi->i_type == OMFS_DIR)
+				d_type = DT_DIR;
+			else
+				d_type = DT_REG;
+
+			res = filldir(dirent, oi->i_name, strnlen(oi->i_name, 
+				OMFS_NAMELEN), filp->f_pos, self, d_type);
+			brelse(bh2);
+			if (res < 0) 
+				goto success;
+			
+			filp->f_pos++;
+		}
+		filp->f_pos = (hchain+2) << 24;
+	}
+success:
+	if (bh)
+		brelse(bh);
+	return 0;
+no_bh2:
+	brelse(bh);
+no_bh:
+	return -EINVAL;
+}
+
+struct inode_operations omfs_dir_inops = {
+	.lookup = omfs_lookup,
+#ifdef OMFS_WRITE
+	.mkdir = omfs_mkdir,
+	.rename = simple_rename,
+	.create = omfs_create,
+#endif
+};
+
+struct file_operations omfs_dir_operations = {
+	.read = generic_read_dir,
+	.readdir = omfs_readdir,
+};
-- 
1.2.1



  reply	other threads:[~2006-03-16  3:01 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-03-16  3:01 [RFC][PATCH 0/7] Optimized MPEG file system Bob Copeland
2006-03-16  3:01 ` [RFC][PATCH 1/7] omfs: filesystem headers Bob Copeland
2006-03-16  3:01   ` [RFC][PATCH 2/7] omfs: inode and superblock routines Bob Copeland
2006-03-16  3:01     ` Bob Copeland [this message]
2006-03-16  3:01       ` [RFC][PATCH 4/7] omfs: file routines Bob Copeland
2006-03-16  3:01         ` [RFC][PATCH 5/7] omfs: bitmap / block allocation routines Bob Copeland
2006-03-16  3:01           ` [RFC][PATCH 6/7] omfs: checksumming routines Bob Copeland
2006-03-16  3:01             ` [RFC][PATCH 7/7] omfs: kbuild updates Bob Copeland
2006-03-16  4:33 ` [RFC][PATCH 0/7] Optimized MPEG file system Brad Boyer
2006-03-16 18:33   ` Bob Copeland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=11424781041688-git-send-email-me@bobcopeland.com \
    --to=me@bobcopeland.com \
    --cc=linux-fsdevel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).