/* * return to user-space the branch indices containing the file in question * * We use fd_set and therefore we are limited to the number of the branches * to FD_SETSIZE, which is currently 1024 - plenty for most people */ static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent, unsigned int cmd, unsigned long arg) { int err = 0; fd_set branchlist; int bstart = 0, bend = 0, bindex = 0; int orig_bstart, orig_bend; struct dentry *dentry, *lower_dentry; struct vfsmount *mnt; dentry = file->f_path.dentry; orig_bstart = dbstart(dentry); orig_bend = dbend(dentry); err = unionfs_partial_lookup(dentry, parent); if (err) goto out; bstart = dbstart(dentry); bend = dbend(dentry); FD_ZERO(&branchlist); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; if (likely(lower_dentry->d_inode)) FD_SET(bindex, &branchlist); /* purge any lower objects after partial_lookup */ if (bindex < orig_bstart || bindex > orig_bend) { dput(lower_dentry); unionfs_set_lower_dentry_idx(dentry, bindex, NULL); iput(unionfs_lower_inode_idx(dentry->d_inode, bindex)); unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL); mnt = unionfs_lower_mnt_idx(dentry, bindex); if (!mnt) continue; unionfs_mntput(dentry, bindex); unionfs_set_lower_mnt_idx(dentry, bindex, NULL); } } /* restore original dentry's offsets */ dbstart(dentry) = orig_bstart; dbend(dentry) = orig_bend; ibstart(dentry->d_inode) = orig_bstart; ibend(dentry->d_inode) = orig_bend; err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set)); if (unlikely(err)) err = -EFAULT; out: return err < 0 ? err : bend; }
/* purge a dentry's lower-branch states (dput/mntput, etc.) */ static void __cleanup_dentry(struct dentry *dentry, int bindex, int old_bstart, int old_bend) { int loop_start; int loop_end; int new_bstart = -1; int new_bend = -1; int i; loop_start = min(old_bstart, bindex); loop_end = max(old_bend, bindex); /* * This loop sets the bstart and bend for the new dentry by * traversing from left to right. It also dputs all negative * dentries except bindex */ for (i = loop_start; i <= loop_end; i++) { if (!unionfs_lower_dentry_idx(dentry, i)) continue; if (i == bindex) { new_bend = i; if (new_bstart < 0) new_bstart = i; continue; } if (!unionfs_lower_dentry_idx(dentry, i)->d_inode) { dput(unionfs_lower_dentry_idx(dentry, i)); unionfs_set_lower_dentry_idx(dentry, i, NULL); unionfs_mntput(dentry, i); unionfs_set_lower_mnt_idx(dentry, i, NULL); } else { if (new_bstart < 0) new_bstart = i; new_bend = i; } } if (new_bstart < 0) new_bstart = bindex; if (new_bend < 0) new_bend = bindex; dbstart(dentry) = new_bstart; dbend(dentry) = new_bend; }
/* * Post-copyup helper to ensure we have valid mnts: set lower mnt of * dentry+parents to the first parent node that has an mnt. */ void unionfs_postcopyup_setmnt(struct dentry *dentry) { struct dentry *parent, *hasone; int bindex = dbstart(dentry); if (unionfs_lower_mnt_idx(dentry, bindex)) return; hasone = dentry->d_parent; /* this loop should stop at root dentry */ while (!unionfs_lower_mnt_idx(hasone, bindex)) hasone = hasone->d_parent; parent = dentry; while (!unionfs_lower_mnt_idx(parent, bindex)) { unionfs_set_lower_mnt_idx(parent, bindex, unionfs_mntget(hasone, bindex)); parent = parent->d_parent; } }
static void unionfs_d_release(struct dentry *dentry) { int bindex, bstart, bend; /* There is no reason to lock the dentry, because we have the only * reference, but the printing functions verify that we have a lock * on the dentry before calling dbstart, etc. */ unionfs_lock_dentry(dentry); /* this could be a negative dentry, so check first */ if (!UNIONFS_D(dentry)) { printk(KERN_DEBUG "dentry without private data: %.*s", dentry->d_name.len, dentry->d_name.name); goto out; } else if (dbstart(dentry) < 0) { /* this is due to a failed lookup */ printk(KERN_DEBUG "dentry without hidden dentries : %.*s", dentry->d_name.len, dentry->d_name.name); goto out_free; } /* Release all the hidden dentries */ bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { dput(unionfs_lower_dentry_idx(dentry, bindex)); mntput(unionfs_lower_mnt_idx(dentry, bindex)); unionfs_set_lower_dentry_idx(dentry, bindex, NULL); unionfs_set_lower_mnt_idx(dentry, bindex, NULL); } /* free private data (unionfs_dentry_info) here */ kfree(UNIONFS_D(dentry)->lower_paths); UNIONFS_D(dentry)->lower_paths = NULL; out_free: /* No need to unlock it, because it is disappeared. */ free_dentry_private_data(UNIONFS_D(dentry)); dentry->d_fsdata = NULL; /* just to be safe */ out: return; }
/* * Main (and complex) driver function for Unionfs's lookup * * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error * PTR if d_splice returned a different dentry. * * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's * inode info must be locked. If lookupmode is INTERPOSE_LOOKUP (i.e., a * newly looked-up dentry), then unionfs_lookup_backend will return a locked * dentry's info, which the caller must unlock. */ struct dentry *unionfs_lookup_full(struct dentry *dentry, struct dentry *parent, int lookupmode) { int err = 0; struct dentry *lower_dentry = NULL; struct vfsmount *lower_mnt; struct vfsmount *lower_dir_mnt; struct dentry *wh_lower_dentry = NULL; struct dentry *lower_dir_dentry = NULL; struct dentry *d_interposed = NULL; int bindex, bstart, bend, bopaque; int opaque, num_positive = 0; const char *name; int namelen; int pos_start, pos_end; /* * We should already have a lock on this dentry in the case of a * partial lookup, or a revalidation. Otherwise it is returned from * new_dentry_private_data already locked. */ verify_locked(dentry); verify_locked(parent); /* must initialize dentry operations */ dentry->d_op = &unionfs_dops; /* We never partial lookup the root directory. */ if (IS_ROOT(dentry)) goto out; name = dentry->d_name.name; namelen = dentry->d_name.len; /* No dentries should get created for possible whiteout names. */ if (!is_validname(name)) { err = -EPERM; goto out_free; } /* Now start the actual lookup procedure. */ bstart = dbstart(parent); bend = dbend(parent); bopaque = dbopaque(parent); BUG_ON(bstart < 0); /* adjust bend to bopaque if needed */ if ((bopaque >= 0) && (bopaque < bend)) bend = bopaque; /* lookup all possible dentries */ for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); /* skip if we already have a positive lower dentry */ if (lower_dentry) { if (dbstart(dentry) < 0) dbstart(dentry) = bindex; if (bindex > dbend(dentry)) dbend(dentry) = bindex; if (lower_dentry->d_inode) num_positive++; continue; } lower_dir_dentry = unionfs_lower_dentry_idx(parent, bindex); /* if the lower dentry's parent does not exist, skip this */ if (!lower_dir_dentry || !lower_dir_dentry->d_inode) continue; /* also skip it if the parent isn't a directory. */ if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) continue; /* XXX: should be BUG_ON */ /* check for whiteouts: stop lookup if found */ wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry); if (IS_ERR(wh_lower_dentry)) { err = PTR_ERR(wh_lower_dentry); goto out_free; } if (wh_lower_dentry->d_inode) { dbend(dentry) = dbopaque(dentry) = bindex; if (dbstart(dentry) < 0) dbstart(dentry) = bindex; dput(wh_lower_dentry); break; } dput(wh_lower_dentry); /* Now do regular lookup; lookup @name */ lower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex); lower_mnt = NULL; /* XXX: needed? */ lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt, name, &lower_mnt); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out_free; } unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); if (!lower_mnt) lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex); unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); /* adjust dbstart/end */ if (dbstart(dentry) < 0) dbstart(dentry) = bindex; if (bindex > dbend(dentry)) dbend(dentry) = bindex; /* * We always store the lower dentries above, and update * dbstart/dbend, even if the whole unionfs dentry is * negative (i.e., no lower inodes). */ if (!lower_dentry->d_inode) continue; num_positive++; /* * check if we just found an opaque directory, if so, stop * lookups here. */ if (!S_ISDIR(lower_dentry->d_inode->i_mode)) continue; opaque = is_opaque_dir(dentry, bindex); if (opaque < 0) { err = opaque; goto out_free; } else if (opaque) { dbend(dentry) = dbopaque(dentry) = bindex; break; } dbend(dentry) = bindex; /* update parent directory's atime with the bindex */ fsstack_copy_attr_atime(parent->d_inode, lower_dir_dentry->d_inode); } /* sanity checks, then decide if to process a negative dentry */ BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); if (num_positive > 0) goto out_positive; /*** handle NEGATIVE dentries ***/ /* * If negative, keep only first lower negative dentry, to save on * memory. */ if (dbstart(dentry) < dbend(dentry)) { path_put_lowers(dentry, dbstart(dentry) + 1, dbend(dentry), false); dbend(dentry) = dbstart(dentry); } if (lookupmode == INTERPOSE_PARTIAL) goto out; if (lookupmode == INTERPOSE_LOOKUP) { /* * If all we found was a whiteout in the first available * branch, then create a negative dentry for a possibly new * file to be created. */ if (dbopaque(dentry) < 0) goto out; /* XXX: need to get mnt here */ bindex = dbstart(dentry); if (unionfs_lower_dentry_idx(dentry, bindex)) goto out; lower_dir_dentry = unionfs_lower_dentry_idx(parent, bindex); if (!lower_dir_dentry || !lower_dir_dentry->d_inode) goto out; if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) goto out; /* XXX: should be BUG_ON */ /* XXX: do we need to cross bind mounts here? */ lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out; } /* XXX: need to mntget/mntput as needed too! */ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); /* XXX: wrong mnt for crossing bind mounts! */ lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex); unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); goto out; } /* if we're revalidating a positive dentry, don't make it negative */ if (lookupmode != INTERPOSE_REVAL) d_add(dentry, NULL); goto out; out_positive: /*** handle POSITIVE dentries ***/ /* * This unionfs dentry is positive (at least one lower inode * exists), so scan entire dentry from beginning to end, and remove * any negative lower dentries, if any. Then, update dbstart/dbend * to reflect the start/end of positive dentries. */ pos_start = pos_end = -1; for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (lower_dentry && lower_dentry->d_inode) { if (pos_start < 0) pos_start = bindex; if (bindex > pos_end) pos_end = bindex; continue; } path_put_lowers(dentry, bindex, bindex, false); } if (pos_start >= 0) dbstart(dentry) = pos_start; if (pos_end >= 0) dbend(dentry) = pos_end; /* Partial lookups need to re-interpose, or throw away older negs. */ if (lookupmode == INTERPOSE_PARTIAL) { if (dentry->d_inode) { unionfs_reinterpose(dentry); goto out; } /* * This dentry was positive, so it is as if we had a * negative revalidation. */ lookupmode = INTERPOSE_REVAL_NEG; update_bstart(dentry); } /* * Interpose can return a dentry if d_splice returned a different * dentry. */ d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode); if (IS_ERR(d_interposed)) err = PTR_ERR(d_interposed); else if (d_interposed) dentry = d_interposed; if (!err) goto out; d_drop(dentry); out_free: /* should dput/mntput all the underlying dentries on error condition */ if (dbstart(dentry) >= 0) path_put_lowers_all(dentry, false); /* free lower_paths unconditionally */ kfree(UNIONFS_D(dentry)->lower_paths); UNIONFS_D(dentry)->lower_paths = NULL; out: if (dentry && UNIONFS_D(dentry)) { BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); } if (d_interposed && UNIONFS_D(d_interposed)) { BUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0); BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0); } if (!err && d_interposed) return d_interposed; return ERR_PTR(err); }
struct dentry *unionfs_lookup_backend(struct dentry *dentry, struct nameidata *nd, int lookupmode) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *wh_hidden_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; struct dentry *parent_dentry = NULL; int bindex, bstart, bend, bopaque; int dentry_count = 0; /* Number of positive dentries. */ int first_dentry_offset = -1; struct dentry *first_hidden_dentry = NULL; struct vfsmount *first_hidden_mnt = NULL; int locked_parent = 0; int locked_child = 0; int opaque; char *whname = NULL; const char *name; int namelen; /* We should already have a lock on this dentry in the case of a * partial lookup, or a revalidation. Otherwise it is returned from * new_dentry_private_data already locked. */ if (lookupmode == INTERPOSE_PARTIAL || lookupmode == INTERPOSE_REVAL || lookupmode == INTERPOSE_REVAL_NEG) verify_locked(dentry); else { BUG_ON(UNIONFS_D(dentry) != NULL); locked_child = 1; } if (lookupmode != INTERPOSE_PARTIAL) if ((err = new_dentry_private_data(dentry))) goto out; /* must initialize dentry operations */ dentry->d_op = &unionfs_dops; parent_dentry = dget_parent(dentry); /* We never partial lookup the root directory. */ if (parent_dentry != dentry) { unionfs_lock_dentry(parent_dentry); locked_parent = 1; } else { dput(parent_dentry); parent_dentry = NULL; goto out; } name = dentry->d_name.name; namelen = dentry->d_name.len; /* No dentries should get created for possible whiteout names. */ if (!is_validname(name)) { err = -EPERM; goto out_free; } /* Now start the actual lookup procedure. */ bstart = dbstart(parent_dentry); bend = dbend(parent_dentry); bopaque = dbopaque(parent_dentry); BUG_ON(bstart < 0); /* It would be ideal if we could convert partial lookups to only have * to do this work when they really need to. It could probably improve * performance quite a bit, and maybe simplify the rest of the code. */ if (lookupmode == INTERPOSE_PARTIAL) { bstart++; if ((bopaque != -1) && (bopaque < bend)) bend = bopaque; } for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry) continue; BUG_ON(hidden_dentry != NULL); hidden_dir_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); /* if the parent hidden dentry does not exist skip this */ if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode)) continue; /* also skip it if the parent isn't a directory. */ if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode)) continue; /* Reuse the whiteout name because its value doesn't change. */ if (!whname) { whname = alloc_whname(name, namelen); if (IS_ERR(whname)) { err = PTR_ERR(whname); goto out_free; } } /* check if whiteout exists in this branch: lookup .wh.foo */ wh_hidden_dentry = lookup_one_len(whname, hidden_dir_dentry, namelen + UNIONFS_WHLEN); if (IS_ERR(wh_hidden_dentry)) { dput(first_hidden_dentry); mntput(first_hidden_mnt); err = PTR_ERR(wh_hidden_dentry); goto out_free; } if (wh_hidden_dentry->d_inode) { /* We found a whiteout so lets give up. */ if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) { set_dbend(dentry, bindex); set_dbopaque(dentry, bindex); dput(wh_hidden_dentry); break; } err = -EIO; printk(KERN_NOTICE "EIO: Invalid whiteout entry type" " %d.\n", wh_hidden_dentry->d_inode->i_mode); dput(wh_hidden_dentry); dput(first_hidden_dentry); mntput(first_hidden_mnt); goto out_free; } dput(wh_hidden_dentry); wh_hidden_dentry = NULL; /* Now do regular lookup; lookup foo */ nd->dentry = unionfs_lower_dentry_idx(dentry, bindex); /* FIXME: fix following line for mount point crossing */ nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex); hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry, namelen, nd); if (IS_ERR(hidden_dentry)) { dput(first_hidden_dentry); mntput(first_hidden_mnt); err = PTR_ERR(hidden_dentry); goto out_free; } /* Store the first negative dentry specially, because if they * are all negative we need this for future creates. */ if (!hidden_dentry->d_inode) { if (!first_hidden_dentry && (dbstart(dentry) == -1)) { first_hidden_dentry = hidden_dentry; /* FIXME: following line needs to be changed * to allow mountpoint crossing */ first_hidden_mnt = mntget( unionfs_lower_mnt_idx(parent_dentry, bindex)); first_dentry_offset = bindex; } else dput(hidden_dentry); continue; } /* number of positive dentries */ dentry_count++; /* store underlying dentry */ if (dbstart(dentry) == -1) set_dbstart(dentry, bindex); unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry); /* FIXME: the following line needs to get fixed to allow * mountpoint crossing */ unionfs_set_lower_mnt_idx(dentry, bindex, mntget(unionfs_lower_mnt_idx(parent_dentry, bindex))); set_dbend(dentry, bindex); /* update parent directory's atime with the bindex */ fsstack_copy_attr_atime(parent_dentry->d_inode, hidden_dir_dentry->d_inode); /* We terminate file lookups here. */ if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) { if (lookupmode == INTERPOSE_PARTIAL) continue; if (dentry_count == 1) goto out_positive; /* This can only happen with mixed D-*-F-* */ BUG_ON(!S_ISDIR(unionfs_lower_dentry(dentry)->d_inode->i_mode)); continue; } opaque = is_opaque_dir(dentry, bindex); if (opaque < 0) { dput(first_hidden_dentry); mntput(first_hidden_mnt); err = opaque; goto out_free; } else if (opaque) { set_dbend(dentry, bindex); set_dbopaque(dentry, bindex); break; } } if (dentry_count) goto out_positive; else goto out_negative; out_negative: if (lookupmode == INTERPOSE_PARTIAL) goto out; /* If we've only got negative dentries, then use the leftmost one. */ if (lookupmode == INTERPOSE_REVAL) { if (dentry->d_inode) UNIONFS_I(dentry->d_inode)->stale = 1; goto out; } /* This should only happen if we found a whiteout. */ if (first_dentry_offset == -1) { nd->dentry = dentry; /* FIXME: fix following line for mount point crossing */ nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex); first_hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry, namelen, nd); first_dentry_offset = bindex; if (IS_ERR(first_hidden_dentry)) { err = PTR_ERR(first_hidden_dentry); goto out; } /* FIXME: the following line needs to be changed to allow * mountpoint crossing */ first_hidden_mnt = mntget(unionfs_lower_mnt_idx(dentry, bindex)); } unionfs_set_lower_dentry_idx(dentry, first_dentry_offset, first_hidden_dentry); unionfs_set_lower_mnt_idx(dentry, first_dentry_offset, first_hidden_mnt); set_dbstart(dentry, first_dentry_offset); set_dbend(dentry, first_dentry_offset); if (lookupmode == INTERPOSE_REVAL_NEG) BUG_ON(dentry->d_inode != NULL); else d_add(dentry, NULL); goto out; /* This part of the code is for positive dentries. */ out_positive: BUG_ON(dentry_count <= 0); /* If we're holding onto the first negative dentry & corresponding * vfsmount - throw it out. */ dput(first_hidden_dentry); mntput(first_hidden_mnt); /* Partial lookups need to reinterpose, or throw away older negs. */ if (lookupmode == INTERPOSE_PARTIAL) { if (dentry->d_inode) { unionfs_reinterpose(dentry); goto out; } /* This somehow turned positive, so it is as if we had a * negative revalidation. */ lookupmode = INTERPOSE_REVAL_NEG; update_bstart(dentry); bstart = dbstart(dentry); bend = dbend(dentry); } err = unionfs_interpose(dentry, dentry->d_sb, lookupmode); if (err) goto out_drop; goto out; out_drop: d_drop(dentry); out_free: /* should dput all the underlying dentries on error condition */ bstart = dbstart(dentry); if (bstart >= 0) { bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { dput(unionfs_lower_dentry_idx(dentry, bindex)); mntput(unionfs_lower_mnt_idx(dentry, bindex)); } } kfree(UNIONFS_D(dentry)->lower_paths); UNIONFS_D(dentry)->lower_paths = NULL; set_dbstart(dentry, -1); set_dbend(dentry, -1); out: if (!err && UNIONFS_D(dentry)) { BUG_ON(dbend(dentry) > UNIONFS_D(dentry)->bcount); BUG_ON(dbend(dentry) > sbmax(dentry->d_sb)); BUG_ON(dbstart(dentry) < 0); } kfree(whname); if (locked_parent) unionfs_unlock_dentry(parent_dentry); dput(parent_dentry); if (locked_child) unionfs_unlock_dentry(dentry); return ERR_PTR(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; }
/* * 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; }