lkml.org 
[lkml]   [2007]   [Apr]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 09/21] Unionfs: Bulk of branch-management remount code
    Date
    From: Erez Zadok <ezk@cs.sunysb.edu>

    Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
    Signed-off-by: Josef 'Jeff' Sipek <jsipek@cs.sunysb.edu>
    ---
    fs/unionfs/main.c | 52 +++--
    fs/unionfs/super.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++++++-
    fs/unionfs/union.h | 6 +
    3 files changed, 646 insertions(+), 24 deletions(-)

    diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
    index ed264c7..1c93b13 100644
    --- a/fs/unionfs/main.c
    +++ b/fs/unionfs/main.c
    @@ -181,7 +181,7 @@ void unionfs_reinterpose(struct dentry *dentry)
    * 2) it exists
    * 3) is a directory
    */
    -static int check_branch(struct nameidata *nd)
    +int check_branch(struct nameidata *nd)
    {
    if (!strcmp(nd->dentry->d_sb->s_type->name, "unionfs"))
    return -EINVAL;
    @@ -211,20 +211,29 @@ static int is_branch_overlap(struct dentry *dent1, struct dentry *dent2)
    return (dent == dent1);
    }

    -/* parse branch mode */
    -static int parse_branch_mode(char *name)
    +/*
    + * Parse branch mode helper function
    + */
    +int __parse_branch_mode(const char *name)
    {
    - int perms;
    - int l = strlen(name);
    - if (!strcmp(name + l - 3, "=ro")) {
    - perms = MAY_READ;
    - name[l - 3] = '\0';
    - } else if (!strcmp(name + l - 3, "=rw")) {
    - perms = MAY_READ | MAY_WRITE;
    - name[l - 3] = '\0';
    - } else
    - perms = MAY_READ | MAY_WRITE;
    + if (!name)
    + return 0;
    + if (!strcmp(name, "ro"))
    + return MAY_READ;
    + if (!strcmp(name, "rw"))
    + return (MAY_READ | MAY_WRITE);
    + return 0;
    +}

    +/*
    + * Parse "ro" or "rw" options, but default to "rw" of no mode options
    + * was specified.
    + */
    +int parse_branch_mode(const char *name)
    +{
    + int perms = __parse_branch_mode(name);
    + if (perms == 0)
    + perms = MAY_READ | MAY_WRITE;
    return perms;
    }

    @@ -271,18 +280,22 @@ static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info
    goto out;
    }

    - /* now parsing the string b1:b2=rw:b3=ro:b4 */
    + /* now parsing a string such as "b1:b2=rw:b3=ro:b4" */
    branches = 0;
    while ((name = strsep(&options, ":")) != NULL) {
    int perms;
    + char *mode = strchr(name, '=');

    - if (!*name)
    + if (!name || !*name)
    continue;

    branches++;

    - /* strip off =rw or =ro if it is specified. */
    - perms = parse_branch_mode(name);
    + /* strip off '=' if any */
    + if (mode)
    + *mode++ = '\0';
    +
    + perms = parse_branch_mode(mode);
    if (!bindex && !(perms & MAY_WRITE)) {
    err = -EINVAL;
    goto out;
    @@ -305,8 +318,11 @@ static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info
    hidden_root_info->lower_paths[bindex].dentry = nd.dentry;
    hidden_root_info->lower_paths[bindex].mnt = nd.mnt;

    + unionfs_write_lock(sb);
    set_branchperms(sb, bindex, perms);
    set_branch_count(sb, bindex, 0);
    + new_branch_id(sb, bindex);
    + unionfs_write_unlock(sb);

    if (hidden_root_info->bstart < 0)
    hidden_root_info->bstart = bindex;
    @@ -387,7 +403,7 @@ static struct unionfs_dentry_info *unionfs_parse_options(struct super_block *sb,
    char *endptr;
    int intval;

    - if (!*optname)
    + if (!optname || !*optname)
    continue;

    optarg = strchr(optname, '=');
    diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
    index 037c47d..7f0d174 100644
    --- a/fs/unionfs/super.c
    +++ b/fs/unionfs/super.c
    @@ -143,14 +143,614 @@ static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf)
    return err;
    }

    -/* We don't support a standard text remount. Eventually it would be nice to
    - * support a full-on remount, so that you can have all of the directories
    - * change at once, but that would require some pretty complicated matching
    - * code.
    +/* handle mode changing during remount */
    +static int do_remount_mode_option(char *optarg, int cur_branches,
    + struct unionfs_data *new_data,
    + struct path *new_lower_paths)
    +{
    + int err = -EINVAL;
    + int perms, idx;
    + char *modename = strchr(optarg, '=');
    + struct nameidata nd;
    +
    + /* by now, optarg contains the branch name */
    + if (!*optarg) {
    + printk("unionfs: no branch specified for mode change.\n");
    + goto out;
    + }
    + if (!modename) {
    + printk("unionfs: branch \"%s\" requires a mode.\n", optarg);
    + goto out;
    + }
    + *modename++ = '\0';
    + perms = __parse_branch_mode(modename);
    + if (perms == 0) {
    + printk("unionfs: invalid mode \"%s\" for \"%s\".\n",
    + modename, optarg);
    + goto out;
    + }
    +
    + /*
    + * Find matching branch index. For now, this assumes that nothing
    + * has been mounted on top of this Unionfs stack. Once we have /odf
    + * and cache-coherency resolved, we'll address the branch-path
    + * uniqueness.
    + */
    + err = path_lookup(optarg, LOOKUP_FOLLOW, &nd);
    + if (err) {
    + printk(KERN_WARNING "unionfs: error accessing "
    + "hidden directory \"%s\" (error %d)\n",
    + optarg, err);
    + goto out;
    + }
    + for (idx=0; idx<cur_branches; idx++)
    + if (nd.mnt == new_lower_paths[idx].mnt &&
    + nd.dentry == new_lower_paths[idx].dentry)
    + break;
    + path_release(&nd); /* no longer needed */
    + if (idx == cur_branches) {
    + err = -ENOENT; /* err may have been reset above */
    + printk(KERN_WARNING "unionfs: branch \"%s\" "
    + "not found\n", optarg);
    + goto out;
    + }
    + /* check/change mode for existing branch */
    + /* we don't warn if perms==branchperms */
    + new_data[idx].branchperms = perms;
    + err = 0;
    +out:
    + return err;
    +}
    +
    +/* handle branch deletion during remount */
    +static int do_remount_del_option(char *optarg, int cur_branches,
    + struct unionfs_data *new_data,
    + struct path *new_lower_paths)
    +{
    + int err = -EINVAL;
    + int idx;
    + struct nameidata nd;
    + /* optarg contains the branch name to delete */
    +
    + /*
    + * Find matching branch index. For now, this assumes that nothing
    + * has been mounted on top of this Unionfs stack. Once we have /odf
    + * and cache-coherency resolved, we'll address the branch-path
    + * uniqueness.
    + */
    + err = path_lookup(optarg, LOOKUP_FOLLOW, &nd);
    + if (err) {
    + printk(KERN_WARNING "unionfs: error accessing "
    + "hidden directory \"%s\" (error %d)\n",
    + optarg, err);
    + goto out;
    + }
    + for (idx=0; idx < cur_branches; idx++)
    + if (nd.mnt == new_lower_paths[idx].mnt &&
    + nd.dentry == new_lower_paths[idx].dentry)
    + break;
    + path_release(&nd); /* no longer needed */
    + if (idx == cur_branches) {
    + printk(KERN_WARNING "unionfs: branch \"%s\" "
    + "not found\n", optarg);
    + err = -ENOENT;
    + goto out;
    + }
    + /* check if there are any open files on the branch to be deleted */
    + if (atomic_read(&new_data[idx].open_files) > 0) {
    + err = -EBUSY;
    + goto out;
    + }
    +
    + /*
    + * Now we have to delete the branch. First, release any handles it
    + * has. Then, move the remaining array indexes past "idx" in
    + * new_data and new_lower_paths one to the left. Finally, adjust
    + * cur_branches.
    + */
    + pathput(&new_lower_paths[idx]);
    +
    + if (idx < cur_branches - 1) {
    + /* if idx==cur_branches-1, we delete last branch: easy */
    + memmove(&new_data[idx], &new_data[idx+1],
    + (cur_branches - 1 - idx) * sizeof(struct unionfs_data));
    + memmove(&new_lower_paths[idx], &new_lower_paths[idx+1],
    + (cur_branches - 1 - idx) * sizeof(struct path));
    + }
    +
    + err = 0;
    +out:
    + return err;
    +}
    +
    +/* handle branch insertion during remount */
    +static int do_remount_add_option(char *optarg, int cur_branches,
    + struct unionfs_data *new_data,
    + struct path *new_lower_paths,
    + int *high_branch_id)
    +{
    + int err = -EINVAL;
    + int perms;
    + int idx = 0; /* default: insert at beginning */
    + char *new_branch , *modename = NULL;
    + struct nameidata nd;
    +
    + /*
    + * optarg can be of several forms:
    + *
    + * /bar:/foo insert /foo before /bar
    + * /bar:/foo=ro insert /foo in ro mode before /bar
    + * /foo insert /foo in the beginning (prepend)
    + * :/foo insert /foo at the end (append)
    + */
    + if (*optarg == ':') { /* append? */
    + new_branch = optarg + 1; /* skip ':' */
    + idx = cur_branches;
    + goto found_insertion_point;
    + }
    + new_branch = strchr(optarg, ':');
    + if (!new_branch) { /* prepend? */
    + new_branch = optarg;
    + goto found_insertion_point;
    + }
    + *new_branch++ = '\0'; /* holds path+mode of new branch */
    +
    + /*
    + * Find matching branch index. For now, this assumes that nothing
    + * has been mounted on top of this Unionfs stack. Once we have /odf
    + * and cache-coherency resolved, we'll address the branch-path
    + * uniqueness.
    + */
    + err = path_lookup(optarg, LOOKUP_FOLLOW, &nd);
    + if (err) {
    + printk(KERN_WARNING "unionfs: error accessing "
    + "hidden directory \"%s\" (error %d)\n",
    + optarg, err);
    + goto out;
    + }
    + for (idx=0; idx < cur_branches; idx++)
    + if (nd.mnt == new_lower_paths[idx].mnt &&
    + nd.dentry == new_lower_paths[idx].dentry)
    + break;
    + path_release(&nd); /* no longer needed */
    + if (idx == cur_branches) {
    + printk(KERN_WARNING "unionfs: branch \"%s\" "
    + "not found\n", optarg);
    + err = -ENOENT;
    + goto out;
    + }
    +
    + /*
    + * At this point idx will hold the index where the new branch should
    + * be inserted before.
    + */
    +found_insertion_point:
    + /* find the mode for the new branch */
    + if (new_branch)
    + modename = strchr(new_branch, '=');
    + if (modename)
    + *modename++ = '\0';
    + perms = parse_branch_mode(modename);
    +
    + if (!new_branch || !*new_branch) {
    + printk(KERN_WARNING "unionfs: null new branch\n");
    + err = -EINVAL;
    + goto out;
    + }
    + err = path_lookup(new_branch, LOOKUP_FOLLOW, &nd);
    + if (err) {
    + printk(KERN_WARNING "unionfs: error accessing "
    + "hidden directory \"%s\" (error %d)\n",
    + new_branch, err);
    + goto out;
    + }
    + /* it's probably safe to check_mode the new branch to insert */
    + if ((err = check_branch(&nd))) {
    + printk(KERN_WARNING "unionfs: hidden directory "
    + "\"%s\" is not a valid branch\n", optarg);
    + path_release(&nd);
    + goto out;
    + }
    +
    + /*
    + * Now we have to insert the new branch. But first, move the bits
    + * to make space for the new branch, if needed. Finally, adjust
    + * cur_branches.
    + * We don't release nd here; it's kept until umount/remount.
    + */
    + if (idx < cur_branches) {
    + /* if idx==cur_branches, we append: easy */
    + memmove(&new_data[idx+1], &new_data[idx],
    + (cur_branches - idx) * sizeof(struct unionfs_data));
    + memmove(&new_lower_paths[idx+1], &new_lower_paths[idx],
    + (cur_branches - idx) * sizeof(struct path));
    + }
    + new_lower_paths[idx].dentry = nd.dentry;
    + new_lower_paths[idx].mnt = nd.mnt;
    +
    + new_data[idx].sb = nd.dentry->d_sb;
    + atomic_set(&new_data[idx].open_files, 0);
    + new_data[idx].branchperms = perms;
    + new_data[idx].branch_id = ++*high_branch_id; /* assign new branch ID */
    +
    + err = 0;
    +out:
    + return err;
    +}
    +
    +
    +/*
    + * Support branch management options on remount.
    + *
    + * See Documentation/filesystems/unionfs/ for details.
    + *
    + * @flags: numeric mount options
    + * @options: mount options string
    + *
    + * This function can rearrange a mounted union dynamically, adding and
    + * removing branches, including changing branch modes. Clearly this has to
    + * be done safely and atomically. Luckily, the VFS already calls this
    + * function with lock_super(sb) and lock_kernel() held, preventing
    + * concurrent mixing of new mounts, remounts, and unmounts. Moreover,
    + * do_remount_sb(), our caller function, already called shrink_dcache_sb(sb)
    + * to purge dentries/inodes from our superblock, and also called
    + * fsync_super(sb) to purge any dirty pages. So we're good.
    + *
    + * XXX: however, our remount code may also need to invalidate mapped pages
    + * so as to force them to be re-gotten from the (newly reconfigured) lower
    + * branches. This has to wait for proper mmap and cache coherency support
    + * in the VFS.
    + *
    */
    -static int unionfs_remount_fs(struct super_block *sb, int *flags, char *data)
    +static int unionfs_remount_fs(struct super_block *sb, int *flags,
    + char *options)
    {
    - return -ENOSYS;
    + int err = 0;
    + int i;
    + char *optionstmp, *tmp_to_free; /* kstrdup'ed of "options" */
    + char *optname;
    + int cur_branches; /* no. of current branches */
    + int new_branches; /* no. of branches actually left in the end */
    + int add_branches; /* est. no. of branches to add */
    + int del_branches; /* est. no. of branches to del */
    + int max_branches; /* max possible no. of branches */
    + struct unionfs_data *new_data = NULL, *tmp_data = NULL;
    + struct path *new_lower_paths = NULL, *tmp_lower_paths = NULL;
    + int new_high_branch_id; /* new high branch ID */
    +
    + unionfs_write_lock(sb);
    +
    + /*
    + * The VFS will take care of "ro" and "rw" flags, so anything else
    + * is an error. So we need to check if any other flags may have
    + * been passed (none are allowed/supported as of now).
    + */
    + if ((*flags & ~MS_RDONLY) != 0) {
    + printk(KERN_WARNING
    + "unionfs: remount flags 0x%x unsupported\n", *flags);
    + err = -EINVAL;
    + goto out_error;
    + }
    +
    + /*
    + * If 'options' is NULL, it's probably because the user just changed
    + * the union to a "ro" or "rw" and the VFS took care of it. So
    + * nothing to do and we're done.
    + */
    + if (options[0] == '\0')
    + goto out_error;
    +
    + /*
    + * Find out how many branches we will have in the end, counting
    + * "add" and "del" commands. Copy the "options" string because
    + * strsep modifies the string and we need it later.
    + */
    + optionstmp = tmp_to_free = kstrdup(options, GFP_KERNEL);
    + if (!optionstmp) {
    + err = -ENOMEM;
    + goto out_free;
    + }
    + new_branches = cur_branches = sbmax(sb); /* current no. branches */
    + add_branches = del_branches = 0;
    + new_high_branch_id = sbhbid(sb); /* save current high_branch_id */
    + while ((optname = strsep(&optionstmp, ",")) != NULL) {
    + char *optarg;
    +
    + if (!optname || !*optname)
    + continue;
    +
    + optarg = strchr(optname, '=');
    + if (optarg)
    + *optarg++ = '\0';
    +
    + if (!strcmp("add", optname))
    + add_branches++;
    + else if (!strcmp("del", optname))
    + del_branches++;
    + }
    + kfree(tmp_to_free);
    + /* after all changes, will we have at least one branch left? */
    + if ((new_branches + add_branches - del_branches) < 1) {
    + printk(KERN_WARNING
    + "unionfs: no branches left after remount\n");
    + err = -EINVAL;
    + goto out_free;
    + }
    +
    + /*
    + * Since we haven't actually parsed all the add/del options, nor
    + * have we checked them for errors, we don't know for sure how many
    + * branches we will have after all changes have taken place. In
    + * fact, the total number of branches left could be less than what
    + * we have now. So we need to allocate space for a temporary
    + * placeholder that is at least as large as the maximum number of
    + * branches we *could* have, which is the current number plus all
    + * the additions. Once we're done with these temp placeholders, we
    + * may have to re-allocate the final size, copy over from the temp,
    + * and then free the temps (done near the end of this function).
    + */
    + max_branches = cur_branches + add_branches;
    + /* allocate space for new pointers to hidden dentry */
    + tmp_data = kcalloc(max_branches,
    + sizeof(struct unionfs_data), GFP_KERNEL);
    + if (!tmp_data) {
    + err = -ENOMEM;
    + goto out_free;
    + }
    + /* allocate space for new pointers to lower paths */
    + tmp_lower_paths = kcalloc(max_branches,
    + sizeof(struct path), GFP_KERNEL);
    + if (!tmp_lower_paths) {
    + err = -ENOMEM;
    + goto out_free;
    + }
    + /* copy current info into new placeholders, incrementing refcnts */
    + memcpy(tmp_data, UNIONFS_SB(sb)->data,
    + cur_branches * sizeof(struct unionfs_data));
    + memcpy(tmp_lower_paths, UNIONFS_D(sb->s_root)->lower_paths,
    + cur_branches * sizeof(struct path));
    + for (i=0; i<cur_branches; i++)
    + pathget(&tmp_lower_paths[i]); /* drop refs at end of fxn */
    +
    + /*******************************************************************
    + * For each branch command, do path_lookup on the requested branch,
    + * and apply the change to a temp branch list. To handle errors, we
    + * already dup'ed the old arrays (above), and increased the refcnts
    + * on various f/s objects. So now we can do all the path_lookups
    + * and branch-management commands on the new arrays. If it fail mid
    + * way, we free the tmp arrays and *put all objects. If we succeed,
    + * then we free old arrays and *put its objects, and then replace
    + * the arrays with the new tmp list (we may have to re-allocate the
    + * memory because the temp lists could have been larger than what we
    + * actually needed).
    + *******************************************************************/
    +
    + while ((optname = strsep(&options, ",")) != NULL) {
    + char *optarg;
    +
    + if (!optname || !*optname)
    + continue;
    + /*
    + * At this stage optname holds a comma-delimited option, but
    + * without the commas. Next, we need to break the string on
    + * the '=' symbol to separate CMD=ARG, where ARG itself can
    + * be KEY=VAL. For example, in mode=/foo=rw, CMD is "mode",
    + * KEY is "/foo", and VAL is "rw".
    + */
    + optarg = strchr(optname, '=');
    + if (optarg)
    + *optarg++ = '\0';
    + /* incgen remount option (instead of old ioctl) */
    + if (!strcmp("incgen", optname)) {
    + err = 0;
    + goto out_no_change;
    + }
    +
    + /*
    + * All of our options take an argument now. (Insert ones
    + * that don't above this check.) So at this stage optname
    + * contains the CMD part and optarg contains the ARG part.
    + */
    + if (!optarg || !*optarg) {
    + printk("unionfs: all remount options require "
    + "an argument (%s).\n", optname);
    + err = -EINVAL;
    + goto out_release;
    + }
    +
    + if (!strcmp("add", optname)) {
    + err = do_remount_add_option(optarg, new_branches,
    + tmp_data,
    + tmp_lower_paths,
    + &new_high_branch_id);
    + if (err)
    + goto out_release;
    + new_branches++;
    + if (new_branches > UNIONFS_MAX_BRANCHES) {
    + printk("unionfs: command exceeds %d branches\n",
    + UNIONFS_MAX_BRANCHES);
    + err = -E2BIG;
    + goto out_release;
    + }
    + continue;
    + }
    + if (!strcmp("del", optname)) {
    + err = do_remount_del_option(optarg, new_branches,
    + tmp_data,
    + tmp_lower_paths);
    + if (err)
    + goto out_release;
    + new_branches--;
    + continue;
    + }
    + if (!strcmp("mode", optname)) {
    + err = do_remount_mode_option(optarg, new_branches,
    + tmp_data,
    + tmp_lower_paths);
    + if (err)
    + goto out_release;
    + continue;
    + }
    +
    + /*
    + * When you use "mount -o remount,ro", mount(8) will
    + * reportedly pass the original dirs= string from
    + * /proc/mounts. So for now, we have to ignore dirs= and
    + * not consider it an error, unless we want to allow users
    + * to pass dirs= in remount. Note that to allow the VFS to
    + * actually process the ro/rw remount options, we have to
    + * return 0 from this function.
    + */
    + if (!strcmp("dirs", optname)) {
    + printk(KERN_WARNING
    + "unionfs: remount ignoring option \"%s\".\n",
    + optname);
    + continue;
    + }
    +
    + err = -EINVAL;
    + printk(KERN_WARNING
    + "unionfs: unrecognized option \"%s\"\n", optname);
    + goto out_release;
    + }
    +
    +out_no_change:
    +
    + /******************************************************************
    + * WE'RE ALMOST DONE: see if we need to allocate a small-sized new
    + * vector, copy the vectors to their correct place, release the
    + * refcnt of the older ones, and return.
    + * Also handle invalidating any pgaes that will have to be re-read.
    + *******************************************************************/
    +
    + /*
    + * Allocate space for actual pointers, if needed. By the time we
    + * finish this block of code, new_branches and new_lower_paths will
    + * have the correct size. None of this code below would be needed
    + * if the kernel had a realloc() function, at least one capable of
    + * shrinking/truncating an allocation's size (hint, hint).
    + */
    + if (new_branches < max_branches) {
    +
    + /* allocate space for new pointers to hidden dentry */
    + new_data = kcalloc(new_branches,
    + sizeof(struct unionfs_data), GFP_KERNEL);
    + if (!new_data) {
    + err = -ENOMEM;
    + goto out_release;
    + }
    + /* allocate space for new pointers to lower paths */
    + new_lower_paths = kcalloc(new_branches,
    + sizeof(struct path), GFP_KERNEL);
    + if (!new_lower_paths) {
    + err = -ENOMEM;
    + goto out_release;
    + }
    + /*
    + * copy current info into new placeholders, incrementing
    + * refcounts.
    + */
    + memcpy(new_data, tmp_data,
    + new_branches * sizeof(struct unionfs_data));
    + memcpy(new_lower_paths, tmp_lower_paths,
    + new_branches * sizeof(struct path));
    + /*
    + * Since we already hold various refcnts on the objects, we
    + * don't need to redo it here. Just free the older memory
    + * and re-point the pointers.
    + */
    + kfree(tmp_data);
    + kfree(tmp_lower_paths);
    + /* no need to nullify pointers here */
    + } else {
    + /* number of branches didn't change, no need to re-alloc */
    + new_data = tmp_data;
    + new_lower_paths = tmp_lower_paths;
    + }
    +
    + /*
    + * OK, just before we actually put the new set of branches in place,
    + * we need to ensure that our own f/s has no dirty objects left.
    + * Luckily, do_remount_sb() already calls shrink_dcache_sb(sb) and
    + * fsync_super(sb), taking care of dentries, inodes, and dirty
    + * pages. So all that's left is for us to invalidate any leftover
    + * (non-dirty) pages to ensure that they will be re-read from the
    + * new lower branches (and to support mmap).
    + */
    +
    + /*
    + * No we call drop_pagecache_sb() to invalidate all pages in this
    + * super. This function calls invalidate_inode_pages(mapping),
    + * which calls invalidate_mapping_pages(): the latter, however, will
    + * not invalidate pages which are dirty, locked, under writeback, or
    + * mapped into pagetables. We shouldn't have to worry about dirty
    + * or under-writeback pages, because do_remount_sb() called
    + * fsync_super() which would not have returned until all dirty pages
    + * were flushed.
    + *
    + * But do w have to worry about locked pages? Is there any chance
    + * that in here we'll get locked pages?
    + *
    + * XXX: what about pages mapped into pagetables? Are these pages
    + * which user processes may have mmap(2)'ed? If so, then we need to
    + * invalidate those too, no? Maybe we'll have to write our own
    + * version of invalidate_mapping_pages() which also handled mapped
    + * pages.
    + *
    + * XXX: Alternatively, maybe we should call truncate_inode_pages(),
    + * which use two passes over the pages list, and will truncate all
    + * pages.
    + */
    + drop_pagecache_sb(sb);
    +
    + /* copy new vectors into their correct place */
    + tmp_data = UNIONFS_SB(sb)->data;
    + UNIONFS_SB(sb)->data = new_data;
    + new_data = NULL; /* so don't free good pointers below */
    + tmp_lower_paths = UNIONFS_D(sb->s_root)->lower_paths;
    + UNIONFS_D(sb->s_root)->lower_paths = new_lower_paths;
    + new_lower_paths = NULL; /* so don't free good pointers below */
    +
    + /* update our unionfs_sb_info and root dentry index of last branch */
    + i = sbmax(sb); /* save no. of branches to release at end */
    + sbend(sb) = new_branches - 1;
    + set_dbend(sb->s_root, new_branches - 1);
    + UNIONFS_D(sb->s_root)->bcount = new_branches;
    + new_branches = i; /* no. of branches to release below */
    +
    + /* maxbytes may have changed */
    + sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;
    + /* update high branch ID */
    + sbhbid(sb) = new_high_branch_id;
    +
    + /* update our sb->generation for revalidating objects */
    + i = atomic_inc_return(&UNIONFS_SB(sb)->generation);
    + atomic_set(&UNIONFS_D(sb->s_root)->generation, i);
    + atomic_set(&UNIONFS_I(sb->s_root->d_inode)->generation, i);
    + printk("unionfs: new generation number %d\n", i);
    + err = 0; /* reset to success */
    +
    + /*
    + * The code above falls through to the next label, and releases the
    + * refcnts of the older ones (stored in tmp_*): if we fell through
    + * here, it means success. However, if we jump directly to this
    + * label from any error above, then an error occurred after we
    + * grabbed various refcnts, and so we have to release the
    + * temporarily constructed structures.
    + */
    +out_release:
    + /* no need to cleanup/release anything in tmp_data */
    + if (tmp_lower_paths)
    + for (i=0; i<new_branches; i++)
    + pathput(&tmp_lower_paths[i]);
    +out_free:
    + kfree(tmp_lower_paths);
    + kfree(tmp_data);
    + kfree(new_lower_paths);
    + kfree(new_data);
    +out_error:
    + unionfs_write_unlock(sb);
    + return err;
    }

    /*
    diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
    index 7bd6306..715a3ad 100644
    --- a/fs/unionfs/union.h
    +++ b/fs/unionfs/union.h
    @@ -60,6 +60,9 @@
    /* number of times we try to get a unique temporary file name */
    #define GET_TMPNAM_MAX_RETRY 5

    +/* maximum number of branches we support, to avoid memory blowup */
    +#define UNIONFS_MAX_BRANCHES 128
    +
    /* Operations vectors defined in specific files. */
    extern struct file_operations unionfs_main_fops;
    extern struct file_operations unionfs_dir_fops;
    @@ -408,6 +411,9 @@ static inline int is_robranch(const struct dentry *dentry)
    * EXTERNALS:
    */
    extern char *alloc_whname(const char *name, int len);
    +extern int check_branch(struct nameidata *nd);
    +extern int __parse_branch_mode(const char *name);
    +extern int parse_branch_mode(const char *name);

    /* These two functions are here because it is kind of daft to copy and paste the
    * contents of the two functions to 32+ places in unionfs
    --
    1.5.0.3.268.g3dda
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-04-09 17:03    [W:0.070 / U:30.740 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site