* but not unhashed dentries.
*/
if (!d_deleted(dentry) &&
- !__unionfs_d_revalidate_chain(dentry, NULL)) {
+ !__unionfs_d_revalidate_chain(dentry, NULL, willwrite)) {
err = -ESTALE;
goto out_nofree;
}
out_nofree:
unionfs_read_unlock(inode->i_sb);
unionfs_check_inode(inode);
- unionfs_check_file(file);
+ if (!err) {
+ unionfs_check_file(file);
+ unionfs_check_dentry(file->f_dentry->d_parent);
+ }
return err;
}
int bindex, bstart, bend;
int fgen, err = 0;
- unionfs_check_file(file);
unionfs_read_lock(sb);
/*
* Yes, we have to revalidate this file even if it's being released.
*/
if ((err = unionfs_file_revalidate(file, 1)))
goto out;
+ unionfs_check_file(file);
fileinfo = UNIONFS_F(file);
BUG_ON(file->f_dentry->d_inode != inode);
inodeinfo = UNIONFS_I(inode);
struct dentry *dentry = file->f_dentry;
int bindex, bstart, bend;
- unionfs_read_lock(file->f_dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb);
if ((err = unionfs_file_revalidate(file, 1)))
goto out;
}
+ /* on success, update our times */
+ unionfs_copy_attr_times(dentry->d_inode);
+ /* parent time could have changed too (async) */
+ unionfs_copy_attr_times(dentry->d_parent->d_inode);
+
out_lock:
unionfs_unlock_dentry(dentry);
out:
- unionfs_read_unlock(file->f_dentry->d_sb);
+ unionfs_read_unlock(dentry->d_sb);
unionfs_check_file(file);
return err;
}
}
}
unionfs_inherit_mnt(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:
__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.
+ */
+ unionfs_copy_attr_times(parent_dentry->d_inode);
+ unionfs_copy_attr_times(child_dentry->d_inode);
parent_dentry = child_dentry;
child_dentry = path[--count];
* caller (__unionfs_d_revalidate_chain) by calling
* purge_inode_data.
*/
- fsstack_copy_attr_all(dentry->d_inode,
- unionfs_lower_inode(dentry->d_inode),
- unionfs_get_nlinks);
+ unionfs_copy_attr_all(dentry->d_inode,
+ unionfs_lower_inode(dentry->d_inode));
fsstack_copy_inode_size(dentry->d_inode,
unionfs_lower_inode(dentry->d_inode));
}
{
int err;
- unionfs_check_dentry(dentry);
unionfs_lock_dentry(dentry);
- err = __unionfs_d_revalidate_chain(dentry, nd);
+ err = __unionfs_d_revalidate_chain(dentry, nd, 0);
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
int err;
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
+ unionfs_check_file(file);
err = do_sync_read(file, buf, count, ppos);
struct file *file = iocb->ki_filp;
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
+ unionfs_check_file(file);
err = generic_file_aio_read(iocb, iov, nr_segs, pos);
unionfs_check_file(file);
return err;
}
-static ssize_t unionfs_write(struct file * file, const char __user * buf,
+
+static ssize_t unionfs_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int err = 0;
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
if ((err = unionfs_file_revalidate(file, 1)))
goto out;
+ unionfs_check_file(file);
err = do_sync_write(file, buf, count, ppos);
+ /* update our inode times upon a successful lower write */
+ if (err >= 0) {
+ unionfs_copy_attr_times(file->f_dentry->d_inode);
+ unionfs_check_file(file);
+ }
out:
unionfs_read_unlock(file->f_dentry->d_sb);
- unionfs_check_file(file);
return err;
}
struct file *lower_file;
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
- if ((err = unionfs_file_revalidate(file, 1)))
- goto out;
/* This might be deferred to mmap's writepage */
willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
if ((err = unionfs_file_revalidate(file, willwrite)))
goto out;
+ unionfs_check_file(file);
/*
* File systems which do not implement ->writepage may use
out:
unionfs_read_unlock(file->f_dentry->d_sb);
- unionfs_check_file(file);
+ if (!err) {
+ /* copyup could cause parent dir times to change */
+ unionfs_copy_attr_times(file->f_dentry->d_parent->d_inode);
+ unionfs_check_file(file);
+ unionfs_check_dentry(file->f_dentry->d_parent);
+ }
return err;
}
unionfs_lock_dentry(dentry);
unionfs_lock_dentry(dentry->d_parent);
- valid = __unionfs_d_revalidate_chain(dentry->d_parent, nd);
+ valid = __unionfs_d_revalidate_chain(dentry->d_parent, nd, 0);
unionfs_unlock_dentry(dentry->d_parent);
if (!valid) {
err = -ESTALE; /* same as what real_lookup does */
goto out;
}
- valid = __unionfs_d_revalidate_chain(dentry, nd);
+ valid = __unionfs_d_revalidate_chain(dentry, nd, 0);
/*
* It's only a bug if this dentry was not negative and couldn't be
* revalidated (shouldn't happen).
err = PTR_ERR(unionfs_interpose(dentry,
parent->i_sb, 0));
if (!err) {
- fsstack_copy_attr_times(parent,
- lower_parent_dentry->
- d_inode);
+ unionfs_copy_attr_times(parent);
fsstack_copy_inode_size(parent,
lower_parent_dentry->
d_inode);
unionfs_read_unlock(dentry->d_sb);
unionfs_check_inode(parent);
- unionfs_check_dentry(dentry->d_parent);
+ if (!err)
+ unionfs_check_dentry(dentry->d_parent);
unionfs_check_dentry(dentry);
return err;
}
if (!IS_ERR(ret)) {
if (ret)
dentry = ret;
+ /* parent times may have changed */
+ unionfs_copy_attr_times(dentry->d_parent->d_inode);
}
unionfs_check_inode(parent);
unionfs_check_dentry(dentry);
+ unionfs_check_dentry(dentry->d_parent);
return ret;
}
unionfs_double_lock_dentry(new_dentry, old_dentry);
- if (!__unionfs_d_revalidate_chain(old_dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(old_dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
if (new_dentry->d_inode &&
- !__unionfs_d_revalidate_chain(new_dentry, NULL)) {
+ !__unionfs_d_revalidate_chain(new_dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
/* Its a hard link, so use the same inode */
new_dentry->d_inode = igrab(old_dentry->d_inode);
d_instantiate(new_dentry, new_dentry->d_inode);
- fsstack_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode,
- unionfs_get_nlinks);
+ unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode);
fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode);
/* propagate number of hard-links */
old_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode);
+ /* new dentry's ctime may have changed due to hard-link counts */
+ unionfs_copy_attr_times(new_dentry->d_inode);
out:
if (!new_dentry->d_inode)
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
- !__unionfs_d_revalidate_chain(dentry, NULL)) {
+ !__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
- !__unionfs_d_revalidate_chain(dentry, NULL)) {
+ !__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
*/
err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
if (!err) {
- fsstack_copy_attr_times(parent,
- lower_parent_dentry->d_inode);
+ unionfs_copy_attr_times(parent);
fsstack_copy_inode_size(parent,
lower_parent_dentry->d_inode);
kfree(name);
+ if (!err)
+ unionfs_copy_attr_times(dentry->d_inode);
unionfs_unlock_dentry(dentry);
unionfs_check_inode(parent);
unionfs_check_dentry(dentry);
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
- !__unionfs_d_revalidate_chain(dentry, NULL)) {
+ !__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
unionfs_lock_dentry(dentry);
if (dentry->d_inode &&
- !__unionfs_d_revalidate_chain(dentry, nd)) {
+ !__unionfs_d_revalidate_chain(dentry, nd, 0)) {
err = -ESTALE;
goto out;
}
void *cookie)
{
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, nd))
+ if (!__unionfs_d_revalidate_chain(dentry, nd, 0))
printk("unionfs: put_link failed to revalidate dentry\n");
unionfs_unlock_dentry(dentry);
break;
}
}
+ /* sync times which may have changed (asynchronously) below */
+ unionfs_copy_attr_times(inode);
out:
if (!list_empty(&UNIONFS_SB(inode->i_sb)->rwsem.wait_list))
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
}
/* get the size from the first lower inode */
- lower_inode = unionfs_lower_inode(dentry->d_inode);
- fsstack_copy_attr_all(inode, lower_inode, unionfs_get_nlinks);
+ lower_inode = unionfs_lower_inode(inode);
+ unionfs_copy_attr_all(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
+ /* if setattr succeeded, then parent dir may have changed */
+ unionfs_copy_attr_times(dentry->d_parent->d_inode);
out:
unionfs_unlock_dentry(dentry);
unionfs_check_dentry(dentry);
+ unionfs_check_dentry(dentry->d_parent);
return err;
}
lower_inode->i_rdev);
/* all well, copy inode attributes */
- fsstack_copy_attr_all(inode, lower_inode, unionfs_get_nlinks);
+ unionfs_copy_attr_all(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
if (spliced)
err = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
wbc->for_writepages = saved_for_writepages; /* restore value */
- /*
- * update mtime and ctime of lower level file system
- * unionfs' mtime and ctime are updated by generic_file_write
- */
- lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
/* b/c grab_cache_page increased refcnt */
page_cache_release(lower_page);
if (err)
ClearPageUptodate(page);
- else
+ else {
SetPageUptodate(page);
+ /* lower mtimes has changed: update ours */
+ unionfs_copy_attr_times(inode);
+ }
out:
unlock_page(page);
int err;
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
if ((err = unionfs_file_revalidate(file, 0)))
goto out;
+ unionfs_check_file(file);
err = unionfs_do_readpage(file, page);
- if (!err)
+ if (!err) {
touch_atime(unionfs_lower_mnt(file->f_path.dentry),
unionfs_lower_dentry(file->f_path.dentry));
+ unionfs_copy_attr_times(file->f_dentry->d_inode);
+ }
/*
* we have to unlock our page, b/c we _might_ have gotten a locked
int err;
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
+ /*
+ * This is the only place where we unconditionally copy the lower
+ * attribute times before calling unionfs_file_revalidate. The
+ * reason is that our ->write calls do_sync_write which in turn will
+ * call our ->prepare_write and then ->commit_write. Before our
+ * ->write is called, the lower mtimes are in sync, but by the time
+ * the VFS calls our ->commit_write, the lower mtimes have changed.
+ * Therefore, the only reasonable time for us to sync up from the
+ * changed lower mtimes, and avoid an invariant violation warning,
+ * is here, in ->prepare_write.
+ */
+ unionfs_copy_attr_times(file->f_dentry->d_inode);
err = unionfs_file_revalidate(file, 1);
unionfs_check_file(file);
unionfs_read_unlock(file->f_dentry->d_sb);
BUG_ON(file == NULL);
unionfs_read_lock(file->f_dentry->d_sb);
- unionfs_check_file(file);
if ((err = unionfs_file_revalidate(file, 1)))
goto out;
+ unionfs_check_file(file);
inode = page->mapping->host;
lower_inode = unionfs_lower_inode(inode);
pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to;
if (pos > i_size_read(inode))
i_size_write(inode, pos);
-
- /*
- * update mtime and ctime of lower level file system
- * unionfs' mtime and ctime are updated by generic_file_write
- */
- lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
-
+ /* if vfs_write succeeded above, sync up our times */
+ unionfs_copy_attr_times(inode);
mark_inode_dirty_sync(inode);
out:
unionfs_double_lock_dentry(old_dentry, new_dentry);
- if (!__unionfs_d_revalidate_chain(old_dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(old_dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
if (!d_deleted(new_dentry) && new_dentry->d_inode &&
- !__unionfs_d_revalidate_chain(new_dentry, NULL)) {
+ !__unionfs_d_revalidate_chain(new_dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
}
}
+ /* if all of this renaming succeeded, update our times */
+ unionfs_copy_attr_times(old_dir);
+ unionfs_copy_attr_times(new_dir);
+ 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);
struct super_block *sb;
struct dentry *lower_dentry;
- unionfs_check_dentry(dentry);
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
+ unionfs_check_dentry(dentry);
sb = dentry->d_sb;
dget(lower_dentry);
if (!(err = is_robranch_super(dentry->d_sb, bindex)))
err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);
+ /* if vfs_unlink succeeded, update our inode's times */
+ if (!err)
+ unionfs_copy_attr_times(dentry->d_inode);
dput(lower_dentry);
fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
unlock_dir(lower_dir_dentry);
{
int err = 0;
- unionfs_check_dentry(dentry);
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
+ unionfs_check_dentry(dentry);
err = unionfs_unlink_whiteout(dir, dentry);
/* call d_drop so the system "forgets" about us */
if (!err) {
if (!S_ISDIR(dentry->d_inode->i_mode))
unionfs_purge_extras(dentry);
- unionfs_check_dentry(dentry);
d_drop(dentry);
+ /*
+ * if unlink/whiteout succeeded, parent dir mtime has
+ * changed
+ */
+ unionfs_copy_attr_times(dir);
}
out:
+ if (!err) {
+ unionfs_check_dentry(dentry);
+ unionfs_check_inode(dir);
+ }
unionfs_unlock_dentry(dentry);
return err;
}
int err = 0;
struct unionfs_dir_state *namelist = NULL;
- unionfs_check_dentry(dentry);
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
+ unionfs_check_dentry(dentry);
/* check if this unionfs directory is empty or not */
err = check_empty(dentry, &namelist);
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}
unionfs_lock_dentry(dentry);
- if (!__unionfs_d_revalidate_chain(dentry, NULL)) {
+ if (!__unionfs_d_revalidate_chain(dentry, NULL, 0)) {
err = -ESTALE;
goto out;
}