Arla 0.25 on Linux 2.2.10 bug

Assar Westerlund assar at stacken.kth.se
Sat Aug 7 09:13:06 CEST 1999


Martin Dickopp <martin at pktw16.phy.tu-dresden.de> writes:
> Sometimes two distinct files get the same inode number, so that some
> programs (e.g. emacs) believe it's the same file.  I don't know how
> to reliably reproduce this, but when I check the inode numbers of a
> large number of files (a few hundered), there are always some with
> the same inode number.  The patch below makes Arla use the same inode
> numbers as Transarc AFS.

[ getting back to this ]

There are two different problems with inode-collisions on Linux.
First of all the kernel keeps inodes indexed in a hash table hashed by
(superblock, inode-number).  And second you have the problem of
applications that look at the inode numbers they get back from *stat()
and use these to see what files are really the same.

A simple way of making sure that the kernel doesn't think two
different files are really the same is to make sure that the inode
numbers are unique at a particular point in time, because the kernel
should never compare inodes that don't exist simultaneously in the
kernel, for example by making the `inode *' into the inode number.

This however, can fail the second criteria, and can return two inodes
(that existed at different times in the kernel) with the same number
to an application.  The usual way of handling this is to calculate the
inode number as (volume & 0x7fff) << 16 | (vnode & 0xffffffff).  This
could however fail to first criteria.

We've tried solving both problems at the same time by introducing a
new inode-operation to linux called `getattr' which is called whenever
attributes are to be returned to an application.  This allows us to
keep the inode number used in the kernel different from those seen by
applications.

