static int __open_file(struct inode *inode, struct file *file, struct dentry *parent) { struct dentry *lower_dentry; struct file *lower_file; struct vfsmount *lower_mnt; struct dentry *dentry = file->f_path.dentry; int lower_flags; int i = 0, idx = 0; for (i = 0; i <= 1; i++) { lower_dentry = wrapfs_lower_dentry_idx(dentry, i); if (!lower_dentry || !lower_dentry->d_inode) continue; lower_flags = file->f_flags; if (lower_dentry->d_inode && (i == 1)) { UDBG; if (lower_flags & O_TRUNC) { int size = 0; int err = -EROFS; UDBG; err = copyup_file(parent->d_inode, file, i, 0, size); if (!err) break; return err; } else lower_flags &= ~(OPEN_WRITE_FLAGS); } dget(lower_dentry); lower_mnt = mntget(wrapfs_lower_mnt_idx(dentry, i)); if (!lower_mnt) lower_mnt = mntget(wrapfs_lower_mnt_idx(parent, i)); lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); wrapfs_set_lower_file(file, lower_file); branchget(inode->i_sb, i); idx = i; goto out; } out: if (!wrapfs_lower_inode_idx(inode, idx)) fsstack_copy_attr_all(inode, wrapfs_lower_inode_idx(inode, idx)); return 0; }
/* 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; }
/* open the highest priority file for a given upper file */ static int open_highest_file(struct file *file, bool willwrite) { int bindex, bstart, bend, err = 0; struct file *lower_file; struct dentry *lower_dentry; struct dentry *dentry = file->f_path.dentry; struct dentry *parent = dget_parent(dentry); struct inode *parent_inode = parent->d_inode; struct super_block *sb = dentry->d_sb; bstart = dbstart(dentry); bend = dbend(dentry); lower_dentry = unionfs_lower_dentry(dentry); if (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) { for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_file(parent_inode, file, bstart, bindex, i_size_read(dentry->d_inode)); if (!err) break; } atomic_set(&UNIONFS_F(file)->generation, atomic_read(&UNIONFS_I(dentry->d_inode)-> generation)); goto out; } dget(lower_dentry); unionfs_mntget(dentry, bstart); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(dentry, bstart), file->f_flags, current_cred()); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); goto out; } branchget(sb, bstart); unionfs_set_lower_file(file, lower_file); /* Fix up the position. */ lower_file->f_pos = file->f_pos; memcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state)); out: dput(parent); return err; }
/* 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; }