/* * create the new device/file/directory - use copyup_permission to copyup * times, and mode * * if the object being copied up is a regular file, the file is only created, * the contents have to be copied up separately */ static int __copyup_ndentry(struct dentry *old_lower_dentry, struct dentry *new_lower_dentry, struct dentry *new_lower_parent_dentry, char *symbuf) { int err = 0; umode_t old_mode = old_lower_dentry->d_inode->i_mode; struct sioq_args args; if (S_ISDIR(old_mode)) { args.mkdir.parent = new_lower_parent_dentry->d_inode; args.mkdir.dentry = new_lower_dentry; args.mkdir.mode = old_mode; run_sioq(__unionfs_mkdir, &args); err = args.err; } else if (S_ISLNK(old_mode)) { args.symlink.parent = new_lower_parent_dentry->d_inode; args.symlink.dentry = new_lower_dentry; args.symlink.symbuf = symbuf; run_sioq(__unionfs_symlink, &args); err = args.err; } else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) || S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) { args.mknod.parent = new_lower_parent_dentry->d_inode; args.mknod.dentry = new_lower_dentry; args.mknod.mode = old_mode; args.mknod.dev = old_lower_dentry->d_inode->i_rdev; run_sioq(__unionfs_mknod, &args); err = args.err; } else if (S_ISREG(old_mode)) { struct nameidata nd; err = init_lower_nd(&nd, LOOKUP_CREATE); if (unlikely(err < 0)) goto out; args.create.nd = &nd; args.create.parent = new_lower_parent_dentry->d_inode; args.create.dentry = new_lower_dentry; args.create.mode = old_mode; run_sioq(__unionfs_create, &args); err = args.err; release_lower_nd(&nd, err); } else { printk(KERN_CRIT "unionfs: unknown inode type %d\n", old_mode); BUG(); } out: return err; }
/* create the new device/file/directory - use copyup_permission to copyup * times, and mode * * if the object being copied up is a regular file, the file is only created, * the contents have to be copied up separately */ static int __copyup_ndentry(struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry, struct dentry *new_hidden_parent_dentry, char *symbuf) { int err = 0; umode_t old_mode = old_hidden_dentry->d_inode->i_mode; struct sioq_args args; if (S_ISDIR(old_mode)) { args.mkdir.parent = new_hidden_parent_dentry->d_inode; args.mkdir.dentry = new_hidden_dentry; args.mkdir.mode = old_mode; run_sioq(__unionfs_mkdir, &args); err = args.err; } else if (S_ISLNK(old_mode)) { args.symlink.parent = new_hidden_parent_dentry->d_inode; args.symlink.dentry = new_hidden_dentry; args.symlink.symbuf = symbuf; args.symlink.mode = old_mode; run_sioq(__unionfs_symlink, &args); err = args.err; } else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) || S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) { args.mknod.parent = new_hidden_parent_dentry->d_inode; args.mknod.dentry = new_hidden_dentry; args.mknod.mode = old_mode; args.mknod.dev = old_hidden_dentry->d_inode->i_rdev; run_sioq(__unionfs_mknod, &args); err = args.err; } else if (S_ISREG(old_mode)) { args.create.parent = new_hidden_parent_dentry->d_inode; args.create.dentry = new_hidden_dentry; args.create.mode = old_mode; args.create.nd = NULL; run_sioq(__unionfs_create, &args); err = args.err; } else { printk(KERN_ERR "Unknown inode type %d\n", old_mode); BUG(); } return err; }
/* The rest of these are utility functions for lookup. */ static int is_opaque_dir(struct dentry *dentry, int bindex) { int err = 0; struct dentry *hidden_dentry; struct dentry *wh_hidden_dentry; struct inode *hidden_inode; struct sioq_args args; hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex); hidden_inode = hidden_dentry->d_inode; BUG_ON(!S_ISDIR(hidden_inode->i_mode)); mutex_lock(&hidden_inode->i_mutex); if (!permission(hidden_inode, MAY_EXEC, NULL)) wh_hidden_dentry = lookup_one_len(UNIONFS_DIR_OPAQUE, hidden_dentry, sizeof(UNIONFS_DIR_OPAQUE) - 1); else { args.is_opaque.dentry = hidden_dentry; run_sioq(__is_opaque_dir, &args); wh_hidden_dentry = args.ret; } mutex_unlock(&hidden_inode->i_mutex); if (IS_ERR(wh_hidden_dentry)) { err = PTR_ERR(wh_hidden_dentry); goto out; } /* This is an opaque dir iff wh_hidden_dentry is positive */ err = !!wh_hidden_dentry->d_inode; dput(wh_hidden_dentry); out: return err; }
/* * This function replicates the directory structure up-to given dentry * in the bindex branch. */ struct dentry *create_parents(struct inode *dir, struct dentry *dentry, const char *name, int bindex) { int err; struct dentry *child_dentry; struct dentry *parent_dentry; struct dentry *lower_parent_dentry = NULL; struct dentry *lower_dentry = NULL; const char *childname; unsigned int childnamelen; int nr_dentry; int count = 0; int old_bstart; int old_bend; struct dentry **path = NULL; struct super_block *sb; verify_locked(dentry); err = is_robranch_super(dir->i_sb, bindex); if (err) { lower_dentry = ERR_PTR(err); goto out; } old_bstart = dbstart(dentry); old_bend = dbend(dentry); lower_dentry = ERR_PTR(-ENOMEM); /* There is no sense allocating any less than the minimum. */ nr_dentry = 1; path = kmalloc(nr_dentry * sizeof(struct dentry *), GFP_KERNEL); if (unlikely(!path)) goto out; /* assume the negative dentry of unionfs as the parent dentry */ parent_dentry = dentry; /* * This loop finds the first parent that exists in the given branch. * We start building the directory structure from there. At the end * of the loop, the following should hold: * - child_dentry is the first nonexistent child * - parent_dentry is the first existent parent * - path[0] is the = deepest child * - path[count] is the first child to create */ do { child_dentry = parent_dentry; /* find the parent directory dentry in unionfs */ parent_dentry = dget_parent(child_dentry); /* find out the lower_parent_dentry in the given branch */ lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); /* grow path table */ if (count == nr_dentry) { void *p; nr_dentry *= 2; p = krealloc(path, nr_dentry * sizeof(struct dentry *), GFP_KERNEL); if (unlikely(!p)) { lower_dentry = ERR_PTR(-ENOMEM); goto out; } path = p; } /* store the child dentry */ path[count++] = child_dentry; } while (!lower_parent_dentry); count--; sb = dentry->d_sb; /* * This code goes between the begin/end labels and basically * emulates a while(child_dentry != dentry), only cleaner and * shorter than what would be a much longer while loop. */ begin: /* get lower parent dir in the current branch */ lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); dput(parent_dentry); /* init the values to lookup */ childname = child_dentry->d_name.name; childnamelen = child_dentry->d_name.len; if (child_dentry != dentry) { /* lookup child in the underlying file system */ lower_dentry = lookup_one_len(childname, lower_parent_dentry, childnamelen); if (IS_ERR(lower_dentry)) goto out; } else { /* * Is the name a whiteout of the child name ? lookup the * whiteout child in the underlying file system */ lower_dentry = lookup_one_len(name, lower_parent_dentry, strlen(name)); if (IS_ERR(lower_dentry)) goto out; /* Replace the current dentry (if any) with the new one */ dput(unionfs_lower_dentry_idx(dentry, bindex)); unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); __cleanup_dentry(dentry, bindex, old_bstart, old_bend); goto out; } if (lower_dentry->d_inode) { /* * since this already exists we dput to avoid * multiple references on the same dentry */ dput(lower_dentry); } else { struct sioq_args args; /* it's a negative dentry, create a new dir */ lower_parent_dentry = lock_parent(lower_dentry); args.mkdir.parent = lower_parent_dentry->d_inode; args.mkdir.dentry = lower_dentry; args.mkdir.mode = child_dentry->d_inode->i_mode; run_sioq(__unionfs_mkdir, &args); err = args.err; if (!err) err = copyup_permissions(dir->i_sb, child_dentry, lower_dentry); unlock_dir(lower_parent_dentry); if (err) { dput(lower_dentry); lower_dentry = ERR_PTR(err); goto out; } } __set_inode(child_dentry, lower_dentry, bindex); __set_dentry(child_dentry, lower_dentry, bindex); /* * update times of this dentry, but also the parent, because if * we changed, the parent may have changed too. */ fsstack_copy_attr_times(parent_dentry->d_inode, lower_parent_dentry->d_inode); unionfs_copy_attr_times(child_dentry->d_inode); parent_dentry = child_dentry; child_dentry = path[--count]; goto begin; out: /* cleanup any leftover locks from the do/while loop above */ if (IS_ERR(lower_dentry)) while (count) dput(path[count--]); kfree(path); return lower_dentry; }
/* This function replicates the directory structure upto given dentry * in the bindex branch. */ static struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry, const char *name, int bindex) { int err; struct dentry *child_dentry; struct dentry *parent_dentry; struct dentry *hidden_parent_dentry = NULL; struct dentry *hidden_dentry = NULL; const char *childname; unsigned int childnamelen; int old_kmalloc_size; int kmalloc_size; int num_dentry; int count; int old_bstart; int old_bend; struct dentry **path = NULL; struct dentry **tmp_path; struct super_block *sb; verify_locked(dentry); /* There is no sense allocating any less than the minimum. */ kmalloc_size = malloc_sizes[0].cs_size; num_dentry = kmalloc_size / sizeof(struct dentry *); if ((err = is_robranch_super(dir->i_sb, bindex))) { hidden_dentry = ERR_PTR(err); goto out; } old_bstart = dbstart(dentry); old_bend = dbend(dentry); hidden_dentry = ERR_PTR(-ENOMEM); path = kzalloc(kmalloc_size, GFP_KERNEL); if (!path) goto out; /* assume the negative dentry of unionfs as the parent dentry */ parent_dentry = dentry; count = 0; /* This loop finds the first parent that exists in the given branch. * We start building the directory structure from there. At the end * of the loop, the following should hold: * - child_dentry is the first nonexistent child * - parent_dentry is the first existent parent * - path[0] is the = deepest child * - path[count] is the first child to create */ do { child_dentry = parent_dentry; /* find the parent directory dentry in unionfs */ parent_dentry = child_dentry->d_parent; unionfs_lock_dentry(parent_dentry); /* find out the hidden_parent_dentry in the given branch */ hidden_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); /* store the child dentry */ path[count++] = child_dentry; /* grow path table */ if (count == num_dentry) { old_kmalloc_size = kmalloc_size; kmalloc_size *= 2; num_dentry = kmalloc_size / sizeof(struct dentry *); tmp_path = kzalloc(kmalloc_size, GFP_KERNEL); if (!tmp_path) { hidden_dentry = ERR_PTR(-ENOMEM); goto out; } memcpy(tmp_path, path, old_kmalloc_size); kfree(path); path = tmp_path; tmp_path = NULL; } } while (!hidden_parent_dentry); count--; sb = dentry->d_sb; /* This is basically while(child_dentry != dentry). This loop is * horrible to follow and should be replaced with cleaner code. */ while (1) { /* get hidden parent dir in the current branch */ hidden_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); unionfs_unlock_dentry(parent_dentry); /* init the values to lookup */ childname = child_dentry->d_name.name; childnamelen = child_dentry->d_name.len; if (child_dentry != dentry) { /* lookup child in the underlying file system */ hidden_dentry = lookup_one_len(childname, hidden_parent_dentry, childnamelen); if (IS_ERR(hidden_dentry)) goto out; } else { /* is the name a whiteout of the childname ? * lookup the whiteout child in the underlying file system */ hidden_dentry = lookup_one_len(name, hidden_parent_dentry, strlen(name)); if (IS_ERR(hidden_dentry)) goto out; /* Replace the current dentry (if any) with the new one. */ dput(unionfs_lower_dentry_idx(dentry, bindex)); unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry); __cleanup_dentry(dentry, bindex, old_bstart, old_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 { struct sioq_args args; /* its a negative dentry, create a new dir */ hidden_parent_dentry = lock_parent(hidden_dentry); args.mkdir.parent = hidden_parent_dentry->d_inode; args.mkdir.dentry = hidden_dentry; args.mkdir.mode = child_dentry->d_inode->i_mode; run_sioq(__unionfs_mkdir, &args); err = args.err; if (!err) err = copyup_permissions(dir->i_sb, child_dentry, hidden_dentry); unlock_dir(hidden_parent_dentry); if (err) { dput(hidden_dentry); hidden_dentry = ERR_PTR(err); goto out; } } __set_inode(child_dentry, hidden_dentry, bindex); __set_dentry(child_dentry, hidden_dentry, bindex); parent_dentry = child_dentry; child_dentry = path[--count]; } out: kfree(path); return hidden_dentry; }