static int unionfs_statfs(struct super_block *sb, struct kstatfs *buf) { int err = 0; struct super_block *hidden_sb; struct kstatfs rsb; int bindex, bindex1, bstart, bend; print_entry_location(); memset(buf, 0, sizeof(struct kstatfs)); buf->f_type = UNIONFS_SUPER_MAGIC; buf->f_frsize = 0; buf->f_namelen = 0; bstart = sbstart(sb); bend = sbend(sb); for (bindex = bstart; bindex <= bend; bindex++) { int dup = 0; hidden_sb = stohs_index(sb, bindex); /* Ignore duplicate super blocks. */ for (bindex1 = bstart; bindex1 < bindex; bindex1++) { if (hidden_sb == stohs_index(sb, bindex1)) { dup = 1; break; } } if (dup) { continue; } err = vfs_statfs(hidden_sb, &rsb); fist_dprint(8, "adding values for bindex:%d bsize:%d blocks:%d bfree:%d bavail:%d\n", bindex, (int)rsb.f_bsize, (int)rsb.f_blocks, (int)rsb.f_bfree, (int)rsb.f_bavail); if (!buf->f_frsize) buf->f_frsize = rsb.f_frsize; if (!buf->f_namelen) { buf->f_namelen = rsb.f_namelen; } else { if (buf->f_namelen > rsb.f_namelen) buf->f_namelen = rsb.f_namelen; } if (!buf->f_bsize) { buf->f_bsize = rsb.f_bsize; } else { if (buf->f_bsize < rsb.f_bsize) { int shifter = 0; while (buf->f_bsize < rsb.f_bsize) { shifter++; rsb.f_bsize >>= 1; } rsb.f_blocks <<= shifter; rsb.f_bfree <<= shifter; rsb.f_bavail <<= shifter; } else { int shifter = 0; while (buf->f_bsize > rsb.f_bsize) { shifter++; rsb.f_bsize <<= 1; } rsb.f_blocks >>= shifter; rsb.f_bfree >>= shifter; rsb.f_bavail >>= shifter; } }
/* This must be called with the super block already locked. */ int unionfs_ioctl_delbranch(struct super_block *sb, unsigned long arg) { struct dentry *hidden_dentry; struct inode *hidden_inode; struct super_block *hidden_sb; struct vfsmount *hidden_mnt; struct dentry *root_dentry; struct inode *root_inode; int err = 0; int pmindex, i, gen; print_entry("branch = %lu ", arg); lock_dentry(sb->s_root); err = -EBUSY; if (sbmax(sb) == 1) goto out; err = -EINVAL; if (arg < 0 || arg > stopd(sb)->b_end) goto out; err = -EBUSY; if (branch_count(sb, arg)) goto out; if ((err = newputmap(sb))) goto out; pmindex = stopd(sb)->usi_lastputmap; pmindex -= stopd(sb)->usi_firstputmap; atomic_inc(&stopd(sb)->usi_generation); gen = atomic_read(&stopd(sb)->usi_generation); root_dentry = sb->s_root; root_inode = sb->s_root->d_inode; hidden_dentry = dtohd_index(root_dentry, arg); hidden_mnt = stohiddenmnt_index(sb, arg); hidden_inode = itohi_index(root_inode, arg); hidden_sb = stohs_index(sb, arg); DPUT(hidden_dentry); iput(hidden_inode); mntput(hidden_mnt); for (i = arg; i <= (sbend(sb) - 1); i++) { set_branch_count(sb, i, branch_count(sb, i + 1)); set_stohiddenmnt_index(sb, i, stohiddenmnt_index(sb, i + 1)); set_stohs_index(sb, i, stohs_index(sb, i + 1)); set_branchperms(sb, i, branchperms(sb, i + 1)); set_dtohd_index(root_dentry, i, dtohd_index(root_dentry, i + 1)); set_itohi_index(root_inode, i, itohi_index(root_inode, i + 1)); stopd(sb)->usi_putmaps[pmindex]->map[i + 1] = i; } set_dtohd_index(root_dentry, sbend(sb), NULL); set_itohi_index(root_inode, sbend(sb), NULL); set_stohiddenmnt_index(sb, sbend(sb), NULL); set_stohs_index(sb, sbend(sb), NULL); stopd(sb)->b_end--; set_dbend(root_dentry, dbend(root_dentry) - 1); dtopd(root_dentry)->udi_bcount--; itopd(root_inode)->b_end--; atomic_set(&dtopd(root_dentry)->udi_generation, gen); atomic_set(&itopd(root_inode)->uii_generation, gen); fixputmaps(sb); /* This doesn't open a file, so we might have to free the map here. */ if (atomic_read(&stopd(sb)->usi_putmaps[pmindex]->count) == 0) { KFREE(stopd(sb)->usi_putmaps[pmindex]); stopd(sb)->usi_putmaps[pmindex] = NULL; } out: unlock_dentry(sb->s_root); 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 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 struct dentry *unionfs_d_alloc_root(struct super_block *sb) { struct dentry *ret = NULL; if (sb) { static const struct qstr name = {.name = "/",.len = 1 }; ret = d_alloc(NULL, &name); if (ret) { ret->d_op = &unionfs_dops; ret->d_sb = sb; ret->d_parent = ret; } } return ret; } static int unionfs_read_super(struct super_block *sb, void *raw_data, int silent) { int err = 0; struct unionfs_dentry_info *hidden_root_info = NULL; int bindex, bstart, bend; unsigned long long maxbytes; print_entry_location(); if (!raw_data) { printk(KERN_WARNING "unionfs_read_super: missing data argument\n"); err = -EINVAL; goto out; } /* * Allocate superblock private data */ stopd_lhs(sb) = KZALLOC(sizeof(struct unionfs_sb_info), GFP_KERNEL); if (!stopd(sb)) { printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__); err = -ENOMEM; goto out; } stopd(sb)->b_end = -1; atomic_set(&stopd(sb)->usi_generation, 1); init_rwsem(&stopd(sb)->usi_rwsem); hidden_root_info = unionfs_parse_options(sb, raw_data); if (IS_ERR(hidden_root_info)) { printk(KERN_WARNING "unionfs_read_super: error while parsing options (err = %ld)\n", PTR_ERR(hidden_root_info)); err = PTR_ERR(hidden_root_info); hidden_root_info = NULL; goto out_free; } if (hidden_root_info->udi_bstart == -1) { err = -ENOENT; goto out_free; } /* set the hidden superblock field of upper superblock */ bstart = hidden_root_info->udi_bstart; BUG_ON(bstart != 0); sbend(sb) = bend = hidden_root_info->udi_bend; for (bindex = bstart; bindex <= bend; bindex++) { struct dentry *d; d = hidden_root_info->udi_dentry[bindex]; set_stohs_index(sb, bindex, d->d_sb); } /* Unionfs: Max Bytes is the maximum bytes from among all the branches */ maxbytes = -1; for (bindex = bstart; bindex <= bend; bindex++) if (maxbytes < stohs_index(sb, bindex)->s_maxbytes) maxbytes = stohs_index(sb, bindex)->s_maxbytes; sb->s_maxbytes = maxbytes; sb->s_op = &unionfs_sops; sb->s_export_op = &unionfs_export_ops; /* * we can't use d_alloc_root if we want to use * our own interpose function unchanged, * so we simply call our own "fake" d_alloc_root */ sb->s_root = unionfs_d_alloc_root(sb); if (!sb->s_root) { err = -ENOMEM; goto out_dput; } /* link the upper and lower dentries */ dtopd_lhs(sb->s_root) = NULL; if ((err = new_dentry_private_data(sb->s_root))) goto out_freedpd; /* Set the hidden dentries for s_root */ for (bindex = bstart; bindex <= bend; bindex++) { struct dentry *d; d = hidden_root_info->udi_dentry[bindex]; set_dtohd_index(sb->s_root, bindex, d); } set_dbstart(sb->s_root, bstart); set_dbend(sb->s_root, bend); /* Set the generation number to one, since this is for the mount. */ atomic_set(&dtopd(sb->s_root)->udi_generation, 1); /* call interpose to create the upper level inode */ if ((err = unionfs_interpose(sb->s_root, sb, 0))) goto out_freedpd; unlock_dentry(sb->s_root); goto out; out_freedpd: if (dtopd(sb->s_root)) { KFREE(dtohd_ptr(sb->s_root)); free_dentry_private_data(dtopd(sb->s_root)); } DPUT(sb->s_root); out_dput: if (hidden_root_info && !IS_ERR(hidden_root_info)) { for (bindex = hidden_root_info->udi_bstart; bindex <= hidden_root_info->udi_bend; bindex++) { struct dentry *d; d = hidden_root_info->udi_dentry[bindex]; if (d) DPUT(d); if (stopd(sb) && stohiddenmnt_index(sb, bindex)) mntput(stohiddenmnt_index(sb, bindex)); } KFREE(hidden_root_info->udi_dentry); KFREE(hidden_root_info); hidden_root_info = NULL; } out_free: KFREE(stopd(sb)->usi_data); KFREE(stopd(sb)); stopd_lhs(sb) = NULL; out: if (hidden_root_info && !IS_ERR(hidden_root_info)) { KFREE(hidden_root_info->udi_dentry); KFREE(hidden_root_info); } print_exit_status(err); return err; }