Logo Search packages:      
Sourcecode: unionfs version File versions

copyup.c

/*
 * Copyright (c) 2003-2005 Erez Zadok
 * Copyright (c) 2003-2005 Charles P. Wright
 * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
 * Copyright (c) 2003-2005 Puja Gupta
 * Copyright (c) 2003-2005 Harikesavan Krishnan
 * Copyright (c) 2003-2005 Stony Brook University
 * Copyright (c) 2003-2005 The Research Foundation of State University of New York
 *
 * For specific licensing information, see the COPYING file distributed with
 * this package.
 *
 * This Copyright notice must be kept intact and distributed with all sources.
 */
/*
 *  $Id: copyup.c,v 1.17 2005/03/02 15:10:40 cwright Exp $
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include "fist.h"
#include "unionfs.h"

#if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20))
/*Not Working Yet*/
static int unionfs_copyup_xattrs(struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry) {
      int err = 0;
      ssize_t list_size = -1;
      char *name_list = NULL;
      char *attr_value = NULL;

      print_entry_location();

      PASSERT(old_hidden_dentry);
      PASSERT(old_hidden_dentry->d_inode);
      PASSERT(old_hidden_dentry->d_inode->i_op);
      PASSERT(new_hidden_dentry);
      PASSERT(new_hidden_dentry->d_inode);
      PASSERT(new_hidden_dentry->d_inode->i_op);

      if(!old_hidden_dentry->d_inode->i_op->getxattr ||
                  !old_hidden_dentry->d_inode->i_op->listxattr ||
                  !new_hidden_dentry->d_inode->i_op->setxattr) {
            err = -ENOTSUPP;
            goto out;
      }

      list_size = old_hidden_dentry->d_inode->i_op->listxattr(old_hidden_dentry,NULL,0);
      if (list_size <= 0) {
            err = list_size;
            goto out;
      }

      name_list = xattr_alloc(list_size + 1, XATTR_LIST_MAX);
      if (!name_list || IS_ERR(name_list)) {
            err = PTR_ERR(name_list);
            goto out;
      }
      list_size = old_hidden_dentry->d_inode->i_op->listxattr(old_hidden_dentry,name_list,list_size);

      attr_value = xattr_alloc(XATTR_SIZE_MAX,XATTR_SIZE_MAX);
      if (!attr_value || IS_ERR(attr_value)) {
            err = PTR_ERR(name_list);
            goto out;
      }

      while(*name_list) {
            size_t size;
            down(&old_hidden_dentry->d_inode->i_sem);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
            err = security_inode_getxattr(old_hidden_dentry, name_list);
            if (!err)
#endif
            size = old_hidden_dentry->d_inode->i_op->getxattr(old_hidden_dentry,name_list,attr_value, XATTR_SIZE_MAX);
            up(&old_hidden_dentry->d_inode->i_sem);
            if(size < 0) {
                  err = size;
                  goto out;
            }

            if(size > XATTR_SIZE_MAX) {
                  err = -E2BIG;
                  goto out;
            }

            down(&new_hidden_dentry->d_inode->i_sem);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
            err = security_inode_setxattr(old_hidden_dentry, name_list, attr_value, size, XATTR_CREATE);
            if (!err) {
#endif
                  err = new_hidden_dentry->d_inode->i_op->setxattr(new_hidden_dentry,name_list,attr_value, size, XATTR_CREATE);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
                  if (!err)
                        security_inode_post_setxattr(old_hidden_dentry, name_list, attr_value, size, XATTR_CREATE);
            }
#endif
            up(&new_hidden_dentry->d_inode->i_sem);
            if(err < 0)
                  goto out;

            name_list += strlen(name_list) + 1;
      }

out:
      if(name_list)
            xattr_free(name_list, XATTR_LIST_MAX);
      if(attr_value)
            xattr_free(attr_value, XATTR_SIZE_MAX);
      /* It is no big deal if this fails, we just roll with the punches. */
      if (err == -ENOTSUPP) {
            err = 0;
      }
      return err;
}
#endif

