/* * Copy up a dentry to a file of specified name. * * @dir: used to pull the ->i_sb to access other branches * @dentry: the non-negative dentry whose lower_inode we should copy * @bstart: the branch of the lower_inode to copy from * @new_bindex: the branch to create the new file in * @name: the name of the file to create * @namelen: length of @name * @copyup_file: the "struct file" to return (optional) * @len: how many bytes to copy-up? */ int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, const char *name, int namelen, struct file **copyup_file, loff_t len) { struct dentry *new_lower_dentry; struct dentry *old_lower_dentry = NULL; struct super_block *sb; int err = 0; int old_bindex; int old_bstart; int old_bend; struct dentry *new_lower_parent_dentry = NULL; mm_segment_t oldfs; char *symbuf = NULL; verify_locked(dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); BUG_ON(new_bindex < 0); BUG_ON(new_bindex >= old_bindex); sb = dir->i_sb; err = is_robranch_super(sb, new_bindex); if (err) goto out; /* Create the directory structure above this dentry. */ new_lower_dentry = create_parents(dir, dentry, name, new_bindex); if (IS_ERR(new_lower_dentry)) { err = PTR_ERR(new_lower_dentry); goto out; } old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex); /* we conditionally dput this old_lower_dentry at end of function */ dget(old_lower_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_lower_dentry->d_inode->i_mode)) { symbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (unlikely(!symbuf)) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); err = -ENOMEM; goto out_free; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_lower_dentry->d_inode->i_op->readlink( old_lower_dentry, (char __user *)symbuf, PATH_MAX); set_fs(oldfs); if (err < 0) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); goto out_free; } symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_lower_parent_dentry = lock_parent(new_lower_dentry); /* create the new inode */ err = __copyup_ndentry(old_lower_dentry, new_lower_dentry, new_lower_parent_dentry, symbuf); if (err) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); goto out_unlock; } /* We actually copyup the file here. */ if (S_ISREG(old_lower_dentry->d_inode->i_mode)) err = __copyup_reg_data(dentry, new_lower_dentry, new_bindex, old_lower_dentry, old_bindex, copyup_file, len); if (err) goto out_unlink; /* Set permissions. */ err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry); if (err) goto out_unlink; #ifdef CONFIG_UNION_FS_XATTR /* Selinux uses extended attributes for permissions. */ err = copyup_xattrs(old_lower_dentry, new_lower_dentry); if (err) goto out_unlink; #endif /* CONFIG_UNION_FS_XATTR */ /* do not allow files getting deleted to be re-interposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); goto out_unlock; out_unlink: /* * copyup failed, because we possibly ran out of space or * quota, or something else happened so let's unlink; we don't * really care about the return value of vfs_unlink */ vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry); if (copyup_file) { /* need to close the file */ fput(*copyup_file); branchput(sb, new_bindex); } /* * TODO: should we reset the error to something like -EIO? * * If we don't reset, the user may get some nonsensical errors, but * on the other hand, if we reset to EIO, we guarantee that the user * will get a "confusing" error message. */ out_unlock: unlock_dir(new_lower_parent_dentry); out_free: /* * If old_lower_dentry was not a file, then we need to dput it. If * it was a file, then it was already dput indirectly by other * functions we call above which operate on regular files. */ if (old_lower_dentry && old_lower_dentry->d_inode && !S_ISREG(old_lower_dentry->d_inode->i_mode)) dput(old_lower_dentry); kfree(symbuf); if (err) { /* * if directory creation succeeded, but inode copyup failed, * then purge new dentries. */ if (dbstart(dentry) < old_bstart && ibstart(dentry->d_inode) > dbstart(dentry)) __clear(dentry, NULL, old_bstart, old_bend, unionfs_lower_dentry(dentry), dbstart(dentry)); goto out; } if (!S_ISDIR(dentry->d_inode->i_mode)) { unionfs_postcopyup_release(dentry); if (!unionfs_lower_inode(dentry->d_inode)) { /* * If we got here, then we copied up to an * unlinked-open file, whose name is .unionfsXXXXX. */ struct inode *inode = new_lower_dentry->d_inode; atomic_inc(&inode->i_count); unionfs_set_lower_inode_idx(dentry->d_inode, ibstart(dentry->d_inode), inode); } } unionfs_postcopyup_setmnt(dentry); /* sync inode times from copied-up inode to our inode */ unionfs_copy_attr_times(dentry->d_inode); unionfs_check_inode(dir); unionfs_check_dentry(dentry); out: return err; }
int copyup_named_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, char *name, int namelen, struct file **copyup_file, int len) { struct dentry *new_hidden_dentry; struct dentry *old_hidden_dentry = NULL; struct super_block *sb; struct file *input_file = NULL; struct file *output_file = NULL; ssize_t read_bytes, write_bytes; mm_segment_t old_fs; int err = 0; char *buf; int old_bindex; int got_branch_input = -1; int got_branch_output = -1; int old_bstart; int old_bend; int size = len; struct dentry *new_hidden_parent_dentry; mm_segment_t oldfs; char *symbuf = NULL; uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; print_entry_location(); verify_locked(dentry); fist_print_dentry("IN: copyup_named_dentry", dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); ASSERT(new_bindex >= 0); ASSERT(new_bindex < old_bindex); PASSERT(dir); PASSERT(dentry); sb = dir->i_sb; if ((err = is_robranch_super(sb, new_bindex))) goto out; /* Create the directory structure above this dentry. */ new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex); PASSERT(new_hidden_dentry); if (IS_ERR(new_hidden_dentry)) { err = PTR_ERR(new_hidden_dentry); goto out; } fist_print_generic_dentry("Copyup Object", new_hidden_dentry); /* Now we actually create the object. */ old_hidden_dentry = dtohd_index(dentry, old_bindex); PASSERT(old_hidden_dentry); PASSERT(old_hidden_dentry->d_inode); DGET(old_hidden_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { PASSERT(old_hidden_dentry->d_inode->i_op); PASSERT(old_hidden_dentry->d_inode->i_op->readlink); symbuf = KMALLOC(PATH_MAX, GFP_UNIONFS); if (!symbuf) { err = -ENOMEM; goto copyup_readlink_err; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_hidden_dentry->d_inode->i_op-> readlink(old_hidden_dentry, symbuf, PATH_MAX); set_fs(oldfs); if (err < 0) goto copyup_readlink_err; symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_hidden_parent_dentry = lock_parent(new_hidden_dentry); current->fsuid = new_hidden_parent_dentry->d_inode->i_uid; current->fsgid = new_hidden_parent_dentry->d_inode->i_gid; if (S_ISDIR(old_hidden_dentry->d_inode->i_mode)) { err = vfs_mkdir(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU); } else if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf); #else err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf, S_IRWXU); #endif } else if (S_ISBLK(old_hidden_dentry->d_inode->i_mode) || S_ISCHR(old_hidden_dentry->d_inode->i_mode) || S_ISFIFO(old_hidden_dentry->d_inode->i_mode) || S_ISSOCK(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, kdev_t_to_nr(old_hidden_dentry->d_inode-> i_rdev)); #else err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, old_hidden_dentry->d_inode->i_rdev); #endif } else if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU); #else err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU, NULL); #endif } else { char diemsg[100]; snprintf(diemsg, sizeof(diemsg), "Unknown inode type %d\n", old_hidden_dentry->d_inode->i_mode); FISTBUG(diemsg); } current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(new_hidden_parent_dentry); copyup_readlink_err: KFREE(symbuf); if (err) { /* get rid of the hidden dentry and all its traces */ DPUT(new_hidden_dentry); set_dtohd_index(dentry, new_bindex, NULL); set_dbstart(dentry, old_bstart); set_dbend(dentry, old_bend); goto out; } /* We actually copyup the file here. */ if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { mntget(stohiddenmnt_index(sb, old_bindex)); branchget(sb, old_bindex); got_branch_input = old_bindex; input_file = DENTRY_OPEN(old_hidden_dentry, stohiddenmnt_index(sb, old_bindex), O_RDONLY); if (IS_ERR(input_file)) { err = PTR_ERR(input_file); goto out; } if (!input_file->f_op || !input_file->f_op->read) { err = -EINVAL; goto out; } /* copy the new file */ DGET(new_hidden_dentry); mntget(stohiddenmnt_index(sb, new_bindex)); branchget(sb, new_bindex); got_branch_output = new_bindex; output_file = DENTRY_OPEN(new_hidden_dentry, stohiddenmnt_index(sb, new_bindex), O_WRONLY); if (IS_ERR(output_file)) { err = PTR_ERR(output_file); goto out; } if (!output_file->f_op || !output_file->f_op->write) { err = -EINVAL; goto out; } /* allocating a buffer */ buf = (char *)KMALLOC(PAGE_SIZE, GFP_UNIONFS); if (!buf) { err = -ENOMEM; goto out; } /* now read PAGE_SIZE bytes from offset 0 in a loop */ old_fs = get_fs(); input_file->f_pos = 0; output_file->f_pos = 0; set_fs(KERNEL_DS); do { if (len >= PAGE_SIZE) size = PAGE_SIZE; else if ((len < PAGE_SIZE) && (len > 0)) size = len; len -= PAGE_SIZE; read_bytes = input_file->f_op->read(input_file, buf, size, &input_file->f_pos); if (read_bytes <= 0) { err = read_bytes; break; } write_bytes = output_file->f_op->write(output_file, buf, read_bytes, &output_file->f_pos); if (write_bytes < 0 || (write_bytes < read_bytes)) { err = -EIO; break; } } while ((read_bytes > 0) && (len > 0)); set_fs(old_fs); KFREE(buf); } /* Set permissions. */ if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) goto out; /* Selinux uses extended attributes for permissions. */ #if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) goto out; #endif /* do not allow files getting deleted to be reinterposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); out: if (input_file && !IS_ERR(input_file)) { fput(input_file); } else { /* since input file was not opened, we need to explicitly * dput the old_hidden_dentry */ DPUT(old_hidden_dentry); } /* in any case, we have to branchput */ if (got_branch_input >= 0) branchput(sb, got_branch_input); if (output_file) { if (copyup_file && !err) { *copyup_file = output_file; } else { fput(output_file); branchput(sb, got_branch_output); } } fist_print_dentry("OUT: copyup_dentry", dentry); fist_print_inode("OUT: copyup_dentry", dentry->d_inode); print_exit_status(err); return err; }
/* copy up a dentry to a file of specified name */ static int copyup_named_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, const char *name, int namelen, struct file **copyup_file, loff_t len) { struct dentry *new_hidden_dentry; struct dentry *old_hidden_dentry = NULL; struct super_block *sb; int err = 0; int old_bindex; int old_bstart; int old_bend; struct dentry *new_hidden_parent_dentry = NULL; mm_segment_t oldfs; char *symbuf = NULL; verify_locked(dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); BUG_ON(new_bindex < 0); BUG_ON(new_bindex >= old_bindex); sb = dir->i_sb; unionfs_read_lock(sb); if ((err = is_robranch_super(sb, new_bindex))) { dput(old_hidden_dentry); goto out; } /* Create the directory structure above this dentry. */ new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex); if (IS_ERR(new_hidden_dentry)) { dput(old_hidden_dentry); err = PTR_ERR(new_hidden_dentry); goto out; } old_hidden_dentry = unionfs_lower_dentry_idx(dentry, old_bindex); dget(old_hidden_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { symbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (!symbuf) { __clear(dentry, old_hidden_dentry, old_bstart, old_bend, new_hidden_dentry, new_bindex); err = -ENOMEM; goto out_free; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_hidden_dentry->d_inode->i_op->readlink( old_hidden_dentry, (char __user *)symbuf, PATH_MAX); set_fs(oldfs); if (err) { __clear(dentry, old_hidden_dentry, old_bstart, old_bend, new_hidden_dentry, new_bindex); goto out_free; } symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_hidden_parent_dentry = lock_parent(new_hidden_dentry); /* create the new inode */ err = __copyup_ndentry(old_hidden_dentry, new_hidden_dentry, new_hidden_parent_dentry, symbuf); if (err) { __clear(dentry, old_hidden_dentry, old_bstart, old_bend, new_hidden_dentry, new_bindex); goto out_unlock; } /* We actually copyup the file here. */ if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) err = __copyup_reg_data(dentry, new_hidden_dentry, new_bindex, old_hidden_dentry, old_bindex, copyup_file, len); if (err) goto out_unlink; /* Set permissions. */ if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) goto out_unlink; #ifdef CONFIG_UNION_FS_XATTR /* Selinux uses extended attributes for permissions. */ if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) goto out_unlink; #endif /* do not allow files getting deleted to be reinterposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); goto out_unlock; /****/ out_unlink: /* copyup failed, because we possibly ran out of space or * quota, or something else happened so let's unlink; we don't * really care about the return value of vfs_unlink */ vfs_unlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry); if (copyup_file) { /* need to close the file */ fput(*copyup_file); branchput(sb, new_bindex); } /* * TODO: should we reset the error to something like -EIO? * * If we don't reset, the user may get some non-sensical errors, but * on the other hand, if we reset to EIO, we guarantee that the user * will get a "confusing" error message. */ out_unlock: unlock_dir(new_hidden_parent_dentry); out_free: kfree(symbuf); out: unionfs_read_unlock(sb); return err; }