Is it valid to call d_add on a negative dentry?
(or on a dentry that is already linked in d_hash, but all negative
 dentries are, right?)

I'm guessing it isn't because I think that is how I can get my machine to
hang in d_lookup, looping on a corrupt d_hash list.


The problem can be reproduced like this. /mnt/smb is a smbfs mount of
/mnt/samba/export from a samba server on localhost.

/mnt/smb% ls
aa
/mnt/smb% rm aa
/mnt/smb% touch /mnt/samba/export/aa
/mnt/smb% ls
ls: aa: No such file or directory

And shortly after it will lock up completely.


My printk's tell me that a negative dentry is still hashed since d_hash is
non-empty. d_add calls d_instantiate and d_rehash, the later adds it to a
d_hash list without first removing it. But it was already linked so now 2
extra dentries are also pointing to this dentry. And it is then no longer
a list ...

The attached patch fixes things for me. Comments?

Oh, and I *think* ncpfs has the same problem. But that's just from reading
the code.

/Urban
diff -urN -X exclude linux-2.4.2-ac11-orig/fs/smbfs/cache.c 
linux-2.4.2-ac11-smbfs/fs/smbfs/cache.c
--- linux-2.4.2-ac11-orig/fs/smbfs/cache.c      Thu Feb 22 20:52:03 2001
+++ linux-2.4.2-ac11-smbfs/fs/smbfs/cache.c     Mon Mar  5 23:48:12 2001
@@ -167,6 +167,7 @@
        struct inode *newino, *inode = dentry->d_inode;
        struct smb_cache_control ctl = *ctrl;
        int valid = 0;
+       int hashed = 0;
        ino_t ino = 0;
 
        qname->hash = full_name_hash(qname->name, qname->len);
@@ -181,9 +182,11 @@
                newdent = d_alloc(dentry, qname);
                if (!newdent)
                        goto end_advance;
-       } else
+       } else {
+               hashed = 1;
                memcpy((char *) newdent->d_name.name, qname->name,
                       newdent->d_name.len);
+       }
 
        if (!newdent->d_inode) {
                smb_renew_times(newdent);
@@ -191,7 +194,10 @@
                newino = smb_iget(inode->i_sb, entry);
                if (newino) {
                        smb_new_dentry(newdent);
-                       d_add(newdent, newino);
+                       if (hashed)
+                               d_instantiate(newdent, newino);
+                       else
+                               d_add(newdent, newino);
                }
        } else
                smb_set_inode_attr(newdent->d_inode, entry);

Reply via email to