/* Determine the mode based on the copyup flags, and the existing dentry. */
static int unionfs_copyup_permissions(struct super_block *sb, struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry) {
      struct iattr newattrs;
      int err;

      print_entry_location();

      newattrs.ia_valid = ATTR_CTIME;
      if (IS_SET(sb, COPYUP_FS_MOUNTER)) {
            /* f/s mounter */
            newattrs.ia_mode = stopd(sb)->copyupmode;
            newattrs.ia_valid |= ATTR_FORCE | ATTR_MODE;
            if (stopd(sb)->copyupuid != -1) {
                  newattrs.ia_uid = stopd(sb)->copyupuid;
                  newattrs.ia_valid |= ATTR_UID;
            }
            if (stopd(sb)->copyupgid != -1) {
                  newattrs.ia_gid = stopd(sb)->copyupgid;
                  newattrs.ia_valid |= ATTR_GID;
            }
      } else if (IS_SET(sb, COPYUP_CURRENT_USER)) {
            /* current file permission */
            newattrs.ia_mode = ~current->fs->umask & S_IRWXUGO;
            newattrs.ia_valid |= ATTR_FORCE | ATTR_MODE;
      } else {
            /* original mode of old file */
            newattrs.ia_mode = old_hidden_dentry->d_inode->i_mode;
            newattrs.ia_gid = old_hidden_dentry->d_inode->i_gid;
            newattrs.ia_uid = old_hidden_dentry->d_inode->i_uid;
            newattrs.ia_valid |= ATTR_FORCE | ATTR_GID | ATTR_UID | ATTR_MODE;
      }
      if (newattrs.ia_valid & ATTR_MODE) {
            newattrs.ia_mode = (newattrs.ia_mode & S_IALLUGO) | (old_hidden_dentry->d_inode->i_mode & ~S_IALLUGO);
      }

      err = notify_change(new_hidden_dentry, &newattrs);

      print_exit_status(err);
      return err;
}

STATIC int
unionfs_copyup_dentry_len(inode_t *dir, dentry_t *dentry, int bstart, int new_bindex, file_t **copyup_file, int len)
{
    dentry_t *new_hidden_dentry;
    dentry_t *old_hidden_dentry = NULL;
    super_block_t *sb;
    file_t *input_file = NULL;
    file_t *output_file = NULL;
    ssize_t read_bytes, write_bytes;
    mm_segment_t old_fs;
    int err = 0;
    char *buf;
    int old_bindex;
    int got_branch_input = -1;
    int got_branch_output = -1;
    int old_bstart;
    int old_bend;
    int size = len;
    struct dentry *new_hidden_parent_dentry;
    mm_segment_t oldfs;
    char *symbuf = NULL;
    uid_t saved_uid = current->fsuid;
    gid_t saved_gid = current->fsgid;

    print_entry_location();

    fist_print_dentry("IN: unionfs_copyup_dentry_len: ", dentry);

    dget(dentry);
    old_bindex = bstart;
    old_bstart = dbstart(dentry);
    old_bend = dbend(dentry);

    ASSERT(new_bindex >= 0);
    ASSERT(new_bindex < old_bindex);
    PASSERT(dir);
    PASSERT(dentry);

    sb = dir->i_sb;

      if((err = is_robranch_super(sb, new_bindex)))
            goto out;

      /* Create the directory structure above this dentry. */
      new_hidden_dentry = unionfs_create_dirs(dir, dentry, new_bindex);
      PASSERT(new_hidden_dentry);
      if (IS_ERR(new_hidden_dentry)) {
            err = PTR_ERR(new_hidden_dentry);
            goto out;
      }

    /* Now we actually create the object. */
    old_hidden_dentry = dtohd_index(dentry, old_bindex);
    PASSERT(old_hidden_dentry);
    PASSERT(old_hidden_dentry->d_inode);
    dget(old_hidden_dentry);

    /* For symlinks only, we have to read the link before we lock the directory. */
      if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) {
            PASSERT(old_hidden_dentry->d_inode->i_op);
            PASSERT(old_hidden_dentry->d_inode->i_op->readlink);

            symbuf = KMALLOC(PATH_MAX, GFP_UNIONFS);
            if (!symbuf) {
                  err = -ENOMEM;
                  goto copyup_readlink_err;
            }

            oldfs = get_fs();
            set_fs(KERNEL_DS);
            err = old_hidden_dentry->d_inode->i_op->readlink(old_hidden_dentry, symbuf, PATH_MAX);
            set_fs(oldfs);
            if (err < 0) {
                  goto copyup_readlink_err;
            }
            symbuf[err] = '\0';
      }

    /* Now we lock the parent, and create the object in the new branch. */
    new_hidden_parent_dentry = lock_parent(new_hidden_dentry);
    current->fsuid = new_hidden_parent_dentry->d_inode->i_uid;
    current->fsgid = new_hidden_parent_dentry->d_inode->i_gid;
      if (S_ISDIR(old_hidden_dentry->d_inode->i_mode)) {
            err = vfs_mkdir(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU);
      } else if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
            err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf);
