Commit 83ab62e6 authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: directory op



This is very similar to file operations, including re-open after branch
management. The major part of readdir(3) is split into another object
called VDIR (in previous commit).
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent dc25d54f
......@@ -7,6 +7,8 @@
* directory operations
*/
#include <linux/fs_stack.h>
#include <linux/iversion.h>
#include "aufs.h"
void au_add_nlink(struct inode *dir, struct inode *h_dir)
......@@ -188,6 +190,325 @@ out:
/* ---------------------------------------------------------------------- */
static int reopen_dir(struct file *file)
{
int err;
unsigned int flags;
aufs_bindex_t bindex, btail, btop;
struct dentry *dentry, *h_dentry;
struct file *h_file;
/* open all lower dirs */
dentry = file->f_path.dentry;
btop = au_dbtop(dentry);
for (bindex = au_fbtop(file); bindex < btop; bindex++)
au_set_h_fptr(file, bindex, NULL);
au_set_fbtop(file, btop);
btail = au_dbtaildir(dentry);
for (bindex = au_fbbot_dir(file); btail < bindex; bindex--)
au_set_h_fptr(file, bindex, NULL);
au_set_fbbot_dir(file, btail);
flags = vfsub_file_flags(file);
for (bindex = btop; bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
h_file = au_hf_dir(file, bindex);
if (h_file)
continue;
h_file = au_h_open(dentry, bindex, flags, file);
err = PTR_ERR(h_file);
if (IS_ERR(h_file))
goto out; /* close all? */
au_set_h_fptr(file, bindex, h_file);
}
au_update_figen(file);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
err = 0;
out:
return err;
}
static int do_open_dir(struct file *file, int flags, struct file *h_file)
{
int err;
aufs_bindex_t bindex, btail;
struct dentry *dentry, *h_dentry;
struct vfsmount *mnt;
FiMustWriteLock(file);
AuDebugOn(h_file);
err = 0;
mnt = file->f_path.mnt;
dentry = file->f_path.dentry;
file->f_version = inode_query_iversion(d_inode(dentry));
bindex = au_dbtop(dentry);
au_set_fbtop(file, bindex);
btail = au_dbtaildir(dentry);
au_set_fbbot_dir(file, btail);
for (; !err && bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
h_file = au_h_open(dentry, bindex, flags, file);
if (IS_ERR(h_file)) {
err = PTR_ERR(h_file);
break;
}
au_set_h_fptr(file, bindex, h_file);
}
au_update_figen(file);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
if (!err)
return 0; /* success */
/* close all */
for (bindex = au_fbtop(file); bindex <= btail; bindex++)
au_set_h_fptr(file, bindex, NULL);
au_set_fbtop(file, -1);
au_set_fbbot_dir(file, -1);
return err;
}
static int aufs_open_dir(struct inode *inode __maybe_unused,
struct file *file)
{
int err;
struct super_block *sb;
struct au_fidir *fidir;
err = -ENOMEM;
sb = file->f_path.dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
fidir = au_fidir_alloc(sb);
if (fidir) {
struct au_do_open_args args = {
.open = do_open_dir,
.fidir = fidir
};
err = au_do_open(file, &args);
if (unlikely(err))
au_kfree_rcu(fidir);
}
si_read_unlock(sb);
return err;
}
static int aufs_release_dir(struct inode *inode __maybe_unused,
struct file *file)
{
struct au_vdir *vdir_cache;
struct au_finfo *finfo;
struct au_fidir *fidir;
struct au_hfile *hf;
aufs_bindex_t bindex, bbot;
finfo = au_fi(file);
fidir = finfo->fi_hdir;
if (fidir) {
vdir_cache = fidir->fd_vdir_cache; /* lock-free */
if (vdir_cache)
au_vdir_free(vdir_cache);
bindex = finfo->fi_btop;
if (bindex >= 0) {
hf = fidir->fd_hfile + bindex;
/*
* calls fput() instead of filp_close(),
* since no dnotify or lock for the lower file.
*/
bbot = fidir->fd_bbot;
for (; bindex <= bbot; bindex++, hf++)
if (hf->hf_file)
au_hfput(hf, /*execed*/0);
}
au_kfree_rcu(fidir);
finfo->fi_hdir = NULL;
}
au_finfo_fin(file);
return 0;
}
/* ---------------------------------------------------------------------- */
static int au_do_flush_dir(struct file *file, fl_owner_t id)
{
int err;
aufs_bindex_t bindex, bbot;
struct file *h_file;
err = 0;
bbot = au_fbbot_dir(file);
for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) {
h_file = au_hf_dir(file, bindex);
if (h_file)
err = vfsub_flush(h_file, id);
}
return err;
}
static int aufs_flush_dir(struct file *file, fl_owner_t id)
{
return au_do_flush(file, id, au_do_flush_dir);
}
/* ---------------------------------------------------------------------- */
static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync)
{
int err;
aufs_bindex_t bbot, bindex;
struct inode *inode;
struct super_block *sb;
err = 0;
sb = dentry->d_sb;
inode = d_inode(dentry);
IMustLock(inode);
bbot = au_dbbot(dentry);
for (bindex = au_dbtop(dentry); !err && bindex <= bbot; bindex++) {
struct path h_path;
if (au_test_ro(sb, bindex, inode))
continue;
h_path.dentry = au_h_dptr(dentry, bindex);
if (!h_path.dentry)
continue;
h_path.mnt = au_sbr_mnt(sb, bindex);
err = vfsub_fsync(NULL, &h_path, datasync);
}
return err;
}
static int au_do_fsync_dir(struct file *file, int datasync)
{
int err;
aufs_bindex_t bbot, bindex;
struct file *h_file;
struct super_block *sb;
struct inode *inode;
err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0);
if (unlikely(err))
goto out;
inode = file_inode(file);
sb = inode->i_sb;
bbot = au_fbbot_dir(file);
for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) {
h_file = au_hf_dir(file, bindex);
if (!h_file || au_test_ro(sb, bindex, inode))
continue;
err = vfsub_fsync(h_file, &h_file->f_path, datasync);
}
out:
return err;
}
/*
* @file may be NULL
*/
static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end,
int datasync)
{
int err;
struct dentry *dentry;
struct inode *inode;
struct super_block *sb;
err = 0;
dentry = file->f_path.dentry;
inode = d_inode(dentry);
inode_lock(inode);
sb = dentry->d_sb;
si_noflush_read_lock(sb);
if (file)
err = au_do_fsync_dir(file, datasync);
else {
di_write_lock_child(dentry);
err = au_do_fsync_dir_no_file(dentry, datasync);
}
au_cpup_attr_timesizes(inode);
di_write_unlock(dentry);
if (file)
fi_write_unlock(file);
si_read_unlock(sb);
inode_unlock(inode);
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_iterate_shared(struct file *file, struct dir_context *ctx)
{
int err;
struct dentry *dentry;
struct inode *inode, *h_inode;
struct super_block *sb;
AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos);
dentry = file->f_path.dentry;
inode = d_inode(dentry);
IMustLock(inode);
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0);
if (unlikely(err))
goto out;
err = au_alive_dir(dentry);
if (!err)
err = au_vdir_init(file);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err))
goto out_unlock;
h_inode = au_h_iptr(inode, au_ibtop(inode));
if (!au_test_nfsd()) {
err = au_vdir_fill_de(file, ctx);
fsstack_copy_attr_atime(inode, h_inode);
} else {
/*
* nfsd filldir may call lookup_one_len(), vfs_getattr(),
* encode_fh() and others.
*/
atomic_inc(&h_inode->i_count);
di_read_unlock(dentry, AuLock_IR);
si_read_unlock(sb);
err = au_vdir_fill_de(file, ctx);
fsstack_copy_attr_atime(inode, h_inode);
fi_write_unlock(file);
iput(h_inode);
AuTraceErr(err);
return err;
}
out_unlock:
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
out:
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
#define AuTestEmpty_WHONLY 1
#define AuTestEmpty_CALLED (1 << 1)
#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name)
......@@ -392,3 +713,16 @@ int au_test_empty(struct dentry *dentry, struct au_nhash *whlist)
return err;
}
/* ---------------------------------------------------------------------- */
const struct file_operations aufs_dir_fop = {
.owner = THIS_MODULE,
.llseek = default_llseek,
.read = generic_read_dir,
.iterate_shared = aufs_iterate_shared,
.open = aufs_open_dir,
.release = aufs_release_dir,
.flush = aufs_flush_dir,
.fsync = aufs_fsync_dir
};
......@@ -70,6 +70,7 @@ struct au_vdir {
/* ---------------------------------------------------------------------- */
/* dir.c */
extern const struct file_operations aufs_dir_fop;
void au_add_nlink(struct inode *dir, struct inode *h_dir);
void au_sub_nlink(struct inode *dir, struct inode *h_dir);
loff_t au_dir_size(struct file *file, struct dentry *dentry);
......
......@@ -223,7 +223,7 @@ static int set_inode(struct inode *inode, struct dentry *dentry)
isdir = 1;
btail = au_dbtaildir(dentry);
inode->i_op = iop + AuIop_DIR;
inode->i_fop = &simple_dir_operations; /* re-commit later */
inode->i_fop = &aufs_dir_fop;
break;
case S_IFLNK:
btail = au_dbtail(dentry);
......
......@@ -751,8 +751,8 @@ static int alloc_root(struct super_block *sb)
if (IS_ERR(inode))
goto out;
inode->i_op = aufs_iop + AuIop_DIR;
inode->i_fop = &simple_dir_operations; /* replace later */
inode->i_op = aufs_iop + AuIop_DIR; /* with getattr by default */
inode->i_fop = &aufs_dir_fop;
inode->i_mode = S_IFDIR;
set_nlink(inode, 2);
unlock_new_inode(inode);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment