Msdos name alias patch for 2.1.49

Alain.Knaff@poboxes.com ("Alain.Knaff@poboxes.com")
Thu, 14 Aug 1997 21:12:04 +0200


On Sat, 9 Aug 1997, Linus Torvalds wrote:
>On Sat, 9 Aug 1997, Ingo Molnar wrote:
>>
>> to _not_ give control over the dentry to the filesystem, but give control
>> over the 'name component' quickstring. The interface between the VFS and
>> the filesystem layer would be purely 'struct qstr', without the filesystem
>> knowing what that thing is actually. This would IMO largely 'neutralize'
>> the impact of filesystem-specific code. It would be a pure 'string
>> equivalency' framework.
>
>This would work. I could live with something like this, but if somebody
>wants to actually implement it I'd suggest ("suggest" means that these
>are my minimum requirements ;):

;-)

> - don't think of the name operations as "inode" operations. They are name
> operations, and thus they conceptually are dentry operations (like
> "d_revalidate"). Create a "struct dentry_operations" and put the new
> functions there, together with d_revalidate().
> - we must not change the actual name (I don't want the name allocation
> problems, and we must not change the name in-place, because the "place"
> may be a disk buffer), so we'd need two separate functions: the
> "d_compare()" function and the "d_hash()" function. Both would get qstr
> arguments.

At the end of this mail is a patch which IMHO fulfils these
conditions.

>Mathematical rules for d_hash and d_compare:
>
> d_compare(a,a) = 1 for all a
> d_compare(a,b) => d_hash(a) == d_hash(b)
> d_compare(a,b) <=> d_compare(b,a)

ok, except that d_compare values are 0 for equivalence, and 1 for
difference in order to match memcmp's behavior. If this is a problem,
I'll gladly supply a patch where the return values of d_compare have
the polarity that you outlined above.

Another difference is that negative values are returned for "bad"
qstr's. But this should not be a problem, as these aren't allowed into
the hash lists in the first place.

>Implementation rules for d_hash/d_compare: they have to be "fast" (no IO).
>That allows us to hold the name lookup spinlock while calling them.

Is ok as well. In msdos' case, both functions do use
msdos_format_name. Although msdos_format_name looks complex, it does
no I/O, it only mangles characters in memory. The resulting name is
stored in an stack variable of d_hash and d_compare, which is only
needed for the duration of both calls. If the msdos_formatt'ed name
will be needed later, it will be recalculated again. Hence, the
buffer in qstr needs not to be touched. Moreover, no kmalloc needs to
be done. IMHO, the cost of msdos_format_name has no big impact as:
* hash needs only to be calculated once per name.
* compare is only called once per name as well in most cases (except
in case of a hash collision)

The d_hash function pointer is taken from the parent directory
instead of the dentry itself, because the dentry itself does not exist
yet at the time that the hash is computed. For symetry reasons, the
d_compare function pointer is also taken from the parent directory.
This convention also allows us to observe the mount flags attached to
our msdos filesystem.

Comments?

Regards,

Alain

diff -ur 2.1.49/linux/fs/autofs/root.c linux/fs/autofs/root.c
--- 2.1.49/linux/fs/autofs/root.c Thu Aug 14 18:45:22 1997
+++ linux/fs/autofs/root.c Thu Aug 14 20:38:21 1997
@@ -176,6 +176,13 @@
return 1;
}

+static struct dentry_operations autofs_dentry_operations = {
+ autofs_revalidate,
+ 0, /* d_hash */
+ 0, /* d_compare */
+};
+
+
static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
{
struct autofs_sb_info *sbi;
@@ -204,7 +211,7 @@
*
* We need to do this before we release the directory semaphore.
*/
- dentry->d_revalidate = autofs_revalidate;
+ dentry->d_op = &autofs_dentry_operations;
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
d_add(dentry, NULL);

diff -ur 2.1.49/linux/fs/dcache.c linux/fs/dcache.c
--- 2.1.49/linux/fs/dcache.c Tue Aug 12 23:40:41 1997
+++ linux/fs/dcache.c Thu Aug 14 20:10:07 1997
@@ -178,7 +178,7 @@
dentry->d_name.name = str;
dentry->d_name.len = name->len;
dentry->d_name.hash = name->hash;
- dentry->d_revalidate = NULL;
+ dentry->d_op = NULL;
return dentry;
}

@@ -237,8 +237,14 @@
continue;
if (dentry->d_parent != parent)
continue;
- if (memcmp(dentry->d_name.name, str, len))
- continue;
+ if (parent && parent->d_op && parent->d_op->d_compare) {
+ if(parent->d_op->d_compare(parent,
+ &dentry->d_name, name))
+ continue;
+ } else {
+ if (memcmp(dentry->d_name.name, str, len))
+ continue;
+ }
return dentry;
}
return NULL;
diff -ur 2.1.49/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- 2.1.49/linux/fs/msdos/namei.c Tue Aug 12 23:40:27 1997
+++ linux/fs/msdos/namei.c Thu Aug 14 20:38:37 1997
@@ -63,24 +63,6 @@
NULL
};

-struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
-{
- struct super_block *res;
-
- MOD_INC_USE_COUNT;
-
- sb->s_op = &msdos_sops;
- res = fat_read_super(sb, data, silent);
- if (res == NULL)
- MOD_DEC_USE_COUNT;
-
- return res;
-}
-
-
-
-
-
/***** Formats an MS-DOS file name. Rejects invalid names. */
static int msdos_format_name(char conv,const char *name,int len,
char *res,int dot_dirs,char dotsOK)
@@ -190,6 +172,73 @@
return fat_scan(dir,msdos_name,bh,de,ino,scantype);
}

+
+static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash;
+ char msdos_name[MSDOS_NAME];
+ int error;
+ int i;
+ struct fat_mount_options *options =
+ & (MSDOS_SB(dentry->d_inode->i_sb)->options);
+
+ error = msdos_format_name(options->name_check,
+ qstr->name, qstr->len, msdos_name,1,
+ options->dotsOK);
+ if(error)
+ return error;
+ hash = init_name_hash();
+ for(i=0; i< MSDOS_NAME; i++)
+ hash = partial_name_hash(msdos_name[i], hash);
+ qstr->hash = end_name_hash(hash);
+ return 0;
+}
+
+
+static int msdos_cmp(struct dentry *dentry,
+ struct qstr *a, struct qstr *b)
+{
+ char a_msdos_name[MSDOS_NAME],b_msdos_name[MSDOS_NAME];
+ int error;
+ struct fat_mount_options *options =
+ & (MSDOS_SB(dentry->d_inode->i_sb)->options);
+
+ error = msdos_format_name(options->name_check,
+ a->name, a->len, a_msdos_name,1,
+ options->dotsOK);
+ if(error)
+ return error;
+ error = msdos_format_name(options->name_check,
+ b->name, b->len, b_msdos_name,1,
+ options->dotsOK);
+ if(error)
+ return error;
+
+ return memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
+}
+
+
+static struct dentry_operations msdos_dentry_operations = {
+ 0, /* d_revalidate */
+ msdos_hash,
+ msdos_cmp
+};
+
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
+{
+ struct super_block *res;
+
+ MOD_INC_USE_COUNT;
+
+ sb->s_op = &msdos_sops;
+ res = fat_read_super(sb, data, silent);
+ if (res == NULL)
+ MOD_DEC_USE_COUNT;
+ sb->s_root->d_op = &msdos_dentry_operations;
+ return res;
+}
+
+
/***** Get inode using directory and name */
int msdos_lookup(struct inode *dir,struct dentry *dentry)
{
@@ -201,10 +250,20 @@

PRINTK (("msdos_lookup\n"));

- if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
+ dentry->d_op = &msdos_dentry_operations;
+
+ if(!dir) {
d_add(dentry, NULL);
return 0;
}
+
+ if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
+ if(res == -ENOENT) {
+ d_add(dentry, NULL);
+ res = 0;
+ }
+ return res;
+ }
PRINTK (("msdos_lookup 4\n"));
if (bh)
fat_brelse(sb, bh);
@@ -229,6 +288,7 @@
iput(inode);
if (!(inode = iget(next->i_sb,next->i_ino))) {
fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
+ d_add(dentry, NULL);
return -ENOENT;
}
}
@@ -248,6 +308,9 @@
struct msdos_dir_entry *de;
int res,ino;