#else
            err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry,symbuf,S_IRWXU);
#endif
      } else if (S_ISBLK(old_hidden_dentry->d_inode->i_mode) || S_ISCHR(old_hidden_dentry->d_inode->i_mode) || S_ISFIFO(old_hidden_dentry->d_inode->i_mode) || S_ISSOCK(old_hidden_dentry->d_inode->i_mode)) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
            err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, kdev_t_to_nr(old_hidden_dentry->d_inode->i_rdev));
#else
            err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode,old_hidden_dentry->d_inode->i_rdev);
#endif
      } else if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) {
            //DQ: number of params changes in 2.6
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
            err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU);
#else
            err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU, NULL);
#endif
      } else {
            char diemsg[100];
            snprintf(diemsg, sizeof(diemsg), "Unknown inode type %d\n", old_hidden_dentry->d_inode->i_mode);
            FISTBUG(diemsg);
      }
    current->fsuid = saved_uid;
    current->fsgid = saved_gid;
    unlock_dir(new_hidden_parent_dentry);
copyup_readlink_err:
      if (symbuf) {
            KFREE(symbuf);
      }
      if (err) {
            /* get rid of the hidden dentry and all its traces */
            dput(new_hidden_dentry);
            set_dtohd_index(dentry, new_bindex, NULL);
            set_dbstart(dentry, old_bstart);
            set_dbend(dentry, old_bend);
            goto out;
      }

    /* We actually copyup the file here. */
      if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) {
            mntget(stohiddenmnt_index(sb, old_bindex));
            branchget(sb, old_bindex);
            got_branch_input = old_bindex;
            input_file = dentry_open(old_hidden_dentry, stohiddenmnt_index(sb, old_bindex), O_RDONLY);
            if (IS_ERR(input_file)) {
                  err = PTR_ERR(input_file);
                  goto out;
            }
            if (!input_file->f_op || !input_file->f_op->read) {
                  err = -EINVAL;
                  goto out;
            }

            /* copy the new file */
            dget(new_hidden_dentry);
            mntget(stohiddenmnt_index(sb, new_bindex));
            branchget(sb, new_bindex);
            got_branch_output = new_bindex;
            output_file = dentry_open(new_hidden_dentry, stohiddenmnt_index(sb, new_bindex), O_WRONLY);
            if (IS_ERR(output_file)) {
                  err = PTR_ERR(output_file);
                  goto out;
            }
            if (!output_file->f_op || !output_file->f_op->write) {
                  err = -EINVAL;
                  goto out;
            }

            /* allocating a buffer */
            buf = (char *) KMALLOC(PAGE_SIZE, GFP_UNIONFS);
            if (!buf) {
                  err = -ENOMEM;
                  goto out;
            }

            /* now read PAGE_SIZE bytes from offset 0 in a loop*/
            old_fs = get_fs();

            input_file->f_pos = 0;
            output_file->f_pos = 0;

            set_fs(KERNEL_DS);
            do {
                  if (len >= PAGE_SIZE) {
                        size = PAGE_SIZE;
                  } else if ((len < PAGE_SIZE) && (len > 0)) {
                        size = len;
                  }

                  len -= PAGE_SIZE;

                  read_bytes = input_file->f_op->read(input_file, buf, size, &input_file->f_pos);
                  if (read_bytes <= 0) {
                        err = read_bytes;
                        break;
                  }

                  write_bytes = output_file->f_op->write(output_file, buf, read_bytes, &output_file->f_pos);
                  if (write_bytes < 0 || (write_bytes < read_bytes)) {
                        err = -EIO;
                        break;
                  }
            } while ((read_bytes > 0) && (len > 0));
            set_fs(old_fs);
            KFREE(buf);
      }

    /* Set permissions. */
      if ((err = unionfs_copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) {
            goto out;
      }
      /* Selinux uses extended attributes for permissions. */
#if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20))
      if ((err = unionfs_copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) {
            goto out;
      }
#endif

    unionfs_reinterpose(dentry);

 out:
    if (input_file && !IS_ERR(input_file)) {
      fput(input_file);
    } else {
        /* since input file was not opened, we need to explicitly
         * dput the old_hidden_dentry
         */
        if (old_hidden_dentry) {
         dput(old_hidden_dentry);
      }
    }

    /* in any case, we have to branchput */
    if (got_branch_input >= 0) {
        branchput(sb, got_branch_input);
    }

    if (output_file) {
      if (copyup_file && !err) {
          *copyup_file = output_file;
      } else {
            fput(output_file);
            branchput(sb, got_branch_output);
      }
    }

    dput(dentry);
    fist_print_dentry("OUT: unionfs_copyup_dentry_len: ", dentry);
    fist_print_inode("OUT: unionfs_copyup_dentry_len: ", dentry->d_inode);

    print_exit_status(err);
    return err;
}

/* This function creates a copy of a file represented by 'file' which currently
 * resides in branch 'bstart' to branch 'new_bindex.
 */
STATIC int
unionfs_copyup_file(inode_t *dir, file_t *file, int bstart, int new_bindex, int len)
{
    int err = 0;
    file_t *output_file = NULL;

    print_entry_location();

    err = unionfs_copyup_dentry_len(dir, file->f_dentry, bstart, new_bindex, &output_file, len);
    if (!err) {
        fbstart(file) = new_bindex;
        ftohf_index(file, new_bindex) = output_file;
    }

    print_exit_status(err);
    return err;
}


/* This function replicates the directory structure upto given dentry
 * in the bindex branch. Can create directory structure recursively to the right
 * also.
 */
struct dentry *unionfs_create_dirs(inode_t *dir, dentry_t *dentry, int bindex)
{
      int err;
      dentry_t *child_dentry;
      dentry_t *parent_dentry;
      dentry_t *hidden_parent_dentry = NULL;
      dentry_t *hidden_dentry = NULL;

      const char *name;
      unsigned int namelen;

      dentry_t *path[MAX_DIR_CREATE];
      int count;

      int old_bstart;
      int old_bend;

      print_entry_location();

      if ((err = is_robranch_super(dir->i_sb, bindex))) {
            hidden_dentry = ERR_PTR(err);
            goto out;
      }

      fist_print_dentry("IN: unionfs_create_dirs: ", dentry);

      old_bstart = dbstart(dentry);
      old_bend = dbend(dentry);

      memset(path, 0, sizeof(dentry_t *) * MAX_DIR_CREATE);

      /* assume the negative dentry of unionfs as the parent dentry */
      parent_dentry = dentry;

      count = 0;
      /* find out in the current branch the parent directory from
       * where we need to start building a new directory structure
       */
      do {
            child_dentry = parent_dentry;

            /* find the parent directory dentry in unionfs */
            parent_dentry = child_dentry->d_parent;

            /* find out the hidden_parent_dentry in the given branch */
            hidden_parent_dentry = dtohd_index(parent_dentry, bindex);

            /* store the child dentry */
            path[count++] = child_dentry;

      } while(!hidden_parent_dentry);
      count--;

      while (1) {
            PASSERT(child_dentry);
            PASSERT(parent_dentry);
            PASSERT(parent_dentry->d_inode);

            // get hidden parent dir in the current branch
            hidden_parent_dentry = dtohd_index(parent_dentry, bindex);
            PASSERT(hidden_parent_dentry);
            PASSERT(hidden_parent_dentry->d_inode);

            // init the values to lookup
            name = child_dentry->d_name.name;
            namelen = child_dentry->d_name.len;

            // lookup child in the underlying file system
            hidden_dentry = lookup_one_len(name, hidden_parent_dentry, namelen);
            if (!hidden_dentry || IS_ERR(hidden_dentry)) {
                  printk("ERR from hidden_dentry!!!\n");
                  goto out;
            }

            if (child_dentry == dentry) {
                  int loop_start;
                  int loop_end;
                  int new_bstart = -1;
                  int new_bend = -1;
                  int i;

                  /* initialize the new dentry */
                  set_dtohd_index(dentry, bindex, hidden_dentry);

                  loop_start = (old_bstart < bindex) ? old_bstart : bindex;
                  loop_end = (old_bend > bindex) ? 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 (the newly looked dentry
                   */
                  for ( i = loop_start; i <= loop_end; i++) {

                        if (!dtohd_index(dentry, i)) {
                              continue;
                        }

                        if ( i == bindex) {
                              new_bend = i;
                              if (new_bstart < 0) {
                                    new_bstart = i;
                              }
                              continue;
                        }

                        if (!dtohd_index(dentry, i)->d_inode) {
                              dput(dtohd_index(dentry, i));
                              set_dtohd_index(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;
                  }
                  set_dbstart(dentry, new_bstart);
                  set_dbend(dentry, new_bend);
                  break;
            }

            if (hidden_dentry->d_inode) {
                  /* since this already exists we dput to avoid
                   * multiple references on the same dentry
                   */
                  dput(hidden_dentry);
            }
            else {
                  uid_t saved_uid = current->fsuid;
                  gid_t saved_gid = current->fsgid;

                  /* its a negative dentry, create a new dir */
                  hidden_parent_dentry = lock_parent(hidden_dentry);
                  current->fsuid = hidden_parent_dentry->d_inode->i_uid;
                  current->fsgid = hidden_parent_dentry->d_inode->i_gid;
                  err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, S_IRWXUGO);
                  current->fsuid = saved_uid;
                  current->fsgid = saved_gid;
                  unlock_dir(hidden_parent_dentry);
                  if (err || !hidden_dentry->d_inode) {
                        dput(hidden_dentry);
                        hidden_dentry = ERR_PTR(err);
                        goto out;
                  }
                  err = unionfs_copyup_permissions(dir->i_sb,child_dentry,hidden_dentry);
                  itohi_index(child_dentry->d_inode, bindex) = igrab(hidden_dentry->d_inode);
                  if (ibstart(child_dentry->d_inode) > bindex) {
                        ibstart(child_dentry->d_inode) = bindex;
                  }
                  if (ibend(child_dentry->d_inode) < bindex) {
                        ibend(child_dentry->d_inode) = bindex;
                  }

                  set_dtohd_index(child_dentry, bindex,  hidden_dentry);
                  if (dbstart(child_dentry) > bindex) {
                        set_dbstart(child_dentry, bindex);
                  }
                  if (dbend(child_dentry) < bindex) {
                        set_dbend(child_dentry,  bindex);
                  }
            }

            parent_dentry = child_dentry;
            child_dentry = path[--count];
      }
out:
      fist_print_dentry("OUT: unionfs_create_dirs: ", dentry);
      print_exit_pointer(hidden_dentry);
      return hidden_dentry;
}

Generated by  Doxygen 1.6.0   Back to index