int unionfs_ioctl_addbranch(struct inode *inode, unsigned int cmd, unsigned long arg) { int err; struct unionfs_addbranch_args *addargs = NULL; struct nameidata nd; char *path = NULL; int gen; int i; int pobjects; struct unionfs_usi_data *new_data = NULL; struct dentry **new_udi_dentry = NULL; struct inode **new_uii_inode = NULL; struct dentry *root = NULL; struct dentry *hidden_root = NULL; print_entry_location(); err = -ENOMEM; addargs = KMALLOC(sizeof(struct unionfs_addbranch_args), GFP_KERNEL); if (!addargs) goto out; err = -EFAULT; if (copy_from_user (addargs, (const void __user *)arg, sizeof(struct unionfs_addbranch_args))) goto out; err = -EINVAL; if (addargs->ab_perms & ~(MAY_READ | MAY_WRITE | MAY_NFSRO)) goto out; if (!(addargs->ab_perms & MAY_READ)) goto out; err = -E2BIG; if (sbend(inode->i_sb) > FD_SETSIZE) goto out; err = -ENOMEM; if (!(path = getname((const char __user *)addargs->ab_path))) goto out; err = path_lookup(path, LOOKUP_FOLLOW, &nd); RECORD_PATH_LOOKUP(&nd); if (err) goto out; if ((err = check_branch(&nd))) { path_release(&nd); RECORD_PATH_RELEASE(&nd); goto out; } unionfs_write_lock(inode->i_sb); lock_dentry(inode->i_sb->s_root); root = inode->i_sb->s_root; for (i = dbstart(inode->i_sb->s_root); i <= dbend(inode->i_sb->s_root); i++) { hidden_root = dtohd_index(root, i); if (is_branch_overlap(hidden_root, nd.dentry)) { err = -EINVAL; goto out; } } err = -EINVAL; if (addargs->ab_branch < 0 || (addargs->ab_branch > (sbend(inode->i_sb) + 1))) goto out; if ((err = newputmap(inode->i_sb))) goto out; stopd(inode->i_sb)->b_end++; dtopd(inode->i_sb->s_root)->udi_bcount++; set_dbend(inode->i_sb->s_root, dbend(inode->i_sb->s_root) + 1); itopd(inode->i_sb->s_root->d_inode)->b_end++; atomic_inc(&stopd(inode->i_sb)->usi_generation); gen = atomic_read(&stopd(inode->i_sb)->usi_generation); pobjects = sbend(inode->i_sb) + 1; /* Reallocate the dynamic structures. */ new_data = alloc_new_data(pobjects); new_udi_dentry = alloc_new_dentries(pobjects); new_uii_inode = KZALLOC(sizeof(struct inode *) * pobjects, GFP_KERNEL); if (!new_udi_dentry || !new_uii_inode || !new_data) { err = -ENOMEM; goto out; } /* Copy the in-place values to our new structure. */ for (i = 0; i < addargs->ab_branch; i++) { atomic_set(&(new_data[i].sbcount), branch_count(inode->i_sb, i)); new_data[i].branchperms = branchperms(inode->i_sb, i); new_data[i].hidden_mnt = stohiddenmnt_index(inode->i_sb, i); new_data[i].sb = stohs_index(inode->i_sb, i); new_udi_dentry[i] = dtohd_index(inode->i_sb->s_root, i); new_uii_inode[i] = itohi_index(inode->i_sb->s_root->d_inode, i); } /* Shift the ends to the right (only handle reallocated bits). */ for (i = sbend(inode->i_sb) - 1; i >= (int)addargs->ab_branch; i--) { int j = i + 1; int pmindex; atomic_set(&new_data[j].sbcount, branch_count(inode->i_sb, i)); new_data[j].branchperms = branchperms(inode->i_sb, i); new_data[j].hidden_mnt = stohiddenmnt_index(inode->i_sb, i); new_data[j].sb = stohs_index(inode->i_sb, i); new_udi_dentry[j] = dtohd_index(inode->i_sb->s_root, i); new_uii_inode[j] = itohi_index(inode->i_sb->s_root->d_inode, i); /* Update the newest putmap, so it is correct for later. */ pmindex = stopd(inode->i_sb)->usi_lastputmap; pmindex -= stopd(inode->i_sb)->usi_firstputmap; stopd(inode->i_sb)->usi_putmaps[pmindex]->map[i] = j; } /* Now we can free the old ones. */ KFREE(dtopd(inode->i_sb->s_root)->udi_dentry); KFREE(itopd(inode->i_sb->s_root->d_inode)->uii_inode); KFREE(stopd(inode->i_sb)->usi_data); /* Update the real pointers. */ dtohd_ptr(inode->i_sb->s_root) = new_udi_dentry; itohi_ptr(inode->i_sb->s_root->d_inode) = new_uii_inode; stopd(inode->i_sb)->usi_data = new_data; /* Re-NULL the new ones so we don't try to free them. */ new_data = NULL; new_udi_dentry = NULL; new_uii_inode = NULL; /* Put the new dentry information into it's slot. */ set_dtohd_index(inode->i_sb->s_root, addargs->ab_branch, nd.dentry); set_itohi_index(inode->i_sb->s_root->d_inode, addargs->ab_branch, IGRAB(nd.dentry->d_inode)); set_branchperms(inode->i_sb, addargs->ab_branch, addargs->ab_perms); set_branch_count(inode->i_sb, addargs->ab_branch, 0); set_stohiddenmnt_index(inode->i_sb, addargs->ab_branch, nd.mnt); set_stohs_index(inode->i_sb, addargs->ab_branch, nd.dentry->d_sb); atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, gen); atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, gen); fixputmaps(inode->i_sb); out: unlock_dentry(inode->i_sb->s_root); unionfs_write_unlock(inode->i_sb); KFREE(new_udi_dentry); KFREE(new_uii_inode); KFREE(new_data); KFREE(addargs); if (path) putname(path); print_exit_status(err); return err; }
int unionfs_ioctl_addbranch(struct inode *inode, unsigned int cmd, unsigned long arg) { int err; struct unionfs_addbranch_args *addargs = NULL; struct nameidata nd; char *path = NULL; int gen; int i; int count; int pobjects; struct vfsmount **new_hidden_mnt = NULL; struct inode **new_uii_inode = NULL; struct dentry **new_udi_dentry = NULL; struct super_block **new_usi_sb = NULL; int *new_branchperms = NULL; atomic_t *new_counts = NULL; print_entry_location(); err = -ENOMEM; addargs = KMALLOC(sizeof(struct unionfs_addbranch_args), GFP_UNIONFS); if (!addargs) goto out; err = -EFAULT; if (copy_from_user (addargs, (void *)arg, sizeof(struct unionfs_addbranch_args))) goto out; err = -EINVAL; if (addargs->ab_perms & ~(MAY_READ | MAY_WRITE)) goto out; if (!(addargs->ab_perms & MAY_READ)) goto out; err = -E2BIG; if (sbend(inode->i_sb) > FD_SETSIZE) goto out; err = -ENOMEM; if (!(path = getname(addargs->ab_path))) goto out; err = path_lookup(path, LOOKUP_FOLLOW, &nd); RECORD_PATH_LOOKUP(&nd); if (err) goto out; if ((err = check_branch(&nd))) { path_release(&nd); RECORD_PATH_RELEASE(&nd); goto out; } unionfs_write_lock(inode->i_sb); lock_dentry(inode->i_sb->s_root); err = -EINVAL; if (addargs->ab_branch < 0 || (addargs->ab_branch > (sbend(inode->i_sb) + 1))) goto out; if ((err = newputmap(inode->i_sb))) goto out; stopd(inode->i_sb)->b_end++; dtopd(inode->i_sb->s_root)->udi_bcount++; set_dbend(inode->i_sb->s_root, dbend(inode->i_sb->s_root) + 1); itopd(inode->i_sb->s_root->d_inode)->b_end++; atomic_inc(&stopd(inode->i_sb)->usi_generation); gen = atomic_read(&stopd(inode->i_sb)->usi_generation); pobjects = (sbend(inode->i_sb) + 1) - UNIONFS_INLINE_OBJECTS; if (pobjects > 0) { /* Reallocate the dynamic structures. */ new_hidden_mnt = KMALLOC(sizeof(struct vfsmount *) * pobjects, GFP_UNIONFS); new_udi_dentry = KMALLOC(sizeof(struct dentry *) * pobjects, GFP_UNIONFS); new_uii_inode = KMALLOC(sizeof(struct inode *) * pobjects, GFP_UNIONFS); new_usi_sb = KMALLOC(sizeof(struct super_block *) * pobjects, GFP_UNIONFS); new_counts = KMALLOC(sizeof(atomic_t) * pobjects, GFP_UNIONFS); new_branchperms = KMALLOC(sizeof(int) * pobjects, GFP_UNIONFS); if (!new_hidden_mnt || !new_udi_dentry || !new_uii_inode || !new_counts || !new_usi_sb || !new_branchperms) { err = -ENOMEM; goto out; } memset(new_hidden_mnt, 0, sizeof(struct vfsmount *) * pobjects); memset(new_udi_dentry, 0, sizeof(struct dentry *) * pobjects); memset(new_uii_inode, 0, sizeof(struct inode *) * pobjects); memset(new_usi_sb, 0, sizeof(struct super_block *) * pobjects); memset(new_branchperms, 0, sizeof(int) * pobjects); } /* Copy the in-place values to our new structure. */ for (i = UNIONFS_INLINE_OBJECTS; i < addargs->ab_branch; i++) { int j = i - UNIONFS_INLINE_OBJECTS; count = branch_count(inode->i_sb, i); atomic_set(&(new_counts[j]), count); new_branchperms[j] = branchperms(inode->i_sb, i); new_hidden_mnt[j] = stohiddenmnt_index(inode->i_sb, i); new_usi_sb[j] = stohs_index(inode->i_sb, i); new_udi_dentry[j] = dtohd_index(inode->i_sb->s_root, i); new_uii_inode[j] = itohi_index(inode->i_sb->s_root->d_inode, i); } /* Shift the ends to the right (only handle reallocated bits). */ for (i = sbend(inode->i_sb) - 1; i >= (int)addargs->ab_branch; i--) { int j = i + 1; int perms; struct vfsmount *hm; struct super_block *hs; struct dentry *hd; struct inode *hi; int pmindex; count = branch_count(inode->i_sb, i); perms = branchperms(inode->i_sb, i); hm = stohiddenmnt_index(inode->i_sb, i); hs = stohs_index(inode->i_sb, i); hd = dtohd_index(inode->i_sb->s_root, i); hi = itohi_index(inode->i_sb->s_root->d_inode, i); /* Update the newest putmap, so it is correct for later. */ pmindex = stopd(inode->i_sb)->usi_lastputmap; pmindex -= stopd(inode->i_sb)->usi_firstputmap; stopd(inode->i_sb)->usi_putmaps[pmindex]->map[i] = j; if (j >= UNIONFS_INLINE_OBJECTS) { j -= UNIONFS_INLINE_OBJECTS; atomic_set(&(new_counts[j]), count); new_branchperms[j] = perms; new_hidden_mnt[j] = hm; new_usi_sb[j] = hs; new_udi_dentry[j] = hd; new_uii_inode[j] = hi; } else { set_branch_count(inode->i_sb, j, count); set_branchperms(inode->i_sb, j, perms); set_stohiddenmnt_index(inode->i_sb, j, hm); set_stohs_index(inode->i_sb, j, hs); set_dtohd_index(inode->i_sb->s_root, j, hd); set_itohi_index(inode->i_sb->s_root->d_inode, j, hi); } } /* Now we can free the old ones. */ KFREE(dtopd(inode->i_sb->s_root)->udi_dentry_p); KFREE(itopd(inode->i_sb->s_root->d_inode)->uii_inode_p); KFREE(stopd(inode->i_sb)->usi_hidden_mnt_p); KFREE(stopd(inode->i_sb)->usi_sb_p); KFREE(stopd(inode->i_sb)->usi_sbcount_p); KFREE(stopd(inode->i_sb)->usi_branchperms_p); /* Update the real pointers. */ dtohd_ptr(inode->i_sb->s_root) = new_udi_dentry; itohi_ptr(inode->i_sb->s_root->d_inode) = new_uii_inode; stohiddenmnt_ptr(inode->i_sb) = new_hidden_mnt; stohs_ptr(inode->i_sb) = new_usi_sb; stopd(inode->i_sb)->usi_sbcount_p = new_counts; stopd(inode->i_sb)->usi_branchperms_p = new_branchperms; /* Re-NULL the new ones so we don't try to free them. */ new_hidden_mnt = NULL; new_udi_dentry = NULL; new_usi_sb = NULL; new_uii_inode = NULL; new_counts = NULL; new_branchperms = NULL; /* Put the new dentry information into it's slot. */ set_dtohd_index(inode->i_sb->s_root, addargs->ab_branch, nd.dentry); set_itohi_index(inode->i_sb->s_root->d_inode, addargs->ab_branch, igrab(nd.dentry->d_inode)); set_branchperms(inode->i_sb, addargs->ab_branch, addargs->ab_perms); set_branch_count(inode->i_sb, addargs->ab_branch, 0); set_stohiddenmnt_index(inode->i_sb, addargs->ab_branch, nd.mnt); set_stohs_index(inode->i_sb, addargs->ab_branch, nd.dentry->d_sb); atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, gen); atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, gen); fixputmaps(inode->i_sb); out: unlock_dentry(inode->i_sb->s_root); unionfs_write_unlock(inode->i_sb); KFREE(new_hidden_mnt); KFREE(new_udi_dentry); KFREE(new_uii_inode); KFREE(new_usi_sb); KFREE(new_counts); KFREE(new_branchperms); KFREE(addargs); if (path) putname(path); print_exit_status(err); return err; }
static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info *hidden_root_info, char *options) { struct nameidata nd; char *name; int err = 0; int branches = 1; int bindex = 0; int i = 0; int j = 0; struct dentry *dent1 = NULL; struct dentry *dent2 = NULL; if (options[0] == '\0') { printk(KERN_WARNING "unionfs: no branches specified\n"); err = -EINVAL; goto out; } /* Each colon means we have a separator, this is really just a rough * guess, since strsep will handle empty fields for us. */ for (i = 0; options[i]; i++) { if (options[i] == ':') branches++; } /* allocate space for underlying pointers to hidden dentry */ if (!(stopd(sb)->usi_data = alloc_new_data(branches))) { err = -ENOMEM; goto out; } if (!(hidden_root_info->udi_dentry = alloc_new_dentries(branches))) { err = -ENOMEM; goto out; } /* now parsing the string b1:b2=rw:b3=ro:b4 */ branches = 0; while ((name = strsep(&options, ":")) != NULL) { int perms; if (!*name) continue; branches++; /* strip off =rw or =ro if it is specified. */ perms = parse_branch_mode(name); if (!bindex && !(perms & MAY_WRITE)) { err = -EINVAL; goto out; } fist_dprint(4, "unionfs: using directory: %s (%c%c%c)\n", name, perms & MAY_READ ? 'r' : '-', perms & MAY_WRITE ? 'w' : '-', perms & MAY_NFSRO ? 'n' : '-'); err = path_lookup(name, LOOKUP_FOLLOW, &nd); RECORD_PATH_LOOKUP(&nd); if (err) { printk(KERN_WARNING "unionfs: error accessing " "hidden directory '%s' (error %d)\n", name, err); goto out; } if ((err = check_branch(&nd))) { printk(KERN_WARNING "unionfs: hidden directory " "'%s' is not a valid branch\n", name); path_release(&nd); RECORD_PATH_RELEASE(&nd); goto out; } hidden_root_info->udi_dentry[bindex] = nd.dentry; set_stohiddenmnt_index(sb, bindex, nd.mnt); set_branchperms(sb, bindex, perms); set_branch_count(sb, bindex, 0); if (hidden_root_info->udi_bstart < 0) hidden_root_info->udi_bstart = bindex; hidden_root_info->udi_bend = bindex; bindex++; } if (branches == 0) { printk(KERN_WARNING "unionfs: no branches specified\n"); err = -EINVAL; goto out; } BUG_ON(branches != (hidden_root_info->udi_bend + 1)); /* ensure that no overlaps exist in the branches */ for (i = 0; i < branches; i++) { for (j = i + 1; j < branches; j++) { dent1 = hidden_root_info->udi_dentry[i]; dent2 = hidden_root_info->udi_dentry[j]; if (is_branch_overlap(dent1, dent2)) { goto out_overlap; } } } out_overlap: if (i != branches) { printk(KERN_WARNING "unionfs: branches %d and %d overlap\n", i, j); err = -EINVAL; goto out; } out: if (err) { for (i = 0; i < branches; i++) { if (hidden_root_info->udi_dentry[i]) DPUT(hidden_root_info->udi_dentry[i]); } KFREE(hidden_root_info->udi_dentry); KFREE(stopd(sb)->usi_data); /* MUST clear the pointers to prevent potential double free if * the caller dies later on */ hidden_root_info->udi_dentry = NULL; stopd(sb)->usi_data = NULL; } return err; }