/* * There is no need to lock the unionfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. */ static int unionfs_read_super(struct super_block *sb, void *raw_data, int silent) { int err = 0; struct unionfs_dentry_info *lower_root_info = NULL; int bindex, bstart, bend; struct inode *inode = NULL; if (!raw_data) { printk(KERN_ERR "unionfs: read_super: missing data argument\n"); err = -EINVAL; goto out; } /* Allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL); if (unlikely(!UNIONFS_SB(sb))) { printk(KERN_CRIT "unionfs: read_super: out of memory\n"); err = -ENOMEM; goto out; } UNIONFS_SB(sb)->bend = -1; atomic_set(&UNIONFS_SB(sb)->generation, 1); init_rwsem(&UNIONFS_SB(sb)->rwsem); UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */ lower_root_info = unionfs_parse_options(sb, raw_data); if (IS_ERR(lower_root_info)) { printk(KERN_ERR "unionfs: read_super: error while parsing options " "(err = %ld)\n", PTR_ERR(lower_root_info)); err = PTR_ERR(lower_root_info); lower_root_info = NULL; goto out_free; } if (lower_root_info->bstart == -1) { err = -ENOENT; goto out_free; } /* set the lower superblock field of upper superblock */ bstart = lower_root_info->bstart; BUG_ON(bstart != 0); sbend(sb) = bend = lower_root_info->bend; for (bindex = bstart; bindex <= bend; bindex++) { struct dentry *d = lower_root_info->lower_paths[bindex].dentry; atomic_inc(&d->d_sb->s_active); unionfs_set_lower_super_idx(sb, bindex, d->d_sb); } /* max Bytes is the maximum bytes from highest priority branch */ sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes; /* * Our c/m/atime granularity is 1 ns because we may stack on file * systems whose granularity is as good. This is important for our * time-based cache coherency. */ sb->s_time_gran = 1; sb->s_op = &unionfs_sops; /* get a new inode and allocate our root dentry */ inode = unionfs_iget(sb, iunique(sb, UNIONFS_ROOT_INO)); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_dput; } sb->s_root = d_make_root(inode); if (unlikely(!sb->s_root)) { err = -ENOMEM; goto out_iput; } d_set_d_op(sb->s_root, &unionfs_dops); /* link the upper and lower dentries */ sb->s_root->d_fsdata = NULL; err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT); if (unlikely(err)) goto out_freedpd; /* if get here: cannot have error */ /* Set the lower dentries for s_root */ for (bindex = bstart; bindex <= bend; bindex++) { struct dentry *d; struct vfsmount *m; d = lower_root_info->lower_paths[bindex].dentry; m = lower_root_info->lower_paths[bindex].mnt; unionfs_set_lower_dentry_idx(sb->s_root, bindex, d); unionfs_set_lower_mnt_idx(sb->s_root, bindex, m); } dbstart(sb->s_root) = bstart; dbend(sb->s_root) = bend; /* Set the generation number to one, since this is for the mount. */ atomic_set(&UNIONFS_D(sb->s_root)->generation, 1); if (atomic_read(&inode->i_count) <= 1) unionfs_fill_inode(sb->s_root, inode); /* * No need to call interpose because we already have a positive * dentry, which was instantiated by d_alloc_root. Just need to * d_rehash it. */ d_rehash(sb->s_root); unionfs_unlock_dentry(sb->s_root); goto out; /* all is well */ out_freedpd: if (UNIONFS_D(sb->s_root)) { kfree(UNIONFS_D(sb->s_root)->lower_paths); free_dentry_private_data(sb->s_root); } dput(sb->s_root); out_iput: iput(inode); out_dput: if (lower_root_info && !IS_ERR(lower_root_info)) { for (bindex = lower_root_info->bstart; bindex <= lower_root_info->bend; bindex++) { struct dentry *d; d = lower_root_info->lower_paths[bindex].dentry; /* drop refs we took earlier */ atomic_dec(&d->d_sb->s_active); path_put(&lower_root_info->lower_paths[bindex]); } kfree(lower_root_info->lower_paths); kfree(lower_root_info); lower_root_info = NULL; } out_free: kfree(UNIONFS_SB(sb)->data); kfree(UNIONFS_SB(sb)); sb->s_fs_info = NULL; out: if (lower_root_info && !IS_ERR(lower_root_info)) { kfree(lower_root_info->lower_paths); kfree(lower_root_info); } return err; }
/* * our custom d_alloc_root work-alike * * 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 */ 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 (likely(ret)) { ret->d_op = &unionfs_dops; ret->d_sb = sb; ret->d_parent = ret; } } return ret; } /* * There is no need to lock the unionfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. */ static int unionfs_read_super(struct super_block *sb, void *raw_data, int silent) { int err = 0; struct unionfs_dentry_info *lower_root_info = NULL; int bindex, bstart, bend; if (!raw_data) { printk(KERN_ERR "unionfs: read_super: missing data argument\n"); err = -EINVAL; goto out; } /* Allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL); if (unlikely(!UNIONFS_SB(sb))) { printk(KERN_CRIT "unionfs: read_super: out of memory\n"); err = -ENOMEM; goto out; } UNIONFS_SB(sb)->bend = -1; atomic_set(&UNIONFS_SB(sb)->generation, 1); init_rwsem(&UNIONFS_SB(sb)->rwsem); UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */ lower_root_info = unionfs_parse_options(sb, raw_data); if (IS_ERR(lower_root_info)) { printk(KERN_ERR "unionfs: read_super: error while parsing options " "(err = %ld)\n", PTR_ERR(lower_root_info)); err = PTR_ERR(lower_root_info); lower_root_info = NULL; goto out_free; } if (lower_root_info->bstart == -1) { err = -ENOENT; goto out_free; } /* set the lower superblock field of upper superblock */ bstart = lower_root_info->bstart; BUG_ON(bstart != 0); sbend(sb) = bend = lower_root_info->bend; for (bindex = bstart; bindex <= bend; bindex++) { struct dentry *d = lower_root_info->lower_paths[bindex].dentry; atomic_inc(&d->d_sb->s_active); unionfs_set_lower_super_idx(sb, bindex, d->d_sb); } /* max Bytes is the maximum bytes from highest priority branch */ sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes; /* * Our c/m/atime granularity is 1 ns because we may stack on file * systems whose granularity is as good. This is important for our * time-based cache coherency. */ sb->s_time_gran = 1; sb->s_op = &unionfs_sops; /* See comment next to the definition of unionfs_d_alloc_root */ sb->s_root = unionfs_d_alloc_root(sb); if (unlikely(!sb->s_root)) { err = -ENOMEM; goto out_dput; } /* link the upper and lower dentries */ sb->s_root->d_fsdata = NULL; err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT); if (unlikely(err)) goto out_freedpd; /* Set the lower dentries for s_root */ for (bindex = bstart; bindex <= bend; bindex++) { struct dentry *d; struct vfsmount *m; d = lower_root_info->lower_paths[bindex].dentry; m = lower_root_info->lower_paths[bindex].mnt; unionfs_set_lower_dentry_idx(sb->s_root, bindex, d); unionfs_set_lower_mnt_idx(sb->s_root, bindex, m); } dbstart(sb->s_root) = bstart; dbend(sb->s_root) = bend; /* Set the generation number to one, since this is for the mount. */ atomic_set(&UNIONFS_D(sb->s_root)->generation, 1); /* * Call interpose to create the upper level inode. Only * INTERPOSE_LOOKUP can return a value other than 0 on err. */ err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0)); unionfs_unlock_dentry(sb->s_root); if (!err) goto out; /* else fall through */ out_freedpd: if (UNIONFS_D(sb->s_root)) { kfree(UNIONFS_D(sb->s_root)->lower_paths); free_dentry_private_data(sb->s_root); } dput(sb->s_root); out_dput: if (lower_root_info && !IS_ERR(lower_root_info)) { for (bindex = lower_root_info->bstart; bindex <= lower_root_info->bend; bindex++) { struct dentry *d; struct vfsmount *m; d = lower_root_info->lower_paths[bindex].dentry; m = lower_root_info->lower_paths[bindex].mnt; dput(d); /* initializing: can't use unionfs_mntput here */ mntput(m); /* drop refs we took earlier */ atomic_dec(&d->d_sb->s_active); } kfree(lower_root_info->lower_paths); kfree(lower_root_info); lower_root_info = NULL; } out_free: kfree(UNIONFS_SB(sb)->data); kfree(UNIONFS_SB(sb)); sb->s_fs_info = NULL; out: if (lower_root_info && !IS_ERR(lower_root_info)) { kfree(lower_root_info->lower_paths); kfree(lower_root_info); } 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; }