static void unionfs_fill_inode(struct dentry *dentry, struct inode *inode) { struct inode *lower_inode; struct dentry *lower_dentry; int bindex, bstart, bend; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) { unionfs_set_lower_inode_idx(inode, bindex, NULL); continue; } /* Initialize the lower inode to the new lower inode. */ if (!lower_dentry->d_inode) continue; unionfs_set_lower_inode_idx(inode, bindex, igrab(lower_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); /* Use attributes from the first branch. */ lower_inode = unionfs_lower_inode(inode); /* Use different set of inode ops for symlinks & directories */ if (S_ISLNK(lower_inode->i_mode)) inode->i_op = &unionfs_symlink_iops; else if (S_ISDIR(lower_inode->i_mode)) inode->i_op = &unionfs_dir_iops; /* Use different set of file ops for directories */ if (S_ISDIR(lower_inode->i_mode)) inode->i_fop = &unionfs_dir_fops; /* properly initialize special inodes */ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) init_special_inode(inode, lower_inode->i_mode, lower_inode->i_rdev); /* all well, copy inode attributes */ unionfs_copy_attr_all(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); }
/* * returns the right n_link value based on the inode type */ int unionfs_get_nlinks(const struct inode *inode) { /* don't bother to do all the work since we're unlinked */ if (inode->i_nlink == 0) return 0; if (!S_ISDIR(inode->i_mode)) return unionfs_lower_inode(inode)->i_nlink; /* * For directories, we return 1. The only place that could cares * about links is readdir, and there's d_type there so even that * doesn't matter. */ return 1; }
static int unionfs_setattr(struct dentry *dentry, struct iattr *ia) { int err = 0; struct dentry *lower_dentry; struct dentry *parent; struct inode *inode; struct inode *lower_inode; int bstart, bend, bindex; loff_t size; struct iattr lower_ia; /* check if user has permission to change inode */ err = inode_change_ok(dentry->d_inode, ia); if (err) goto out_err; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); if (unlikely(!__unionfs_d_revalidate(dentry, parent, false, 0))) { err = -ESTALE; goto out; } bstart = dbstart(dentry); bend = dbend(dentry); inode = dentry->d_inode; /* * mode change is for clearing setuid/setgid. Allow lower filesystem * to reinterpret it in its own way. */ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ia->ia_valid &= ~ATTR_MODE; lower_dentry = unionfs_lower_dentry(dentry); if (!lower_dentry) { /* should never happen after above revalidate */ err = -EINVAL; goto out; } /* * Get the lower inode directly from lower dentry, in case ibstart * is -1 (which happens when the file is open but unlinked. */ lower_inode = lower_dentry->d_inode; /* check if user has permission to change lower inode */ err = inode_change_ok(lower_inode, ia); if (err) goto out; /* copyup if the file is on a read only branch */ if (is_robranch_super(dentry->d_sb, bstart) || __is_rdonly(lower_inode)) { /* check if we have a branch to copy up to */ if (bstart <= 0) { err = -EACCES; goto out; } if (ia->ia_valid & ATTR_SIZE) size = ia->ia_size; else size = i_size_read(inode); /* copyup to next available branch */ for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_dentry(parent->d_inode, dentry, bstart, bindex, dentry->d_name.name, dentry->d_name.len, NULL, size); if (!err) break; } if (err) goto out; /* get updated lower_dentry/inode after copyup */ lower_dentry = unionfs_lower_dentry(dentry); lower_inode = unionfs_lower_inode(inode); /* * check for whiteouts in writeable branch, and remove them * if necessary. */ if (lower_dentry) { err = check_unlink_whiteout(dentry, lower_dentry, bindex); if (err > 0) /* ignore if whiteout found and removed */ err = 0; } } /* * If shrinking, first truncate upper level to cancel writing dirty * pages beyond the new eof; and also if its' maxbytes is more * limiting (fail with -EFBIG before making any change to the lower * level). There is no need to vmtruncate the upper level * afterwards in the other cases: we fsstack_copy_inode_size from * the lower level. */ if (ia->ia_valid & ATTR_SIZE) { err = inode_newsize_ok(inode, ia->ia_size); if (err) goto out; truncate_setsize(inode, ia->ia_size); } /* notify the (possibly copied-up) lower inode */ /* * Note: we use lower_dentry->d_inode, because lower_inode may be * unlinked (no inode->i_sb and i_ino==0. This happens if someone * tries to open(), unlink(), then ftruncate() a file. */ /* prepare our own lower struct iattr (with our own lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) { lower_ia.ia_file = unionfs_lower_file(ia->ia_file); BUG_ON(!lower_ia.ia_file); // XXX? } mutex_lock(&lower_dentry->d_inode->i_mutex); err = notify_change(lower_dentry, &lower_ia); mutex_unlock(&lower_dentry->d_inode->i_mutex); if (err) goto out; /* get attributes from the first lower inode */ if (ibstart(inode) >= 0) unionfs_copy_attr_all(inode, lower_inode); /* * unionfs_copy_attr_all will copy the lower times to our inode if * the lower ones are newer (useful for cache coherency). However, * ->setattr is the only place in which we may have to copy the * lower inode times absolutely, to support utimes(2). */ if (ia->ia_valid & ATTR_MTIME_SET) inode->i_mtime = lower_inode->i_mtime; if (ia->ia_valid & ATTR_CTIME) inode->i_ctime = lower_inode->i_ctime; if (ia->ia_valid & ATTR_ATIME_SET) inode->i_atime = lower_inode->i_atime; fsstack_copy_inode_size(inode, lower_inode); out: if (!err) unionfs_check_dentry(dentry); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); out_err: return err; }
/* * Copy up a dentry to a file of specified name. * * @dir: used to pull the ->i_sb to access other branches * @dentry: the non-negative dentry whose lower_inode we should copy * @bstart: the branch of the lower_inode to copy from * @new_bindex: the branch to create the new file in * @name: the name of the file to create * @namelen: length of @name * @copyup_file: the "struct file" to return (optional) * @len: how many bytes to copy-up? */ int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, const char *name, int namelen, struct file **copyup_file, loff_t len) { struct dentry *new_lower_dentry; struct dentry *old_lower_dentry = NULL; struct super_block *sb; int err = 0; int old_bindex; int old_bstart; int old_bend; struct dentry *new_lower_parent_dentry = NULL; mm_segment_t oldfs; char *symbuf = NULL; verify_locked(dentry); old_bindex = bstart; old_bstart = dbstart(dentry); old_bend = dbend(dentry); BUG_ON(new_bindex < 0); BUG_ON(new_bindex >= old_bindex); sb = dir->i_sb; err = is_robranch_super(sb, new_bindex); if (err) goto out; /* Create the directory structure above this dentry. */ new_lower_dentry = create_parents(dir, dentry, name, new_bindex); if (IS_ERR(new_lower_dentry)) { err = PTR_ERR(new_lower_dentry); goto out; } old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex); /* we conditionally dput this old_lower_dentry at end of function */ dget(old_lower_dentry); /* For symlinks, we must read the link before we lock the directory. */ if (S_ISLNK(old_lower_dentry->d_inode->i_mode)) { symbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (unlikely(!symbuf)) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); err = -ENOMEM; goto out_free; } oldfs = get_fs(); set_fs(KERNEL_DS); err = old_lower_dentry->d_inode->i_op->readlink( old_lower_dentry, (char __user *)symbuf, PATH_MAX); set_fs(oldfs); if (err < 0) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); goto out_free; } symbuf[err] = '\0'; } /* Now we lock the parent, and create the object in the new branch. */ new_lower_parent_dentry = lock_parent(new_lower_dentry); /* create the new inode */ err = __copyup_ndentry(old_lower_dentry, new_lower_dentry, new_lower_parent_dentry, symbuf); if (err) { __clear(dentry, old_lower_dentry, old_bstart, old_bend, new_lower_dentry, new_bindex); goto out_unlock; } /* We actually copyup the file here. */ if (S_ISREG(old_lower_dentry->d_inode->i_mode)) err = __copyup_reg_data(dentry, new_lower_dentry, new_bindex, old_lower_dentry, old_bindex, copyup_file, len); if (err) goto out_unlink; /* Set permissions. */ err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry); if (err) goto out_unlink; #ifdef CONFIG_UNION_FS_XATTR /* Selinux uses extended attributes for permissions. */ err = copyup_xattrs(old_lower_dentry, new_lower_dentry); if (err) goto out_unlink; #endif /* CONFIG_UNION_FS_XATTR */ /* do not allow files getting deleted to be re-interposed */ if (!d_deleted(dentry)) unionfs_reinterpose(dentry); goto out_unlock; out_unlink: /* * copyup failed, because we possibly ran out of space or * quota, or something else happened so let's unlink; we don't * really care about the return value of vfs_unlink */ vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry); if (copyup_file) { /* need to close the file */ fput(*copyup_file); branchput(sb, new_bindex); } /* * TODO: should we reset the error to something like -EIO? * * If we don't reset, the user may get some nonsensical errors, but * on the other hand, if we reset to EIO, we guarantee that the user * will get a "confusing" error message. */ out_unlock: unlock_dir(new_lower_parent_dentry); out_free: /* * If old_lower_dentry was not a file, then we need to dput it. If * it was a file, then it was already dput indirectly by other * functions we call above which operate on regular files. */ if (old_lower_dentry && old_lower_dentry->d_inode && !S_ISREG(old_lower_dentry->d_inode->i_mode)) dput(old_lower_dentry); kfree(symbuf); if (err) { /* * if directory creation succeeded, but inode copyup failed, * then purge new dentries. */ if (dbstart(dentry) < old_bstart && ibstart(dentry->d_inode) > dbstart(dentry)) __clear(dentry, NULL, old_bstart, old_bend, unionfs_lower_dentry(dentry), dbstart(dentry)); goto out; } if (!S_ISDIR(dentry->d_inode->i_mode)) { unionfs_postcopyup_release(dentry); if (!unionfs_lower_inode(dentry->d_inode)) { /* * If we got here, then we copied up to an * unlinked-open file, whose name is .unionfsXXXXX. */ struct inode *inode = new_lower_dentry->d_inode; atomic_inc(&inode->i_count); unionfs_set_lower_inode_idx(dentry->d_inode, ibstart(dentry->d_inode), inode); } } unionfs_postcopyup_setmnt(dentry); /* sync inode times from copied-up inode to our inode */ unionfs_copy_attr_times(dentry->d_inode); unionfs_check_inode(dir); unionfs_check_dentry(dentry); out: return err; }
/* * The locking rules in unionfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *wh_dentry; struct dentry *old_parent, *new_parent; int valid = true; unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); old_parent = dget_parent(old_dentry); new_parent = dget_parent(new_dentry); /* un/lock parent dentries only if they differ from old/new_dentry */ if (old_parent != old_dentry && old_parent != new_dentry) unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT); if (new_parent != old_dentry && new_parent != new_dentry && new_parent != old_parent) unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD); unionfs_double_lock_dentry(old_dentry, new_dentry); valid = __unionfs_d_revalidate(old_dentry, old_parent, false); if (!valid) { err = -ESTALE; goto out; } if (!d_deleted(new_dentry) && new_dentry->d_inode) { valid = __unionfs_d_revalidate(new_dentry, new_parent, false); if (!valid) { err = -ESTALE; goto out; } } if (!S_ISDIR(old_dentry->d_inode->i_mode)) err = unionfs_partial_lookup(old_dentry, old_parent); else err = may_rename_dir(old_dentry, old_parent); if (err) goto out; err = unionfs_partial_lookup(new_dentry, new_parent); if (err) goto out; /* * if new_dentry is already lower because of whiteout, * simply override it even if the whited-out dir is not empty. */ wh_dentry = find_first_whiteout(new_dentry); if (!IS_ERR(wh_dentry)) { dput(wh_dentry); } else if (new_dentry->d_inode) { if (S_ISDIR(old_dentry->d_inode->i_mode) != S_ISDIR(new_dentry->d_inode->i_mode)) { err = S_ISDIR(old_dentry->d_inode->i_mode) ? -ENOTDIR : -EISDIR; goto out; } if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct unionfs_dir_state *namelist = NULL; /* check if this unionfs directory is empty or not */ err = check_empty(new_dentry, new_parent, &namelist); if (err) goto out; if (!is_robranch(new_dentry)) err = delete_whiteouts(new_dentry, dbstart(new_dentry), namelist); free_rdstate(namelist); if (err) goto out; } } err = do_unionfs_rename(old_dir, old_dentry, old_parent, new_dir, new_dentry, new_parent); if (err) goto out; /* * force re-lookup since the dir on ro branch is not renamed, and * lower dentries still indicate the un-renamed ones. */ if (S_ISDIR(old_dentry->d_inode->i_mode)) atomic_dec(&UNIONFS_D(old_dentry)->generation); else unionfs_postcopyup_release(old_dentry); if (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) { unionfs_postcopyup_release(new_dentry); unionfs_postcopyup_setmnt(new_dentry); if (!unionfs_lower_inode(new_dentry->d_inode)) { /* * If we get here, it means that no copyup was * needed, and that a file by the old name already * existing on the destination branch; that file got * renamed earlier in this function, so all we need * to do here is set the lower inode. */ struct inode *inode; inode = unionfs_lower_inode(old_dentry->d_inode); igrab(inode); unionfs_set_lower_inode_idx(new_dentry->d_inode, dbstart(new_dentry), inode); } } /* if all of this renaming succeeded, update our times */ unionfs_copy_attr_times(old_dentry->d_inode); unionfs_copy_attr_times(new_dentry->d_inode); unionfs_check_inode(old_dir); unionfs_check_inode(new_dir); unionfs_check_dentry(old_dentry); unionfs_check_dentry(new_dentry); out: if (err) /* clear the new_dentry stuff created */ d_drop(new_dentry); unionfs_double_unlock_dentry(old_dentry, new_dentry); if (new_parent != old_dentry && new_parent != new_dentry && new_parent != old_parent) unionfs_unlock_dentry(new_parent); if (old_parent != old_dentry && old_parent != new_dentry) unionfs_unlock_dentry(old_parent); dput(new_parent); dput(old_parent); unionfs_read_unlock(old_dentry->d_sb); return err; }
/* * returns 1 if valid, 0 otherwise. */ int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid = 1; /* default is valid (1); invalid is 0. */ struct dentry *hidden_dentry; int bindex, bstart, bend; int sbgen, dgen; int positive = 0; int locked = 0; int restart = 0; int interpose_flag; struct nameidata lowernd; /* TODO: be gentler to the stack */ if (nd) memcpy(&lowernd, nd, sizeof(struct nameidata)); else memset(&lowernd, 0, sizeof(struct nameidata)); restart: verify_locked(dentry); /* if the dentry is unhashed, do NOT revalidate */ if (d_deleted(dentry)) { printk(KERN_DEBUG "unhashed dentry being revalidated: %*s\n", dentry->d_name.len, dentry->d_name.name); goto out; } BUG_ON(dbstart(dentry) == -1); if (dentry->d_inode) positive = 1; dgen = atomic_read(&UNIONFS_D(dentry)->generation); sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation); /* If we are working on an unconnected dentry, then there is no * revalidation to be done, because this file does not exist within the * namespace, and Unionfs operates on the namespace, not data. */ if (sbgen != dgen) { struct dentry *result; int pdgen; unionfs_read_lock(dentry->d_sb); locked = 1; /* The root entry should always be valid */ BUG_ON(IS_ROOT(dentry)); /* We can't work correctly if our parent isn't valid. */ pdgen = atomic_read(&UNIONFS_D(dentry->d_parent)->generation); if (!restart && (pdgen != sbgen)) { unionfs_read_unlock(dentry->d_sb); locked = 0; /* We must be locked before our parent. */ if (! (dentry->d_parent->d_op-> d_revalidate(dentry->d_parent, nd))) { valid = 0; goto out; } restart = 1; goto restart; } BUG_ON(pdgen != sbgen); /* Free the pointers for our inodes and this dentry. */ bstart = dbstart(dentry); bend = dbend(dentry); if (bstart >= 0) { struct dentry *hidden_dentry; for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex); dput(hidden_dentry); } } set_dbstart(dentry, -1); set_dbend(dentry, -1); interpose_flag = INTERPOSE_REVAL_NEG; if (positive) { interpose_flag = INTERPOSE_REVAL; mutex_lock(&dentry->d_inode->i_mutex); bstart = ibstart(dentry->d_inode); bend = ibend(dentry->d_inode); if (bstart >= 0) { struct inode *hidden_inode; for (bindex = bstart; bindex <= bend; bindex++) { hidden_inode = unionfs_lower_inode_idx(dentry->d_inode, bindex); iput(hidden_inode); } } kfree(UNIONFS_I(dentry->d_inode)->lower_inodes); UNIONFS_I(dentry->d_inode)->lower_inodes = NULL; ibstart(dentry->d_inode) = -1; ibend(dentry->d_inode) = -1; mutex_unlock(&dentry->d_inode->i_mutex); } result = unionfs_lookup_backend(dentry, &lowernd, interpose_flag); if (result) { if (IS_ERR(result)) { valid = 0; goto out; } /* current unionfs_lookup_backend() doesn't return * a valid dentry */ dput(dentry); dentry = result; } if (positive && UNIONFS_I(dentry->d_inode)->stale) { make_bad_inode(dentry->d_inode); d_drop(dentry); valid = 0; goto out; } goto out; } /* The revalidation must occur across all branches */ bstart = dbstart(dentry); bend = dbend(dentry); BUG_ON(bstart == -1); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!hidden_dentry || !hidden_dentry->d_op || !hidden_dentry->d_op->d_revalidate) continue; if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd)) valid = 0; } if (!dentry->d_inode) valid = 0; if (valid) { fsstack_copy_attr_all(dentry->d_inode, unionfs_lower_inode(dentry->d_inode), unionfs_get_nlinks); fsstack_copy_inode_size(dentry->d_inode, unionfs_lower_inode(dentry->d_inode)); } out: if (locked) unionfs_read_unlock(dentry->d_sb); return valid; }