patch-2.2.18 linux/fs/adfs/dir.c
Next file: linux/fs/adfs/dir_f.c
Previous file: linux/fs/adfs/adfs.h
Back to the patch index
Back to the overall index
- Lines: 573
- Date:
Fri Sep 15 23:24:41 2000
- Orig file:
v2.2.17/fs/adfs/dir.c
- Orig date:
Fri Apr 21 12:46:40 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/fs/adfs/dir.c linux/fs/adfs/dir.c
@@ -1,347 +1,297 @@
/*
- * linux/fs/adfs/dir.c
+ * linux/fs/adfs/dir.c
*
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1999-2000 Russell King
+ *
+ * Common directory handling for ADFS
*/
-
+#include <linux/version.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/adfs_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
-static ssize_t adfs_dirread (struct file *filp, char *buf,
- size_t siz, loff_t *ppos)
-{
- return -EISDIR;
-}
+#include <asm/spinlock.h>
-static int adfs_readdir (struct file *, void *, filldir_t);
-
-static struct file_operations adfs_dir_operations = {
- NULL, /* lseek - default */
- adfs_dirread, /* read */
- NULL, /* write - bad */
- adfs_readdir, /* readdir */
- NULL, /* select - default */
- NULL, /* ioctl */
- NULL, /* mmap */
- NULL, /* no special open code */
- NULL, /* flush */
- NULL, /* no special release code */
- file_fsync, /* fsync */
- NULL, /* fasync */
- NULL, /* check_media_change */
- NULL /* revalidate */
-};
+#include "adfs.h"
/*
- * directories can handle most operations...
+ * For future. This should probably be per-directory.
*/
-struct inode_operations adfs_dir_inode_operations = {
- &adfs_dir_operations, /* default directory file-ops */
- NULL, /* create */
- adfs_lookup, /* lookup */
- NULL, /* link */
- NULL, /* unlink */
- NULL, /* symlink */
- NULL, /* mkdir */
- NULL, /* rmdir */
- NULL, /* mknod */
- NULL, /* rename */
- NULL, /* read link */
- NULL, /* follow link */
- NULL, /* read page */
- NULL, /* write page */
- NULL, /* bmap */
- NULL, /* truncate */
- NULL, /* permission */
- NULL /* smap */
-};
+static rwlock_t adfs_dir_lock;
-unsigned int adfs_val (unsigned char *p, int len)
+static int
+adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- unsigned int val = 0;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+ struct object_info obj;
+ struct adfs_dir dir;
+ int ret = 0;
+
+ if (filp->f_pos >> 32)
+ goto out;
+
+ ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+ if (ret)
+ goto out;
+
+ switch ((unsigned long)filp->f_pos) {
+ case 0:
+ if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
+ goto free_out;
+ filp->f_pos += 1;
+
+ case 1:
+ if (filldir(dirent, "..", 2, 1, dir.parent_id) < 0)
+ goto free_out;
+ filp->f_pos += 1;
- switch (len) {
- case 4:
- val |= p[3] << 24;
- case 3:
- val |= p[2] << 16;
- case 2:
- val |= p[1] << 8;
default:
- val |= p[0];
+ break;
}
- return val;
-}
-
-static unsigned int adfs_filetype (unsigned int load)
-{
- if ((load & 0xfff00000) != 0xfff00000)
- return (unsigned int) -1;
- return (load >> 8) & 0xfff;
-}
-static unsigned int adfs_time (unsigned int load, unsigned int exec)
-{
- unsigned int high, low;
+ read_lock(&adfs_dir_lock);
- /* Check for unstamped files. */
- if ((load & 0xfff00000) != 0xfff00000)
- return 0;
-
- high = ((load << 24) | (exec >> 8));
- low = exec & 255;
-
- /* Files dated pre 1970. */
- if (high < 0x336e996a)
- return 0;
+ ret = ops->setpos(&dir, filp->f_pos - 2);
+ if (ret)
+ goto unlock_out;
+ while (ops->getnext(&dir, &obj) == 0) {
+ if (filldir(dirent, obj.name, obj.name_len,
+ filp->f_pos, obj.file_id) < 0)
+ goto unlock_out;
+ filp->f_pos += 1;
+ }
- high -= 0x336e996a;
+unlock_out:
+ read_unlock(&adfs_dir_lock);
- /* Files dated post 2038 ish. */
- if (high > 0x31ffffff)
- return 0x7fffffff;
-
- /* 65537 = h256,l1
- * (h256 % 100) = 56 h256 / 100 = 2
- * 56 << 8 = 14336 2 * 256 = 512
- * + l1 = 14337
- * / 100 = 143
- * + 512 = 655
- */
- return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
-}
+free_out:
+ ops->free(&dir);
-int adfs_readname (char *buf, char *ptr, int maxlen)
-{
- int size = 0;
- while (*ptr >= ' ' && maxlen--) {
- switch (*ptr) {
- case '/':
- *buf++ = '.';
- break;
- default:
- *buf++ = *ptr;
- break;
- }
- ptr++;
- size ++;
- }
- *buf = '\0';
- return size;
+out:
+ return ret;
}
-int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
+int
+adfs_dir_update(struct super_block *sb, struct object_info *obj)
{
- struct super_block *sb;
- int i, size;
+ int ret = -EINVAL;
+#if 0
+ struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+ struct adfs_dir dir;
- sb = inode->i_sb;
+ printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
+ obj->file_id, obj->parent_id);
- size = 2048 >> sb->s_blocksize_bits;
-
- for (i = 0; i < size; i++) {
- int block;
-
- block = adfs_parent_bmap (inode, i);
- if (block)
- bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
- else
- adfs_error (sb, "adfs_dir_read_parent",
- "directory %lu with a hole at offset %d", inode->i_ino, i);
- if (!block || !bhp[i]) {
- int j;
- for (j = i - 1; j >= 0; j--)
- brelse (bhp[j]);
- return 0;
- }
+ if (!ops->update) {
+ ret = -EINVAL;
+ goto out;
}
- return i;
+
+ ret = ops->read(sb, obj->parent_id, 0, &dir);
+ if (ret)
+ goto out;
+
+ write_lock(&adfs_dir_lock);
+ ret = ops->update(&dir, obj);
+ write_unlock(&adfs_dir_lock);
+
+ ops->free(&dir);
+out:
+#endif
+ return ret;
}
-int adfs_dir_read (struct inode *inode, struct buffer_head **bhp)
+static int
+adfs_match(struct qstr *name, struct object_info *obj)
{
- struct super_block *sb;
- int i, size;
+ int i;
- if (!inode || !S_ISDIR(inode->i_mode))
+ if (name->len != obj->name_len)
return 0;
- sb = inode->i_sb;
+ for (i = 0; i < name->len; i++) {
+ char c1, c2;
- size = inode->i_size >> sb->s_blocksize_bits;
+ c1 = name->name[i];
+ c2 = obj->name[i];
- for (i = 0; i < size; i++) {
- int block;
+ if (c1 >= 'A' && c1 <= 'Z')
+ c1 += 'a' - 'A';
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 += 'a' - 'A';
- block = adfs_bmap (inode, i);
- if (block)
- bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
- else
- adfs_error (sb, "adfs_dir_read",
- "directory %lX,%lX with a hole at offset %d",
- inode->i_ino, inode->u.adfs_i.file_id, i);
- if (!block || !bhp[i]) {
- int j;
- for (j = i - 1; j >= 0; j--)
- brelse (bhp[j]);
+ if (c1 != c2)
return 0;
- }
}
- return i;
+ return 1;
}
-int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp)
+static int
+adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
{
- struct adfs_dirheader dh;
- union adfs_dirtail dt;
+ struct super_block *sb = inode->i_sb;
+ struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+ struct adfs_dir dir;
+ int ret;
+
+ ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+ if (ret)
+ goto out;
+
+ if (inode->u.adfs_i.parent_id != dir.parent_id) {
+ adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
+ inode->u.adfs_i.parent_id, dir.parent_id);
+ ret = -EIO;
+ goto free_out;
+ }
- memcpy (&dh, bhp[0]->b_data, sizeof (dh));
- memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt));
+ obj->parent_id = inode->i_ino;
- if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) ||
- (memcmp (&dh.startname, "Nick", 4) &&
- memcmp (&dh.startname, "Hugo", 4))) {
- adfs_error (inode->i_sb, "adfs_check_dir",
- "corrupted directory inode %lX,%lX",
- inode->i_ino, inode->u.adfs_i.file_id);
- return 1;
+ /*
+ * '.' is handled by reserved_lookup() in fs/namei.c
+ */
+ if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
+ /*
+ * Currently unable to fill in the rest of 'obj',
+ * but this is better than nothing. We need to
+ * ascend one level to find it's parent.
+ */
+ obj->name_len = 0;
+ obj->file_id = obj->parent_id;
+ goto free_out;
}
- if (dtp)
- *dtp = dt;
- return 0;
-}
-void adfs_dir_free (struct buffer_head **bhp, int buffers)
-{
- int i;
+ read_lock(&adfs_dir_lock);
+
+ ret = ops->setpos(&dir, 0);
+ if (ret)
+ goto unlock_out;
+
+ ret = -ENOENT;
+ while (ops->getnext(&dir, obj) == 0) {
+ if (adfs_match(name, obj)) {
+ ret = 0;
+ break;
+ }
+ }
+
+unlock_out:
+ read_unlock(&adfs_dir_lock);
- for (i = buffers - 1; i >= 0; i--)
- brelse (bhp[i]);
+free_out:
+ ops->free(&dir);
+out:
+ return ret;
}
-/* convert a disk-based directory entry to a Linux ADFS directory entry */
-static inline void
-adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de)
+static ssize_t
+adfs_dir_no_read(struct file *filp, char *buf, size_t siz, loff_t *ppos)
{
- ide->name_len = adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN);
- ide->file_id = adfs_val(de->dirinddiscadd, 3);
- ide->size = adfs_val(de->dirlen, 4);
- ide->mode = de->newdiratts;
- ide->mtime = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4));
- ide->filetype = adfs_filetype(adfs_val(de->dirload, 4));
+ return -EISDIR;
}
-int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
- int buffers, int pos, unsigned long parent_object_id,
- struct adfs_idir_entry *ide)
-{
- struct adfs_direntry de;
- int thissize, buffer, offset;
+struct file_operations adfs_dir_operations = {
+ read: adfs_dir_no_read,
+ readdir: adfs_readdir,
+ fsync: file_fsync,
+};
- offset = pos & (sb->s_blocksize - 1);
- buffer = pos >> sb->s_blocksize_bits;
+static int
+adfs_hash(struct dentry *parent, struct qstr *qstr)
+{
+ const unsigned int name_len = parent->d_sb->u.adfs_sb.s_namelen;
+ const unsigned char *name;
+ unsigned long hash;
+ int i;
- if (buffer > buffers)
+ if (qstr->len < name_len)
return 0;
- thissize = sb->s_blocksize - offset;
- if (thissize > 26)
- thissize = 26;
-
- memcpy (&de, bhp[buffer]->b_data + offset, thissize);
- if (thissize != 26)
- memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
+ /*
+ * Truncate the name in place, avoids
+ * having to define a compare function.
+ */
+ qstr->len = i = name_len;
+ name = qstr->name;
+ hash = init_name_hash();
+ while (i--) {
+ char c;
+
+ c = *name++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
- if (!de.dirobname[0])
- return 0;
+ hash = partial_name_hash(c, hash);
+ }
+ qstr->hash = end_name_hash(hash);
- ide->inode_no = adfs_inode_generate (parent_object_id, pos);
- adfs_dirent_to_idirent(ide, &de);
- return 1;
+ return 0;
}
-int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
- int buffers, unsigned int pos,
- struct adfs_idir_entry *ide)
+/*
+ * Compare two names, taking note of the name length
+ * requirements of the underlying filesystem.
+ */
+static int
+adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
{
- struct adfs_direntry de;
- int offset, buffer, thissize;
-
- offset = pos & (sb->s_blocksize - 1);
- buffer = pos >> sb->s_blocksize_bits;
-
- if (buffer > buffers)
- return 0;
-
- thissize = sb->s_blocksize - offset;
- if (thissize > 26)
- thissize = 26;
-
- memcpy (&de, bhp[buffer]->b_data + offset, thissize);
- if (thissize != 26)
- memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
-
- if (!de.dirobname[0])
- return 0;
+ int i;
- adfs_dirent_to_idirent(ide, &de);
- return 1;
-}
+ if (entry->len != name->len)
+ return 1;
-static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir)
-{
- struct inode *inode = filp->f_dentry->d_inode;
- struct super_block *sb;
- struct buffer_head *bh[4];
- union adfs_dirtail dt;
- unsigned long parent_object_id, dir_object_id;
- int buffers, pos;
-
- sb = inode->i_sb;
-
- if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
- return -ENOENT;
-
- if (!(buffers = adfs_dir_read (inode, bh))) {
- adfs_error (sb, "adfs_readdir", "unable to read directory");
- return -EINVAL;
- }
+ for (i = 0; i < name->len; i++) {
+ char a, b;
- if (adfs_dir_check (inode, bh, buffers, &dt)) {
- adfs_dir_free (bh, buffers);
- return -ENOENT;
- }
+ a = entry->name[i];
+ b = name->name[i];
- parent_object_id = adfs_val (dt.new.dirparent, 3);
- dir_object_id = adfs_inode_objid (inode);
+ if (a >= 'A' && a <= 'Z')
+ a += 'a' - 'A';
+ if (b >= 'A' && b <= 'Z')
+ b += 'a' - 'A';
- if (filp->f_pos < 2) {
- if (filp->f_pos < 1) {
- if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0)
- return 0;
- filp->f_pos ++;
- }
- if (filldir (dirent, "..", 2, 1,
- adfs_inode_generate (parent_object_id, 0)) < 0)
- return 0;
- filp->f_pos ++;
+ if (a != b)
+ return 1;
}
+ return 0;
+}
- pos = 5 + (filp->f_pos - 2) * 26;
- while (filp->f_pos < 79) {
- struct adfs_idir_entry ide;
-
- if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide))
- break;
+struct dentry_operations adfs_dentry_operations = {
+ d_hash: adfs_hash,
+ d_compare: adfs_compare,
+};
- if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0)
- return 0;
- filp->f_pos ++;
- pos += 26;
+struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct object_info obj;
+ int error;
+
+ dentry->d_op = &adfs_dentry_operations;
+ error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
+ if (error == 0) {
+ error = -EACCES;
+ /*
+ * This only returns NULL if get_empty_inode
+ * fails.
+ */
+ inode = adfs_iget(dir->i_sb, &obj);
+ if (inode)
+ error = 0;
}
- adfs_dir_free (bh, buffers);
- return 0;
+ d_add(dentry, inode);
+ return ERR_PTR(error);
}
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations adfs_dir_inode_operations = {
+ default_file_ops: &adfs_dir_operations,
+ lookup: adfs_lookup,
+};
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)