Appended to this mail is the (small) kernel patch to Linux to add
`getattr' (and clean-up some stat handling).  The current arla code
from CVS should automatically figure out if getattr is available and
use it.

Please try it and tell us how well it works.  Thanks.

/assar


--- fs/stat.c.orig      Tue Jul 13 20:17:22 1999
+++ fs/stat.c   Thu Jul 15 18:14:00 1999
@@ -24,56 +24,22 @@
 }
 
 
-#if !defined(__alpha__) && !defined(__sparc__)
-
-/*
- * For backward compatibility?  Maybe this should be moved
- * into arch/i386 instead?
- */
-static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
-{
-       static int warncount = 5;
-       struct __old_kernel_stat tmp;
-
-       if (warncount) {
-               warncount--;
-               printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n",
-                       current->comm);
-       }
-
-       tmp.st_dev = kdev_t_to_nr(inode->i_dev);
-       tmp.st_ino = inode->i_ino;
-       tmp.st_mode = inode->i_mode;
-       tmp.st_nlink = inode->i_nlink;
-       tmp.st_uid = inode->i_uid;
-       tmp.st_gid = inode->i_gid;
-       tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
-       tmp.st_size = inode->i_size;
-       tmp.st_atime = inode->i_atime;
-       tmp.st_mtime = inode->i_mtime;
-       tmp.st_ctime = inode->i_ctime;
-       return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
-}
-
-#endif
-
-static int cp_new_stat(struct inode * inode, struct stat * statbuf)
+int generic_getattr(struct inode * inode, struct stat * statbuf)
 {
-       struct stat tmp;
        unsigned int blocks, indirect;
 
-       memset(&tmp, 0, sizeof(tmp));
-       tmp.st_dev = kdev_t_to_nr(inode->i_dev);
-       tmp.st_ino = inode->i_ino;
-       tmp.st_mode = inode->i_mode;
-       tmp.st_nlink = inode->i_nlink;
-       tmp.st_uid = inode->i_uid;
-       tmp.st_gid = inode->i_gid;
-       tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
-       tmp.st_size = inode->i_size;
-       tmp.st_atime = inode->i_atime;
-       tmp.st_mtime = inode->i_mtime;
-       tmp.st_ctime = inode->i_ctime;
+       memset(statbuf, 0, sizeof(*statbuf));
+       statbuf->st_dev = kdev_t_to_nr(inode->i_dev);
+       statbuf->st_ino = inode->i_ino;
+       statbuf->st_mode = inode->i_mode;
+       statbuf->st_nlink = inode->i_nlink;
+       statbuf->st_uid = inode->i_uid;
+       statbuf->st_gid = inode->i_gid;
+       statbuf->st_rdev = kdev_t_to_nr(inode->i_rdev);
+       statbuf->st_size = inode->i_size;
+       statbuf->st_atime = inode->i_atime;
+       statbuf->st_mtime = inode->i_mtime;
+       statbuf->st_ctime = inode->i_ctime;
 /*
  * st_blocks and st_blksize are approximated with a simple algorithm if
  * they aren't supported directly by the filesystem. The minix and msdos
@@ -87,13 +53,13 @@
  * Use minix fs values for the number of direct and indirect blocks.  The
  * count is now exact for the minix fs except that it counts zero blocks.
  * Everything is in units of BLOCK_SIZE until the assignment to
- * tmp.st_blksize.
+ * statbuf->st_blksize.
  */
 #define D_B   7
 #define I_B   (BLOCK_SIZE / sizeof(unsigned short))
 
        if (!inode->i_blksize) {
-               blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
+               blocks = (statbuf->st_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
                if (blocks > D_B) {
                        indirect = (blocks - D_B + I_B - 1) / I_B;
                        blocks += indirect;
@@ -104,15 +70,72 @@
                                        blocks++;
                        }
                }
-               tmp.st_blocks = (BLOCK_SIZE / 512) * blocks;
-               tmp.st_blksize = BLOCK_SIZE;
+               statbuf->st_blocks = (BLOCK_SIZE / 512) * blocks;
+               statbuf->st_blksize = BLOCK_SIZE;
        } else {
-               tmp.st_blocks = inode->i_blocks;
-               tmp.st_blksize = inode->i_blksize;
+               statbuf->st_blocks = inode->i_blocks;
+               statbuf->st_blksize = inode->i_blksize;
+       }
+       return 0;
+}
+
+int vfs_getattr(struct inode * inode, struct stat * statbuf)
+{
+       if (inode->i_op && inode->i_op->getattr)
+               return inode->i_op->getattr (inode, statbuf);
+       else
+               return generic_getattr (inode, statbuf);
        }
+
+#if !defined(__alpha__) && !defined(__sparc__)
+
+/*
+ * For backward compatibility?  Maybe this should be moved
+ * into arch/i386 instead?
+ */
+static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
+{
+       static int warncount = 5;
+       struct __old_kernel_stat tmp;
+       struct stat new_tmp;
+       int ret;
+
+       if (warncount) {
+               warncount--;
+               printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n",
+                       current->comm);
+       }
+
+       ret = vfs_getattr (inode, &new_tmp);
+       if (ret)
+               return ret;
+       tmp.st_dev   = new_tmp.st_dev;
+       tmp.st_ino   = new_tmp.st_ino;
+       tmp.st_mode  = new_tmp.st_mode;
+       tmp.st_nlink = new_tmp.st_nlink;
+       tmp.st_uid   = new_tmp.st_uid;
+       tmp.st_gid   = new_tmp.st_gid;
+       tmp.st_rdev  = new_tmp.st_rdev;
+       tmp.st_size  = new_tmp.st_size;
+       tmp.st_atime = new_tmp.st_atime;
+       tmp.st_mtime = new_tmp.st_mtime;
+       tmp.st_ctime = new_tmp.st_ctime;
+
        return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
 }
 
+#endif
+
+static int cp_new_stat(struct inode * inode, struct stat * statbuf)
+{
+       struct stat tmp;
+       int ret;
+
+       ret = vfs_getattr (inode, &tmp);
+       if (ret)
+               return ret;
+       return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
 
 #if !defined(__alpha__) && !defined(__sparc__)
 /*
--- include/linux/fs.h.orig     Tue Jan 26 05:00:51 1999
+++ include/linux/fs.h  Thu Jul 15 18:10:10 1999
@@ -613,6 +613,7 @@
        int (*smap) (struct inode *,int);
        int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int, int);
        int (*revalidate) (struct dentry *);
+       int (*getattr) (struct inode *, struct stat *);
 };
 
 struct super_operations {
@@ -876,6 +877,9 @@
 
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern void inode_setattr(struct inode *, struct iattr *);
+
+extern int generic_getattr(struct inode *, struct stat *);
+extern int vfs_getattr(struct inode * inode, struct stat * statbuf);
 
 /* kludge to get SCSI modules working */
 #include <linux/minix_fs.h>









More information about the Arla-drinkers mailing list