int au_reopen_nondir(struct file *file) { int err; struct dentry *dentry; aufs_bindex_t bstart, bindex, bend; struct file *hidden_file, *h_file_tmp; dentry = file->f_dentry; LKTRTrace("%.*s\n", DLNPair(dentry)); DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode) || !au_h_dptr(dentry)->d_inode); bstart = dbstart(dentry); h_file_tmp = NULL; if (fbstart(file) == bstart) { hidden_file = au_h_fptr(file); if (file->f_mode == hidden_file->f_mode) return 0; /* success */ h_file_tmp = hidden_file; get_file(h_file_tmp); set_h_fptr(file, bstart, NULL); } DEBUG_ON(fbstart(file) < bstart || ftofi(file)->fi_hfile[0 + bstart].hf_file); hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC); //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart)); //hidden_file = ERR_PTR(-1);} err = PTR_ERR(hidden_file); if (IS_ERR(hidden_file)) goto out; // close all? err = 0; //cpup_file_flags(hidden_file, file); set_fbstart(file, bstart); set_h_fptr(file, bstart, hidden_file); memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //?? /* close lower files */ bend = fbend(file); for (bindex = bstart + 1; bindex <= bend; bindex++) set_h_fptr(file, bindex, NULL); set_fbend(file, bstart); out: if (h_file_tmp) fput(h_file_tmp); TraceErr(err); return err; }
/* perform a delayed copyup of a read-write file on a read-only branch */ static int do_delayed_copyup(struct file *file, struct dentry *parent) { int bindex, bstart, bend, err = 0; struct dentry *dentry = file->f_path.dentry; struct inode *parent_inode = parent->d_inode; bstart = fbstart(file); bend = fbend(file); BUG_ON(!S_ISREG(dentry->d_inode->i_mode)); unionfs_check_file(file); for (bindex = bstart - 1; bindex >= 0; bindex--) { if (!d_deleted(dentry)) err = copyup_file(parent_inode, file, bstart, bindex, i_size_read(dentry->d_inode)); else err = copyup_deleted_file(file, dentry, parent, bstart, bindex); /* if succeeded, set lower open-file flags and break */ if (!err) { struct file *lower_file; lower_file = unionfs_lower_file_idx(file, bindex); lower_file->f_flags = file->f_flags; break; } } if (err || (bstart <= fbstart(file))) goto out; bend = fbend(file); for (bindex = bstart; bindex <= bend; bindex++) { if (unionfs_lower_file_idx(file, bindex)) { branchput(dentry->d_sb, bindex); fput(unionfs_lower_file_idx(file, bindex)); unionfs_set_lower_file_idx(file, bindex, NULL); } } path_put_lowers(dentry, bstart, bend, false); iput_lowers(dentry->d_inode, bstart, bend, false); /* for reg file, we only open it "once" */ fbend(file) = fbstart(file); dbend(dentry) = dbstart(dentry); ibend(dentry->d_inode) = ibstart(dentry->d_inode); out: unionfs_check_file(file); return err; }
/* unionfs_open helper function: open a directory */ static int __open_dir(struct inode *inode, struct file *file) { struct dentry *lower_dentry; struct file *lower_file; int bindex, bstart, bend; struct vfsmount *mnt; bstart = fbstart(file) = dbstart(file->f_path.dentry); bend = fbend(file) = dbend(file->f_path.dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(file->f_path.dentry, bindex); if (!lower_dentry) continue; dget(lower_dentry); unionfs_mntget(file->f_path.dentry, bindex); mnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex); lower_file = dentry_open(lower_dentry, mnt, file->f_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); unionfs_set_lower_file_idx(file, bindex, lower_file); /* * The branchget goes after the open, because otherwise * we would miss the reference on release. */ branchget(inode->i_sb, bindex); } return 0; }
int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync) { int bindex, bstart, bend; struct file *lower_file; struct dentry *lower_dentry; struct dentry *parent; struct inode *lower_inode, *inode; int err = -EINVAL; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); err = unionfs_file_revalidate(file, parent, true); if (unlikely(err)) goto out; unionfs_check_file(file); bstart = fbstart(file); bend = fbend(file); if (bstart < 0 || bend < 0) goto out; inode = dentry->d_inode; if (unlikely(!inode)) { printk(KERN_ERR "unionfs: null lower inode in unionfs_fsync\n"); goto out; } for (bindex = bstart; bindex <= bend; bindex++) { lower_inode = unionfs_lower_inode_idx(inode, bindex); if (!lower_inode || !lower_inode->i_fop->fsync) continue; lower_file = unionfs_lower_file_idx(file, bindex); lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); mutex_lock(&lower_inode->i_mutex); err = lower_inode->i_fop->fsync(lower_file, lower_dentry, datasync); if (!err && bindex == bstart) fsstack_copy_attr_times(inode, lower_inode); mutex_unlock(&lower_inode->i_mutex); if (err) goto out; } out: if (!err) unionfs_check_file(file); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
/* * put all references held by upper struct file and free lower file pointer * array */ static void cleanup_file(struct file *file) { int bindex, bstart, bend; struct file **lower_files; struct file *lower_file; struct super_block *sb = file->f_path.dentry->d_sb; lower_files = UNIONFS_F(file)->lower_files; bstart = fbstart(file); bend = fbend(file); for (bindex = bstart; bindex <= bend; bindex++) { int i; /* holds (possibly) updated branch index */ int old_bid; lower_file = unionfs_lower_file_idx(file, bindex); if (!lower_file) continue; /* * Find new index of matching branch with an open * file, since branches could have been added or * deleted causing the one with open files to shift. */ old_bid = UNIONFS_F(file)->saved_branch_ids[bindex]; i = branch_id_to_idx(sb, old_bid); if (unlikely(i < 0)) { printk(KERN_ERR "unionfs: no superblock for " "file %p\n", file); continue; } /* decrement count of open files */ branchput(sb, i); /* * fput will perform an mntput for us on the correct branch. * Although we're using the file's old branch configuration, * bindex, which is the old index, correctly points to the * right branch in the file's branch list. In other words, * we're going to mntput the correct branch even if branches * have been added/removed. */ fput(lower_file); UNIONFS_F(file)->lower_files[bindex] = NULL; UNIONFS_F(file)->saved_branch_ids[bindex] = -1; } UNIONFS_F(file)->lower_files = NULL; kfree(lower_files); kfree(UNIONFS_F(file)->saved_branch_ids); /* set to NULL because caller needs to know if to kfree on error */ UNIONFS_F(file)->saved_branch_ids = NULL; }
/* this unionfs_write() does not modify data pages! */ ssize_t unionfs_write(struct file * file, const char *buf, size_t count, loff_t * ppos) { int err = -EINVAL; struct file *hidden_file = NULL; struct inode *inode; struct inode *hidden_inode; loff_t pos = *ppos; int bstart, bend; print_entry_location(); if ((err = unionfs_file_revalidate(file, 1))) goto out; inode = file->f_dentry->d_inode; bstart = fbstart(file); bend = fbend(file); ASSERT(bstart != -1); PASSERT(ftopd(file)); PASSERT(ftohf(file)); hidden_file = ftohf(file); hidden_inode = hidden_file->f_dentry->d_inode; if (!hidden_file->f_op || !hidden_file->f_op->write) goto out; /* adjust for append -- seek to the end of the file */ if (file->f_flags & O_APPEND) pos = inode->i_size; err = hidden_file->f_op->write(hidden_file, buf, count, &pos); /* * copy ctime and mtime from lower layer attributes * atime is unchanged for both layers */ if (err >= 0) fist_copy_attr_times(inode, hidden_inode); *ppos = pos; /* update this inode's size */ if (pos > inode->i_size) inode->i_size = pos; out: print_exit_status(err); return err; }
/* * This function creates a copy of a file represented by 'file' which * currently resides in branch 'bstart' to branch 'new_bindex.' The copy * will be named "name". */ int copyup_named_file(struct inode *dir, struct file *file, char *name, int bstart, int new_bindex, loff_t len) { int err = 0; struct file *output_file = NULL; err = copyup_dentry(dir, file->f_path.dentry, bstart, new_bindex, name, strlen(name), &output_file, len); if (!err) { fbstart(file) = new_bindex; unionfs_set_lower_file_idx(file, new_bindex, output_file); } return err; }
/* * This function creates a copy of a file represented by 'file' which * currently resides in branch 'bstart' to branch 'new_bindex'. */ int copyup_file(struct inode *dir, struct file *file, int bstart, int new_bindex, loff_t len) { int err = 0; struct file *output_file = NULL; struct dentry *dentry = file->f_path.dentry; err = copyup_dentry(dir, dentry, bstart, new_bindex, dentry->d_name.name, dentry->d_name.len, &output_file, len); if (!err) { fbstart(file) = new_bindex; unionfs_set_lower_file_idx(file, new_bindex, output_file); } return err; }
/* This function creates a copy of a file represented by 'file' which currently * resides in branch 'bstart' to branch 'new_bindex. */ int copyup_file(struct inode *dir, struct file *file, int bstart, int new_bindex, int len) { int err = 0; struct file *output_file = NULL; print_entry_location(); err = copyup_dentry(dir, file->f_dentry, bstart, new_bindex, &output_file, len); if (!err) { fbstart(file) = new_bindex; set_ftohf_index(file, new_bindex, output_file); } print_exit_status(err); return err; }
int unionfs_flush(struct file *file, fl_owner_t id) { int err = 0; struct file *lower_file = NULL; struct dentry *dentry = file->f_path.dentry; struct dentry *parent; int bindex, bstart, bend; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); err = unionfs_file_revalidate(file, parent, UNIONFS_F(file)->wrote_to_file); if (unlikely(err)) goto out; unionfs_check_file(file); bstart = fbstart(file); bend = fbend(file); for (bindex = bstart; bindex <= bend; bindex++) { lower_file = unionfs_lower_file_idx(file, bindex); if (lower_file && lower_file->f_op && lower_file->f_op->flush) { err = lower_file->f_op->flush(lower_file, id); if (err) goto out; } } out: if (!err) unionfs_check_file(file); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
int unionfs_open(struct inode *inode, struct file *file) { int err = 0; struct file *lower_file = NULL; struct dentry *dentry = file->f_path.dentry; struct dentry *parent; int bindex = 0, bstart = 0, bend = 0; int size; int valid = 0; unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); /* don't open unhashed/deleted files */ if (d_deleted(dentry)) { err = -ENOENT; goto out_nofree; } /* XXX: should I change 'false' below to the 'willwrite' flag? */ valid = __unionfs_d_revalidate(dentry, parent, false); if (unlikely(!valid)) { err = -ESTALE; goto out_nofree; } file->private_data = kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL); if (unlikely(!UNIONFS_F(file))) { err = -ENOMEM; goto out_nofree; } fbstart(file) = -1; fbend(file) = -1; atomic_set(&UNIONFS_F(file)->generation, atomic_read(&UNIONFS_I(inode)->generation)); size = sizeof(struct file *) * sbmax(inode->i_sb); UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); if (unlikely(!UNIONFS_F(file)->lower_files)) { err = -ENOMEM; goto out; } size = sizeof(int) * sbmax(inode->i_sb); UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { err = -ENOMEM; goto out; } bstart = fbstart(file) = dbstart(dentry); bend = fbend(file) = dbend(dentry); /* * open all directories and make the unionfs file struct point to * these lower file structs */ if (S_ISDIR(inode->i_mode)) err = __open_dir(inode, file); /* open a dir */ else err = __open_file(inode, file, parent); /* open a file */ /* freeing the allocated resources, and fput the opened files */ if (err) { for (bindex = bstart; bindex <= bend; bindex++) { lower_file = unionfs_lower_file_idx(file, bindex); if (!lower_file) continue; branchput(dentry->d_sb, bindex); /* fput calls dput for lower_dentry */ fput(lower_file); } } out: if (err) { kfree(UNIONFS_F(file)->lower_files); kfree(UNIONFS_F(file)->saved_branch_ids); kfree(UNIONFS_F(file)); } out_nofree: if (!err) { unionfs_postcopyup_setmnt(dentry); unionfs_copy_attr_times(inode); unionfs_check_file(file); unionfs_check_inode(inode); } unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(inode->i_sb); return err; }
void __unionfs_check_file(const struct file *file, const char *fname, const char *fxn, int line) { int bindex; int dstart, dend, fstart, fend; struct dentry *dentry; struct file *lower_file; struct inode *inode; struct super_block *sb; int printed_caller = 0; BUG_ON(!file); dentry = file->f_path.dentry; sb = dentry->d_sb; dstart = dbstart(dentry); dend = dbend(dentry); BUG_ON(dstart > dend); fstart = fbstart(file); fend = fbend(file); BUG_ON(fstart > fend); if (unlikely((fstart == -1 && fend != -1) || (fstart != -1 && fend == -1))) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF0: file/dentry=%p:%p fstart/end=%d:%d\n", file, dentry, fstart, fend); } if (unlikely(fstart != dstart)) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF1: file/dentry=%p:%p fstart=%d dstart=%d\n", file, dentry, fstart, dstart); } if (unlikely(fend != dend)) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF2: file/dentry=%p:%p fend=%d dend=%d\n", file, dentry, fend, dend); } inode = dentry->d_inode; if (!S_ISDIR(inode->i_mode)) { if (unlikely(fend != fstart)) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF3: file/inode=%p:%p fstart=%d fend=%d\n", file, inode, fstart, fend); } if (unlikely(dend != dstart)) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF4: file/dentry=%p:%p dstart=%d dend=%d\n", file, dentry, dstart, dend); } } /* * check for NULL dentries inside the start/end range, or * non-NULL dentries outside the start/end range. */ for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { lower_file = unionfs_lower_file_idx(file, bindex); if (lower_file) { if (unlikely(bindex < fstart || bindex > fend)) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF5: file/lower=%p:%p bindex=%d " "fstart/end=%d:%d\n", file, lower_file, bindex, fstart, fend); } } else { /* lower_file == NULL */ if (bindex >= fstart && bindex <= fend) { /* * directories can have NULL lower inodes in * b/t start/end, but NOT if at the * start/end range. */ if (unlikely(!(S_ISDIR(inode->i_mode) && bindex > fstart && bindex < fend))) { PRINT_CALLER(fname, fxn, line); pr_debug(" CF6: file/lower=%p:%p " "bindex=%d fstart/end=%d:%d\n", file, lower_file, bindex, fstart, fend); } } } } __unionfs_check_dentry(dentry, fname, fxn, line); }
static int unionfs_mmap(struct file *file, struct vm_area_struct *vma) { int err = 0; bool willwrite; struct file *lower_file; struct dentry *dentry = file->f_path.dentry; struct dentry *parent; struct vm_operations_struct *saved_vm_ops = NULL; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); /* This might be deferred to mmap's writepage */ willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); err = unionfs_file_revalidate(file, parent, willwrite); if (unlikely(err)) goto out; unionfs_check_file(file); /* * File systems which do not implement ->writepage may use * generic_file_readonly_mmap as their ->mmap op. If you call * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. * But we cannot call the lower ->mmap op, so we can't tell that * writeable mappings won't work. Therefore, our only choice is to * check if the lower file system supports the ->writepage, and if * not, return EINVAL (the same error that * generic_file_readonly_mmap returns in that case). */ lower_file = unionfs_lower_file(file); if (willwrite && !lower_file->f_mapping->a_ops->writepage) { err = -EINVAL; printk(KERN_ERR "unionfs: branch %d file system does not " "support writeable mmap\n", fbstart(file)); goto out; } /* * find and save lower vm_ops. * * XXX: the VFS should have a cleaner way of finding the lower vm_ops */ if (!UNIONFS_F(file)->lower_vm_ops) { err = lower_file->f_op->mmap(lower_file, vma); if (err) { printk(KERN_ERR "unionfs: lower mmap failed %d\n", err); goto out; } saved_vm_ops = vma->vm_ops; err = do_munmap(current->mm, vma->vm_start, vma->vm_end - vma->vm_start); if (err) { printk(KERN_ERR "unionfs: do_munmap failed %d\n", err); goto out; } } file->f_mapping->a_ops = &unionfs_dummy_aops; err = generic_file_mmap(file, vma); file->f_mapping->a_ops = &unionfs_aops; if (err) { printk(KERN_ERR "unionfs: generic_file_mmap failed %d\n", err); goto out; } vma->vm_ops = &unionfs_vm_ops; if (!UNIONFS_F(file)->lower_vm_ops) UNIONFS_F(file)->lower_vm_ops = saved_vm_ops; out: if (!err) { /* copyup could cause parent dir times to change */ unionfs_copy_attr_times(parent->d_inode); unionfs_check_file(file); } unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
/* * Helper function for unionfs_file_revalidate/locked. * Expects dentry/parent to be locked already, and revalidated. */ static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry, struct dentry *parent, struct super_block *sb, int sbgen, int dgen, bool willwrite) { int fgen; int bstart, bend, orig_brid; int size; int err = 0; fgen = atomic_read(&UNIONFS_F(file)->generation); /* * There are two cases we are interested in. The first is if the * generation is lower than the super-block. The second is if * someone has copied up this file from underneath us, we also need * to refresh things. */ if (d_deleted(dentry) || (sbgen <= fgen && dbstart(dentry) == fbstart(file) && unionfs_lower_file(file))) goto out_may_copyup; /* save orig branch ID */ orig_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; /* First we throw out the existing files. */ cleanup_file(file); /* Now we reopen the file(s) as in unionfs_open. */ bstart = fbstart(file) = dbstart(dentry); bend = fbend(file) = dbend(dentry); size = sizeof(struct file *) * sbmax(sb); UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); if (unlikely(!UNIONFS_F(file)->lower_files)) { err = -ENOMEM; goto out; } size = sizeof(int) * sbmax(sb); UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { err = -ENOMEM; goto out; } if (S_ISDIR(dentry->d_inode->i_mode)) { /* We need to open all the files. */ err = open_all_files(file); if (err) goto out; } else { int new_brid; /* We only open the highest priority branch. */ err = open_highest_file(file, willwrite); if (err) goto out; new_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; if (unlikely(new_brid != orig_brid && sbgen > fgen)) { /* * If we re-opened the file on a different branch * than the original one, and this was due to a new * branch inserted, then update the mnt counts of * the old and new branches accordingly. */ unionfs_mntget(dentry, bstart); unionfs_mntput(sb->s_root, branch_id_to_idx(sb, orig_brid)); } /* regular files have only one open lower file */ fbend(file) = fbstart(file); } atomic_set(&UNIONFS_F(file)->generation, atomic_read(&UNIONFS_I(dentry->d_inode)->generation)); out_may_copyup: /* Copyup on the first write to a file on a readonly branch. */ if (willwrite && IS_WRITE_FLAG(file->f_flags) && !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) && is_robranch(dentry)) { pr_debug("unionfs: do delay copyup of \"%s\"\n", dentry->d_name.name); err = do_delayed_copyup(file, parent); /* regular files have only one open lower file */ if (!err && !S_ISDIR(dentry->d_inode->i_mode)) fbend(file) = fbstart(file); } out: if (err) { kfree(UNIONFS_F(file)->lower_files); kfree(UNIONFS_F(file)->saved_branch_ids); } return err; }
/* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len) { int err; struct dentry *dentry, *parent, *hidden_dentry, *hidden_parent; struct inode *hidden_inode, *hidden_dir, *inode, *dir; struct super_block *sb; aufs_bindex_t bstart, bcpup; dentry = file->f_dentry; LKTRTrace("%.*s, len %Ld\n", DLNPair(dentry), len); FiMustWriteLock(file); sb = dentry->d_sb; bstart = fbstart(file); DEBUG_ON(ftobr(file, bstart) != stobr(sb, bstart)); inode = dentry->d_inode; ii_read_lock_child(inode); LKTRTrace("rdonly %d, bstart %d\n", test_ro(sb, bstart, inode), bstart); err = test_ro(sb, bstart, inode); ii_read_unlock(inode); if (!err && (au_h_fptr(file)->f_mode & FMODE_WRITE)) return 0; /* need to cpup */ parent = dentry->d_parent; // dget_parent() di_write_lock_child(dentry); di_write_lock_parent(parent); bcpup = err = find_rw_parent_br(dentry, bstart); //bcpup = err = find_rw_br(sb, bstart); if (unlikely(err < 0)) goto out_unlock; err = 0; hidden_parent = au_h_dptr_i(parent, bcpup); if (!hidden_parent) { err = cpup_dirs(dentry, bcpup, NULL); //if (LktrCond) err = -1; if (unlikely(err)) goto out_unlock; hidden_parent = au_h_dptr_i(parent, bcpup); } hidden_dir = hidden_parent->d_inode; hidden_dentry = au_h_fptr(file)->f_dentry; hidden_inode = hidden_dentry->d_inode; dir = parent->d_inode; hdir_lock(hidden_dir, dir, bcpup); hi_lock_child(hidden_inode); if (d_unhashed(dentry) || d_unhashed(hidden_dentry) /* || !hidden_inode->i_nlink */) { if (!au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE, need_dlgt(sb))) err = cpup_wh_file(file, bcpup, len); else { struct cpup_wh_file_args args = { .errp = &err, .file = file, .bdst = bcpup, .len = len }; au_wkq_wait(call_cpup_wh_file, &args, /*dlgt*/0); } //if (LktrCond) err = -1; TraceErr(err); } else { if (!au_h_dptr_i(dentry, bcpup))
/* unionfs_open helper function: open a file */ static int __open_file(struct inode *inode, struct file *file, struct dentry *parent) { struct dentry *lower_dentry; struct file *lower_file; int lower_flags; int bindex, bstart, bend; lower_dentry = unionfs_lower_dentry(file->f_path.dentry); lower_flags = file->f_flags; bstart = fbstart(file) = dbstart(file->f_path.dentry); bend = fbend(file) = dbend(file->f_path.dentry); /* * check for the permission for lower file. If the error is * COPYUP_ERR, copyup the file. */ if (lower_dentry->d_inode && is_robranch(file->f_path.dentry)) { /* * if the open will change the file, copy it up otherwise * defer it. */ if (lower_flags & O_TRUNC) { int size = 0; int err = -EROFS; /* copyup the file */ for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_file(parent->d_inode, file, bstart, bindex, size); if (!err) { /* only one regular file open */ fbend(file) = fbstart(file); break; } } return err; } else { /* * turn off writeable flags, to force delayed copyup * by caller. */ lower_flags &= ~(OPEN_WRITE_FLAGS); } } dget(lower_dentry); /* * dentry_open will decrement mnt refcnt if err. * otherwise fput() will do an mntput() for us upon file close. */ unionfs_mntget(file->f_path.dentry, bstart); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(file->f_path.dentry, bstart), lower_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); unionfs_set_lower_file(file, lower_file); branchget(inode->i_sb, bstart); return 0; }
/* * 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; struct unionfs_file_info *fileinfo; struct unionfs_inode_info *inodeinfo; struct super_block *sb = inode->i_sb; struct dentry *dentry = file->f_path.dentry; struct dentry *parent; int bindex, bstart, bend; int fgen, err = 0; /* * Since mm/memory.c:might_fault() (under PROVE_LOCKING) was * modified in 2.6.29-rc1 to call might_lock_read on mmap_sem, this * has been causing false positives in file system stacking layers. * In particular, our ->mmap is called after sys_mmap2 already holds * mmap_sem, then we lock our own mutexes; but earlier, it's * possible for lockdep to have locked our mutexes first, and then * we call a lower ->readdir which could call might_fault. The * different ordering of the locks is what lockdep complains about * -- unnecessarily. Therefore, we have no choice but to tell * lockdep to temporarily turn off lockdep here. Note: the comments * inside might_sleep also suggest that it would have been * nicer to only annotate paths that needs that might_lock_read. */ lockdep_off(); unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); /* * We try to revalidate, but the VFS ignores return return values * from file->release, so we must always try to succeed here, * including to do the kfree and dput below. So if revalidation * failed, all we can do is print some message and keep going. */ err = unionfs_file_revalidate(file, parent, UNIONFS_F(file)->wrote_to_file); if (!err) unionfs_check_file(file); fileinfo = UNIONFS_F(file); BUG_ON(file->f_path.dentry->d_inode != inode); inodeinfo = UNIONFS_I(inode); /* fput all the lower files */ fgen = atomic_read(&fileinfo->generation); bstart = fbstart(file); bend = fbend(file); for (bindex = bstart; bindex <= bend; bindex++) { lower_file = unionfs_lower_file_idx(file, bindex); if (lower_file) { unionfs_set_lower_file_idx(file, bindex, NULL); fput(lower_file); branchput(sb, bindex); } /* if there are no more refs to the dentry, dput it */ if (d_deleted(dentry)) { dput(unionfs_lower_dentry_idx(dentry, bindex)); unionfs_set_lower_dentry_idx(dentry, bindex, NULL); } } kfree(fileinfo->lower_files); kfree(fileinfo->saved_branch_ids); if (fileinfo->rdstate) { fileinfo->rdstate->access = jiffies; spin_lock(&inodeinfo->rdlock); inodeinfo->rdcount++; list_add_tail(&fileinfo->rdstate->cache, &inodeinfo->readdircache); mark_inode_dirty(inode); spin_unlock(&inodeinfo->rdlock); fileinfo->rdstate = NULL; } kfree(fileinfo); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(sb); lockdep_on(); return err; }