printk(KERN_ERR "unionfs: no superblock for "
"file %p\n", file);
else {
- unionfs_read_lock(sb);
/* decrement count of open files */
branchput(sb, i);
- unionfs_read_unlock(sb);
/*
* fput will perform an mntput for us on the
* correct branch. Although we're using the
dget(lower_dentry);
unionfs_mntget(dentry, bindex);
- unionfs_read_lock(sb);
branchget(sb, bindex);
- unionfs_read_unlock(sb);
lower_file =
dentry_open(lower_dentry,
dget(lower_dentry);
unionfs_mntget(dentry, bstart);
- unionfs_read_lock(sb);
branchget(sb, bstart);
- unionfs_read_unlock(sb);
lower_file = dentry_open(lower_dentry,
unionfs_lower_mnt_idx(dentry, bstart),
file->f_flags);
bend = fbend(file);
for (bindex = bstart; bindex <= bend; bindex++) {
if (unionfs_lower_file_idx(file, bindex)) {
- unionfs_read_lock(dentry->d_sb);
branchput(dentry->d_sb, bindex);
- unionfs_read_unlock(dentry->d_sb);
fput(unionfs_lower_file_idx(file, bindex));
unionfs_set_lower_file_idx(file, bindex, NULL);
}
* The branchget goes after the open, because otherwise
* we would miss the reference on release.
*/
- unionfs_read_lock(inode->i_sb);
branchget(inode->i_sb, bindex);
- unionfs_read_unlock(inode->i_sb);
}
return 0;
return PTR_ERR(lower_file);
unionfs_set_lower_file(file, lower_file);
- unionfs_read_lock(inode->i_sb);
branchget(inode->i_sb, bstart);
- unionfs_read_unlock(inode->i_sb);
return 0;
}
int size;
unionfs_read_lock(inode->i_sb);
+
file->private_data =
kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL);
if (!UNIONFS_F(file)) {
if (!lower_file)
continue;
- unionfs_read_lock(file->f_dentry->d_sb);
branchput(file->f_dentry->d_sb, bindex);
- unionfs_read_unlock(file->f_dentry->d_sb);
/* fput calls dput for lower_dentry */
fput(lower_file);
}
return err;
}
-/* release all lower object references & free the file info structure */
+/*
+ * release all lower object references & free the file info structure
+ *
+ * No need to grab sb info's rwsem.
+ */
int unionfs_file_release(struct inode *inode, struct file *file)
{
struct file *lower_file = NULL;
if (lower_file) {
fput(lower_file);
- unionfs_read_lock(sb);
branchput(sb, bindex);
- unionfs_read_unlock(sb);
}
}
kfree(fileinfo->lower_files);
{
long err;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
+
if ((err = unionfs_file_revalidate(file, 1)))
goto out;
}
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
unionfs_check_file(file);
return err;
}
{
int err = 0;
struct file *lower_file = NULL;
- struct dentry *dentry = file->f_dentry;
+ struct dentry *dentry = file->f_path.dentry;
int bindex, bstart, bend;
unionfs_read_lock(dentry->d_sb);
/* open old file */
unionfs_mntget(dentry, old_bindex);
- unionfs_read_lock(sb);
branchget(sb, old_bindex);
- unionfs_read_unlock(sb);
/* dentry_open calls dput and mntput if it returns an error */
input_file = dentry_open(old_lower_dentry,
unionfs_lower_mnt_idx(dentry, old_bindex),
/* open new file */
dget(new_lower_dentry);
output_mnt = unionfs_mntget(sb->s_root, new_bindex);
- unionfs_read_lock(sb);
branchget(sb, new_bindex);
- unionfs_read_unlock(sb);
output_file = dentry_open(new_lower_dentry, output_mnt,
O_WRONLY | O_LARGEFILE);
if (IS_ERR(output_file)) {
fput(output_file);
out_close_in2:
- unionfs_read_lock(sb);
branchput(sb, new_bindex);
- unionfs_read_unlock(sb);
out_close_in:
fput(input_file);
out:
- unionfs_read_lock(sb);
branchput(sb, old_bindex);
- unionfs_read_unlock(sb);
return err;
}
/* need to close the file */
fput(*copyup_file);
- unionfs_read_lock(sb);
branchput(sb, new_bindex);
- unionfs_read_unlock(sb);
}
/*
{
int err;
+ unionfs_read_lock(dentry->d_sb);
+
unionfs_lock_dentry(dentry);
err = __unionfs_d_revalidate_chain(dentry, nd, 0);
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
+
return err;
}
{
int bindex, bstart, bend;
+ unionfs_read_lock(dentry->d_sb);
+
unionfs_check_dentry(dentry);
/* this could be a negative dentry, so check first */
if (!UNIONFS_D(dentry)) {
free_dentry_private_data(dentry);
out:
+ unionfs_read_unlock(dentry->d_sb);
return;
}
int bend;
loff_t offset;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
+
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
file->f_pos = rdstate2offset(uds);
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
return err;
}
struct unionfs_dir_state *rdstate;
loff_t err;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
+
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
}
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
return err;
}
struct sioq_args args;
sb = dentry->d_sb;
- unionfs_read_lock(sb);
BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
BUG_ON(bindex < dbstart(dentry));
mutex_unlock(&lower_dir->i_mutex);
out:
- unionfs_read_unlock(sb);
return err;
}
sb = dentry->d_sb;
- unionfs_read_lock(sb);
BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
dget(lower_dentry);
unionfs_mntget(dentry, bindex);
- unionfs_read_lock(sb);
branchget(sb, bindex);
- unionfs_read_unlock(sb);
lower_file =
dentry_open(lower_dentry,
unionfs_lower_mnt_idx(dentry, bindex),
if (IS_ERR(lower_file)) {
err = PTR_ERR(lower_file);
dput(lower_dentry);
- unionfs_read_lock(sb);
branchput(sb, bindex);
- unionfs_read_unlock(sb);
goto out;
}
/* fput calls dput for lower_dentry */
fput(lower_file);
- unionfs_read_lock(sb);
branchput(sb, bindex);
- unionfs_read_unlock(sb);
if (err < 0)
goto out;
kfree(buf);
}
- unionfs_read_unlock(sb);
return err;
}
{
int err;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
unionfs_check_file(file);
unionfs_lower_dentry(file->f_path.dentry));
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
unionfs_check_file(file);
return err;
}
int err = 0;
struct file *file = iocb->ki_filp;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
unionfs_check_file(file);
unionfs_lower_dentry(file->f_path.dentry));
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
unionfs_check_file(file);
return err;
}
{
int err = 0;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
if ((err = unionfs_file_revalidate(file, 1)))
goto out;
unionfs_check_file(file);
}
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
return err;
}
int willwrite;
struct file *lower_file;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb);
/* This might be deferred to mmap's writepage */
willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
}
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(file->f_path.dentry->d_sb);
if (!err) {
/* copyup could cause parent dir times to change */
unionfs_copy_attr_times(file->f_dentry->d_parent->d_inode);
char *name = NULL;
int valid = 0;
- /*
- * We have to read-lock the superblock rwsem, and we have to
- * revalidate the parent dentry and this one. A branch-management
- * operation could have taken place, mid-way through a VFS operation
- * that eventually reaches here. So we have to ensure consistency,
- * just as we do with the file operations.
- *
- * XXX: we may need to do this for all other inode ops that take a
- * dentry.
- */
unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
return err;
}
+/*
+ * unionfs_lookup is the only special function which takes a dentry, yet we
+ * do NOT want to call __unionfs_d_revalidate_chain because by definition,
+ * we don't have a valid dentry here yet.
+ */
static struct dentry *unionfs_lookup(struct inode *parent,
struct dentry *dentry,
struct nameidata *nd)
struct path path_save;
struct dentry *ret;
- /*
- * lookup is the only special function which takes a dentry, yet we
- * do NOT want to call __unionfs_d_revalidate_chain because by
- * definition, we don't have a valid dentry here yet.
- */
+ unionfs_read_lock(dentry->d_sb);
/* save the dentry & vfsmnt from namei */
if (nd) {
unionfs_check_inode(parent);
unionfs_check_dentry(dentry);
unionfs_check_dentry(dentry->d_parent);
+ unionfs_read_unlock(dentry->d_sb);
+
return ret;
}
struct dentry *whiteout_dentry;
char *name = NULL;
+ unionfs_read_lock(old_dentry->d_sb);
unionfs_double_lock_dentry(new_dentry, old_dentry);
if (!__unionfs_d_revalidate_chain(old_dentry, NULL, 0)) {
unionfs_check_inode(dir);
unionfs_check_dentry(new_dentry);
unionfs_check_dentry(old_dentry);
+ unionfs_read_unlock(old_dentry->d_sb);
+
return err;
}
int bindex = 0, bstart;
char *name = NULL;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
unionfs_check_inode(dir);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
+
return err;
}
int whiteout_unlinked = 0;
struct sioq_args args;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
unionfs_unlock_dentry(dentry);
unionfs_check_inode(parent);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
+
return err;
}
char *name = NULL;
int whiteout_unlinked = 0;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
unionfs_check_inode(dir);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
+
return err;
}
int err;
struct dentry *lower_dentry;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
+
return err;
}
-/* We don't lock the dentry here, because readlink does the heavy lifting. */
+/*
+ * unionfs_follow_link takes a dentry, but it is simple. It only needs to
+ * allocate some memory and then call our ->readlink method. Our
+ * unionfs_readlink *does* lock our dentry and revalidate the dentry.
+ * Therefore, we do not have to lock our dentry here, to prevent a deadlock;
+ * nor do we need to revalidate it either. It is safe to not lock our
+ * dentry here because unionfs_follow_link does not do anything (prior to
+ * calling ->readlink) which could become inconsistent due to branch
+ * management.
+ */
static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
char *buf;
int len = PAGE_SIZE, err;
mm_segment_t old_fs;
- unionfs_lock_dentry(dentry);
-
- if (dentry->d_inode &&
- !__unionfs_d_revalidate_chain(dentry, nd, 0)) {
- err = -ESTALE;
- goto out;
- }
+ unionfs_read_lock(dentry->d_sb);
/* This is freed by the put_link method assuming a successful call. */
buf = kmalloc(len, GFP_KERNEL);
err = 0;
out:
- unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return ERR_PTR(err);
}
+/* FIXME: We may not have to lock here */
static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
void *cookie)
{
+ unionfs_read_lock(dentry->d_sb);
+
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, nd, 0))
printk("unionfs: put_link failed to revalidate dentry\n");
unionfs_check_dentry(dentry);
kfree(nd_get_link(nd));
+ unionfs_read_unlock(dentry->d_sb);
}
/*
(!strcmp("nfs", (inode)->i_sb->s_type->name)) &&
(nd) && (nd->mnt) && (nd->mnt->mnt_sb)) {
int perms;
- unionfs_read_lock(nd->mnt->mnt_sb);
perms = branchperms(nd->mnt->mnt_sb, bindex);
- unionfs_read_unlock(nd->mnt->mnt_sb);
if (perms & MAY_NFSRO)
retval = generic_permission(inode, submask,
NULL);
int i;
int copyup = 0;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
unionfs_check_dentry(dentry->d_parent);
+ unionfs_read_unlock(dentry->d_sb);
+
return err;
}
return perms;
}
-/* parse the dirs= mount argument */
+/*
+ * parse the dirs= mount argument
+ *
+ * We don't need to lock the superblock private data's rwsem, as we get
+ * called only by unionfs_read_super - it is still a long time before anyone
+ * can even get a reference to us.
+ */
static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info
*lower_root_info, char *options)
{
lower_root_info->lower_paths[bindex].dentry = nd.dentry;
lower_root_info->lower_paths[bindex].mnt = nd.mnt;
- unionfs_write_lock(sb);
set_branchperms(sb, bindex, perms);
set_branch_count(sb, bindex, 0);
new_branch_id(sb, bindex);
- unionfs_write_unlock(sb);
if (lower_root_info->bstart < 0)
lower_root_info->bstart = bindex;
return ret;
}
+/*
+ * There is no need to lock the unionfs_super_info's rwsem as there is no
+ * way anyone can have a reference to the superblock at this point in time.
+ */
static int unionfs_read_super(struct super_block *sb, void *raw_data,
int silent)
{
BUG_ON(bstart != 0);
sbend(sb) = bend = lower_root_info->bend;
for (bindex = bstart; bindex <= bend; bindex++) {
- struct dentry *d;
-
- d = lower_root_info->lower_paths[bindex].dentry;
-
- unionfs_write_lock(sb);
+ struct dentry *d = lower_root_info->lower_paths[bindex].dentry;
unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
- unionfs_write_unlock(sb);
}
/* max Bytes is the maximum bytes from highest priority branch */
- unionfs_read_lock(sb);
sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;
- unionfs_read_unlock(sb);
sb->s_op = &unionfs_sops;
int err = 0;
struct dentry *wh_dentry;
+ unionfs_read_lock(old_dentry->d_sb);
unionfs_double_lock_dentry(old_dentry, new_dentry);
if (!__unionfs_d_revalidate_chain(old_dentry, NULL, 0)) {
unionfs_unlock_dentry(new_dentry);
unionfs_unlock_dentry(old_dentry);
+ unionfs_read_unlock(old_dentry->d_sb);
return err;
}
* else that's needed, and the other is fine. This way we truncate the inode
* size (and its pages) and then clear our own inode, which will do an iput
* on our and the lower inode.
+ *
+ * No need to lock sb info's rwsem.
*/
static void unionfs_delete_inode(struct inode *inode)
{
clear_inode(inode);
}
-/* final actions when unmounting a file system */
+/*
+ * final actions when unmounting a file system
+ *
+ * No need to lock rwsem.
+ */
static void unionfs_put_super(struct super_block *sb)
{
int bindex, bstart, bend;
struct super_block *sb;
struct dentry *lower_dentry;
+ sb = dentry->d_sb;
+
+ unionfs_read_lock(sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
}
unionfs_check_dentry(dentry);
- sb = dentry->d_sb;
-
- unionfs_read_lock(sb);
lower_dentry = unionfs_lower_dentry(sb->s_root);
- unionfs_read_unlock(sb);
err = vfs_statfs(lower_dentry, buf);
/* set return buf to our f/s to avoid confusing user-level utils */
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(sb);
return err;
}
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
* on the inode free list.
+ *
+ * No need to lock sb info's rwsem.
*/
static void unionfs_clear_inode(struct inode *inode)
{
/*
* Called when we have a dirty inode, right here we only throw out
* parts of our readdir list that are too old.
+ *
+ * No need to grab sb info's rwsem.
*/
static int unionfs_write_inode(struct inode *inode, int sync)
{
sb = mnt->mnt_sb;
+ unionfs_read_lock(sb);
+
bstart = sbstart(sb);
bend = sbend(sb);
for (bindex = bstart; bindex <= bend; bindex++) {
lower_mnt = unionfs_lower_mnt_idx(sb->s_root, bindex);
- unionfs_read_lock(sb);
lower_sb = unionfs_lower_super_idx(sb, bindex);
- unionfs_read_unlock(sb);
if (lower_mnt && lower_sb && lower_sb->s_op &&
lower_sb->s_op->umount_begin)
lower_sb->s_op->umount_begin(lower_mnt, flags);
}
+
+ unionfs_read_unlock(sb);
}
static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt)
int bindex, bstart, bend;
int perms;
+ unionfs_read_lock(sb);
+
unionfs_lock_dentry(sb->s_root);
tmp_page = (char*) __get_free_page(GFP_KERNEL);
goto out;
}
- unionfs_read_lock(sb);
perms = branchperms(sb, bindex);
- unionfs_read_unlock(sb);
seq_printf(m, "%s=%s", path,
perms & MAY_WRITE ? "rw" : "ro");
unionfs_unlock_dentry(sb->s_root);
+ unionfs_read_unlock(sb);
+
return ret;
}
int bend;
atomic_t generation;
- struct rw_semaphore rwsem; /* protects access to data+id fields */
+
+ /*
+ * This rwsem is used to make sure that a branch management
+ * operation...
+ * 1) will not begin before all currently in-flight operations
+ * complete
+ * 2) any new operations do not execute until the currently
+ * running branch management operation completes
+ */
+ struct rw_semaphore rwsem;
int high_branch_id; /* last unique branch ID given */
struct unionfs_data *data;
};
{
int ret;
- unionfs_read_lock(sb);
ret = (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0;
- unionfs_read_unlock(sb);
return ret;
}
BUG_ON(index < 0);
- unionfs_read_lock(dentry->d_sb);
if ((!(branchperms(dentry->d_sb, index) & MAY_WRITE)) ||
IS_RDONLY(unionfs_lower_dentry_idx(dentry, index)->d_inode))
err = -EROFS;
- unionfs_read_unlock(dentry->d_sb);
return err;
}
{
int err = 0;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
unionfs_check_inode(dir);
}
unionfs_unlock_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
int err = 0;
struct unionfs_dir_state *namelist = NULL;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
free_rdstate(namelist);
unionfs_unlock_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
int err = -EOPNOTSUPP;
char *encoded_list = NULL;
+ unionfs_read_lock(dentry->d_sb);
unionfs_lock_dentry(dentry);
if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}