]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/cramfs/inode.c
Merge branch 'for-2.6.38/core' of git://git.kernel.dk/linux-2.6-block
[mv-sheeva.git] / fs / cramfs / inode.c
index 32fd5fe9ca0e5b8cd13a4a25c4c7a194e235bf1f..e141939080f0d6db65c525305cab5a74b9dd552c 100644 (file)
@@ -34,57 +34,81 @@ static const struct address_space_operations cramfs_aops;
 static DEFINE_MUTEX(read_mutex);
 
 
-/* These two macros may change in future, to provide better st_ino
-   semantics. */
-#define CRAMINO(x)     (((x)->offset && (x)->size)?(x)->offset<<2:1)
+/* These macros may change in future, to provide better st_ino semantics. */
 #define OFFSET(x)      ((x)->i_ino)
 
-static void setup_inode(struct inode *inode, struct cramfs_inode * cramfs_inode)
+static unsigned long cramino(struct cramfs_inode *cino, unsigned int offset)
 {
+       if (!cino->offset)
+               return offset + 1;
+       if (!cino->size)
+               return offset + 1;
+
+       /*
+        * The file mode test fixes buggy mkcramfs implementations where
+        * cramfs_inode->offset is set to a non zero value for entries
+        * which did not contain data, like devices node and fifos.
+        */
+       switch (cino->mode & S_IFMT) {
+       case S_IFREG:
+       case S_IFDIR:
+       case S_IFLNK:
+               return cino->offset << 2;
+       default:
+               break;
+       }
+       return offset + 1;
+}
+
+static struct inode *get_cramfs_inode(struct super_block *sb,
+       struct cramfs_inode *cramfs_inode, unsigned int offset)
+{
+       struct inode *inode;
        static struct timespec zerotime;
+
+       inode = iget_locked(sb, cramino(cramfs_inode, offset));
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+       if (!(inode->i_state & I_NEW))
+               return inode;
+
+       switch (cramfs_inode->mode & S_IFMT) {
+       case S_IFREG:
+               inode->i_fop = &generic_ro_fops;
+               inode->i_data.a_ops = &cramfs_aops;
+               break;
+       case S_IFDIR:
+               inode->i_op = &cramfs_dir_inode_operations;
+               inode->i_fop = &cramfs_directory_operations;
+               break;
+       case S_IFLNK:
+               inode->i_op = &page_symlink_inode_operations;
+               inode->i_data.a_ops = &cramfs_aops;
+               break;
+       default:
+               init_special_inode(inode, cramfs_inode->mode,
+                               old_decode_dev(cramfs_inode->size));
+       }
+
        inode->i_mode = cramfs_inode->mode;
        inode->i_uid = cramfs_inode->uid;
-       inode->i_size = cramfs_inode->size;
-       inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
        inode->i_gid = cramfs_inode->gid;
+
+       /* if the lower 2 bits are zero, the inode contains data */
+       if (!(inode->i_ino & 3)) {
+               inode->i_size = cramfs_inode->size;
+               inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
+       }
+
        /* Struct copy intentional */
        inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime;
        /* inode->i_nlink is left 1 - arguably wrong for directories,
           but it's the best we can do without reading the directory
           contents.  1 yields the right result in GNU find, even
           without -noleaf option. */
-       if (S_ISREG(inode->i_mode)) {
-               inode->i_fop = &generic_ro_fops;
-               inode->i_data.a_ops = &cramfs_aops;
-       } else if (S_ISDIR(inode->i_mode)) {
-               inode->i_op = &cramfs_dir_inode_operations;
-               inode->i_fop = &cramfs_directory_operations;
-       } else if (S_ISLNK(inode->i_mode)) {
-               inode->i_op = &page_symlink_inode_operations;
-               inode->i_data.a_ops = &cramfs_aops;
-       } else {
-               init_special_inode(inode, inode->i_mode,
-                       old_decode_dev(cramfs_inode->size));
-       }
-}
 
-static struct inode *get_cramfs_inode(struct super_block *sb,
-                               struct cramfs_inode * cramfs_inode)
-{
-       struct inode *inode;
-       if (CRAMINO(cramfs_inode) == 1) {
-               inode = new_inode(sb);
-               if (inode) {
-                       inode->i_ino = 1;
-                       setup_inode(inode, cramfs_inode);
-               }
-       } else {
-               inode = iget_locked(sb, CRAMINO(cramfs_inode));
-               if (inode && (inode->i_state & I_NEW)) {
-                       setup_inode(inode, cramfs_inode);
-                       unlock_new_inode(inode);
-               }
-       }
+       unlock_new_inode(inode);
+
        return inode;
 }
 
@@ -265,6 +289,9 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
                printk(KERN_ERR "cramfs: root is not a directory\n");
                goto out;
        }
+       /* correct strange, hard-coded permissions of mkcramfs */
+       super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+
        root_offset = super.root.offset << 2;
        if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
                sbi->size=super.size;
@@ -289,7 +316,7 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 
        /* Set it all up.. */
        sb->s_op = &cramfs_ops;
-       root = get_cramfs_inode(sb, &super.root);
+       root = get_cramfs_inode(sb, &super.root, 0);
        if (!root)
                goto out;
        sb->s_root = d_alloc_root(root);
@@ -365,7 +392,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                 */
                namelen = de->namelen << 2;
                memcpy(buf, name, namelen);
-               ino = CRAMINO(de);
+               ino = cramino(de, OFFSET(inode) + offset);
                mode = de->mode;
                mutex_unlock(&read_mutex);
                nextoffset = offset + sizeof(*de) + namelen;
@@ -404,8 +431,9 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
                struct cramfs_inode *de;
                char *name;
                int namelen, retval;
+               int dir_off = OFFSET(dir) + offset;
 
-               de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
+               de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN);
                name = (char *)(de+1);
 
                /* Try to take advantage of sorted directories */
@@ -436,7 +464,7 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
                if (!retval) {
                        struct cramfs_inode entry = *de;
                        mutex_unlock(&read_mutex);
-                       d_add(dentry, get_cramfs_inode(dir->i_sb, &entry));
+                       d_add(dentry, get_cramfs_inode(dir->i_sb, &entry, dir_off));
                        return NULL;
                }
                /* else (retval < 0) */