/* * create the new device/file/directory - use copyup_permission to copyup * times, and mode * * if the object being copied up is a regular file, the file is only created, * the contents have to be copied up separately */ static int __copyup_ndentry(struct dentry *old_lower_dentry, struct dentry *new_lower_dentry, struct dentry *new_lower_parent_dentry, char *symbuf) { int err = 0; umode_t old_mode = old_lower_dentry->d_inode->i_mode; struct sioq_args args; if (S_ISDIR(old_mode)) { args.mkdir.parent = new_lower_parent_dentry->d_inode; args.mkdir.dentry = new_lower_dentry; args.mkdir.mode = old_mode; run_sioq(__unionfs_mkdir, &args); err = args.err; } else if (S_ISLNK(old_mode)) { args.symlink.parent = new_lower_parent_dentry->d_inode; args.symlink.dentry = new_lower_dentry; args.symlink.symbuf = symbuf; run_sioq(__unionfs_symlink, &args); err = args.err; } else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) || S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) { args.mknod.parent = new_lower_parent_dentry->d_inode; args.mknod.dentry = new_lower_dentry; args.mknod.mode = old_mode; args.mknod.dev = old_lower_dentry->d_inode->i_rdev; run_sioq(__unionfs_mknod, &args); err = args.err; } else if (S_ISREG(old_mode)) { struct nameidata nd; err = init_lower_nd(&nd, LOOKUP_CREATE); if (unlikely(err < 0)) goto out; args.create.nd = &nd; args.create.parent = new_lower_parent_dentry->d_inode; args.create.dentry = new_lower_dentry; args.create.mode = old_mode; run_sioq(__unionfs_create, &args); err = args.err; release_lower_nd(&nd, err); } else { printk(KERN_CRIT "unionfs: unknown inode type %d\n", old_mode); BUG(); } out: return err; }
static int unionfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd_unused) { int err = 0; struct dentry *lower_dentry = NULL; struct dentry *lower_parent_dentry = NULL; struct dentry *parent; int valid = 0; struct nameidata lower_nd; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false); if (unlikely(!valid)) { err = -ESTALE; /* same as what real_lookup does */ goto out; } lower_dentry = find_writeable_branch(dir, dentry); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out; } lower_parent_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_parent_dentry)) { err = PTR_ERR(lower_parent_dentry); goto out_unlock; } err = init_lower_nd(&lower_nd, LOOKUP_CREATE); if (unlikely(err < 0)) goto out_unlock; err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, &lower_nd); release_lower_nd(&lower_nd, err); if (!err) { err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); if (!err) { unionfs_copy_attr_times(dir); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update no. of links on parent directory */ dir->i_nlink = unionfs_get_nlinks(dir); } } out_unlock: unlock_dir(lower_parent_dentry); out: if (!err) { unionfs_postcopyup_setmnt(dentry); unionfs_check_inode(dir); unionfs_check_dentry(dentry); } unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }