|
@@ -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) */
|