+ if(!dir)
+ return -ENOENT;
+
*result = NULL;
if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
if (res != -ENOENT) return res;
@@ -748,7 +811,10 @@
NULL, /* writepage */
fat_bmap, /* bmap */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
};


diff -ur 2.1.49/linux/fs/namei.c linux/fs/namei.c
--- 2.1.49/linux/fs/namei.c Tue Aug 12 23:40:27 1997
+++ linux/fs/namei.c Thu Aug 14 20:31:23 1997
@@ -268,8 +268,8 @@
{
struct dentry * dentry = d_lookup(parent, name);

- if (dentry && dentry->d_revalidate) {
- int validated, (*revalidate)(struct dentry *) = dentry->d_revalidate;
+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+ int validated, (*revalidate)(struct dentry *) = dentry->d_op->d_revalidate;

dentry->d_count++;
validated = revalidate(dentry) || d_invalidate(dentry);
@@ -350,16 +350,6 @@
}

/*
- * Allow a filesystem to translate the character set of
- * a file name. This allows for filesystems that are not
- * case-sensitive, for example.
- *
- * This is only a dummy define right now, but eventually
- * it might become something like "(parent)->d_charmap[c]"
- */
-#define name_translate_char(parent, c) (c)
-
-/*
* Name resolution.
*
* This is the basic name resolution function, turning a pathname
@@ -406,18 +396,30 @@
break;

this.name = name;
- hash = init_name_hash();
len = 0;
c = *name;
- do {
- len++; name++;
- c = name_translate_char(base, c);
- hash = partial_name_hash(c, hash);
- c = *name;
- } while (c && (c != '/'));
-
- this.len = len;
- this.hash = end_name_hash(hash);
+ if(base->d_op && base->d_op->d_hash) {
+ int error;
+ do {
+ len++; name++;
+ c = *name;
+ } while (c && (c != '/'));
+ this.len = len;
+ error = base->d_op->d_hash(base, &this);
+ if(error<0) {
+ dentry = ERR_PTR(error);
+ break;
+ }
+ } else {
+ hash = init_name_hash();
+ do {
+ len++; name++;
+ hash = partial_name_hash(c, hash);
+ c = *name;
+ } while (c && (c != '/'));
+ this.len = len;
+ this.hash = end_name_hash(hash);
+ }

/* remove trailing slashes? */
follow = follow_link;
diff -ur 2.1.49/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- 2.1.49/linux/fs/nfs/dir.c Thu Aug 14 18:32:12 1997
+++ linux/fs/nfs/dir.c Thu Aug 14 18:46:40 1997
@@ -349,6 +349,12 @@
return time < max;
}

+static struct dentry_operations nfs_dentry_operations = {
+ nfs_lookup_revalidate,
+ 0, /* d_hash */
+ 0, /* d_compare */
+};
+
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
struct inode *inode;
@@ -380,7 +386,7 @@
return error;

dentry->d_time = jiffies;
- dentry->d_revalidate = nfs_lookup_revalidate;
+ dentry->d_op = &nfs_dentry_operations;
d_add(dentry, inode);
return 0;
}
diff -ur 2.1.49/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- 2.1.49/linux/include/linux/dcache.h Tue Aug 12 23:42:14 1997
+++ linux/include/linux/dcache.h Thu Aug 14 20:38:58 1997
@@ -50,8 +50,23 @@
struct list_head d_lru; /* d_count = 0 LRU list */
struct qstr d_name;
unsigned long d_time; /* used by d_revalidate */
+ struct dentry_operations *d_op;
+};
+
+struct dentry_operations {
int (*d_revalidate)(struct dentry *);
+ int (*d_hash) (struct dentry *,struct qstr *);
+ int (*d_compare) (struct dentry *,struct qstr *, struct qstr *);
};
+
+/* the dentry parameter passed to d_hash and d_compare is the parent
+ * directory of the entries to be compared. It is used in case these
+ * functions need any directory specific information for determining
+ * equivalency classes. Using the dentry itself might not work, as it
+ * might be a negative dentry which has no information associated with
+ * it */
+
+

/* d_flags entries */
#define DCACHE_AUTOFS_PENDING 0x0001 /* autofs: "under construction" */