fuse: fix stat call on 32 bit platforms

Now we store attr->ino at inode->i_ino, return attr->ino at the
first time and then return inode->i_ino if the attribute timeout
isn't expired. That's wrong on 32 bit platforms because attr->ino
is 64 bit and inode->i_ino is 32 bit in this case.

Fix this by saving 64 bit ino in fuse_inode structure and returning
it every time we call getattr. Also squash attr->ino into inode->i_ino
explicitly.

Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
Pavel Shilovsky 2012-05-10 19:49:38 +04:00 committed by Miklos Szeredi
parent 519c6040ce
commit 45c72cd73c
3 changed files with 20 additions and 1 deletions

View File

@ -863,6 +863,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
if (stat) { if (stat) {
generic_fillattr(inode, stat); generic_fillattr(inode, stat);
stat->mode = fi->orig_i_mode; stat->mode = fi->orig_i_mode;
stat->ino = fi->orig_ino;
} }
} }

View File

@ -82,6 +82,9 @@ struct fuse_inode {
preserve the original mode */ preserve the original mode */
umode_t orig_i_mode; umode_t orig_i_mode;
/** 64 bit inode number */
u64 orig_ino;
/** Version of last attribute change */ /** Version of last attribute change */
u64 attr_version; u64 attr_version;

View File

@ -91,6 +91,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
fi->nlookup = 0; fi->nlookup = 0;
fi->attr_version = 0; fi->attr_version = 0;
fi->writectr = 0; fi->writectr = 0;
fi->orig_ino = 0;
INIT_LIST_HEAD(&fi->write_files); INIT_LIST_HEAD(&fi->write_files);
INIT_LIST_HEAD(&fi->queued_writes); INIT_LIST_HEAD(&fi->queued_writes);
INIT_LIST_HEAD(&fi->writepages); INIT_LIST_HEAD(&fi->writepages);
@ -139,6 +140,18 @@ static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
return 0; return 0;
} }
/*
* ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
* so that it will fit.
*/
static ino_t fuse_squash_ino(u64 ino64)
{
ino_t ino = (ino_t) ino64;
if (sizeof(ino_t) < sizeof(u64))
ino ^= ino64 >> (sizeof(u64) - sizeof(ino_t)) * 8;
return ino;
}
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
u64 attr_valid) u64 attr_valid)
{ {
@ -148,7 +161,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
fi->attr_version = ++fc->attr_version; fi->attr_version = ++fc->attr_version;
fi->i_time = attr_valid; fi->i_time = attr_valid;
inode->i_ino = attr->ino; inode->i_ino = fuse_squash_ino(attr->ino);
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
set_nlink(inode, attr->nlink); set_nlink(inode, attr->nlink);
inode->i_uid = attr->uid; inode->i_uid = attr->uid;
@ -174,6 +187,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
fi->orig_i_mode = inode->i_mode; fi->orig_i_mode = inode->i_mode;
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
inode->i_mode &= ~S_ISVTX; inode->i_mode &= ~S_ISVTX;
fi->orig_ino = attr->ino;
} }
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,