blob: 1753ea16ee1e2e1072d4ea9c7804b5eb643d58fa [file] [log] [blame]
/*
* Copyright (C) 2005-2013 Junjiro R. Okajima
*
* This program, aufs is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* file operations for special files.
* while they exist in aufs virtually,
* their file I/O is handled out of aufs.
*/
#include <linux/aio.h>
#include "aufs.h"
/*
* I don't think the size of this list grows much.
* so here is a very simple list implemented in order to find finfo matching a
* given file.
*/
static struct au_sphlhead au_finfo_sp = {
.spin = __SPIN_LOCK_INITIALIZER(au_finfo_sp.spin),
.head = HLIST_HEAD_INIT
};
struct au_finfo_sp {
struct hlist_node hlist;
struct file *file;
struct au_finfo *finfo;
};
struct au_finfo *au_fi_sp(struct file *file)
{
struct au_finfo *finfo;
struct au_finfo_sp *sp;
finfo = NULL;
spin_lock(&au_finfo_sp.spin);
hlist_for_each_entry(sp, &au_finfo_sp.head, hlist) {
if (sp->file != file)
continue;
finfo = sp->finfo;
break;
}
spin_unlock(&au_finfo_sp.spin);
return finfo;
}
static int au_fi_sp_add(struct file *file)
{
int err;
struct au_finfo_sp *sp;
err = -ENOMEM;
sp = kmalloc(sizeof(*sp), GFP_NOFS);
if (sp) {
err = 0;
sp->file = file;
sp->finfo = file->private_data;
spin_lock(&au_finfo_sp.spin);
hlist_add_head(&sp->hlist, &au_finfo_sp.head);
spin_unlock(&au_finfo_sp.spin);
}
return err;
}
static void au_fi_sp_del(struct file *file)
{
struct au_finfo_sp *sp, *do_free;
do_free = NULL;
spin_lock(&au_finfo_sp.spin);
hlist_for_each_entry(sp, &au_finfo_sp.head, hlist) {
if (sp->file != file)
continue;
hlist_del(&sp->hlist);
do_free = sp;
break;
}
spin_unlock(&au_finfo_sp.spin);
kfree(do_free);
}
/* ---------------------------------------------------------------------- */
static ssize_t aufs_aio_read_sp(struct kiocb *kio, const struct iovec *iov,
unsigned long nv, loff_t pos)
{
ssize_t err;
aufs_bindex_t bstart;
unsigned char wbr;
struct file *file, *h_file;
struct super_block *sb;
file = kio->ki_filp;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
fi_read_lock(file);
bstart = au_fbstart(file);
h_file = au_hf_top(file);
fi_read_unlock(file);
wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm);
si_read_unlock(sb);
/* do not change the file in kio */
AuDebugOn(!h_file->f_op || !h_file->f_op->aio_read);
err = h_file->f_op->aio_read(kio, iov, nv, pos);
if (err > 0 && wbr)
file_accessed(h_file);
return err;
}
static ssize_t aufs_aio_write_sp(struct kiocb *kio, const struct iovec *iov,
unsigned long nv, loff_t pos)
{
ssize_t err;
aufs_bindex_t bstart;
unsigned char wbr;
struct super_block *sb;
struct file *file, *h_file;
file = kio->ki_filp;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
fi_read_lock(file);
bstart = au_fbstart(file);
h_file = au_hf_top(file);
fi_read_unlock(file);
wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm);
si_read_unlock(sb);
/* do not change the file in kio */
AuDebugOn(!h_file->f_op || !h_file->f_op->aio_write);
err = h_file->f_op->aio_write(kio, iov, nv, pos);
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_release_sp(struct inode *inode, struct file *file)
{
int err;
struct file *h_file;
fi_read_lock(file);
h_file = au_hf_top(file);
fi_read_unlock(file);
/* close this fifo in aufs */
err = h_file->f_op->release(inode, file); /* ignore */
aufs_release_nondir(inode, file); /* ignore */
au_fi_sp_del(file);
return err;
}
/* ---------------------------------------------------------------------- */
/* currently, support only FIFO */
enum {
AuSp_FIFO, AuSp_FIFO_R, AuSp_FIFO_W, AuSp_FIFO_RW,
/* AuSp_SOCK, AuSp_CHR, AuSp_BLK, */
AuSp_Last
};
static int aufs_open_sp(struct inode *inode, struct file *file);
static struct au_sp_fop {
int done;
struct file_operations fop; /* not 'const' */
spinlock_t spin;
} au_sp_fop[AuSp_Last] = {
[AuSp_FIFO] = {
.fop = {
.owner = THIS_MODULE,
.open = aufs_open_sp
}
}
};
static void au_init_fop_sp(struct file *file)
{
struct au_sp_fop *p;
int i;
struct file *h_file;
p = au_sp_fop;
if (unlikely(!p->done)) {
/* initialize first time only */
static DEFINE_SPINLOCK(spin);
spin_lock(&spin);
if (!p->done) {
BUILD_BUG_ON(sizeof(au_sp_fop)/sizeof(*au_sp_fop)
!= AuSp_Last);
for (i = 0; i < AuSp_Last; i++)
spin_lock_init(&p[i].spin);
p->done = 1;
}
spin_unlock(&spin);
}
switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
case FMODE_READ:
i = AuSp_FIFO_R;
break;
case FMODE_WRITE:
i = AuSp_FIFO_W;
break;
case FMODE_READ | FMODE_WRITE:
i = AuSp_FIFO_RW;
break;
default:
BUG();
}
p += i;
if (unlikely(!p->done)) {
/* initialize first time only */
h_file = au_hf_top(file);
spin_lock(&p->spin);
if (!p->done) {
p->fop = *h_file->f_op;
p->fop.owner = THIS_MODULE;
if (p->fop.aio_read)
p->fop.aio_read = aufs_aio_read_sp;
if (p->fop.aio_write)
p->fop.aio_write = aufs_aio_write_sp;
p->fop.release = aufs_release_sp;
p->done = 1;
}
spin_unlock(&p->spin);
}
file->f_op = &p->fop;
}
static int au_cpup_sp(struct dentry *dentry)
{
int err;
struct au_pin pin;
struct au_wr_dir_args wr_dir_args = {
.force_btgt = -1,
.flags = 0
};
struct au_cp_generic cpg = {
.dentry = dentry,
.bdst = -1,
.bsrc = -1,
.len = -1,
.pin = &pin,
.flags = AuCpup_DTIME
};
AuDbg("%.*s\n", AuDLNPair(dentry));
di_read_unlock(dentry, AuLock_IR);
di_write_lock_child(dentry);
err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
if (unlikely(err < 0))
goto out;
cpg.bdst = err;
err = 0;
if (cpg.bdst == au_dbstart(dentry))
goto out; /* success */
err = au_pin(&pin, dentry, cpg.bdst, au_opt_udba(dentry->d_sb),
AuPin_MNT_WRITE);
if (!err) {
err = au_sio_cpup_simple(&cpg);
au_unpin(&pin);
}
out:
di_downgrade_lock(dentry, AuLock_IR);
return err;
}
static int au_do_open_sp(struct file *file, int flags)
{
int err;
struct dentry *dentry;
struct super_block *sb;
struct file *h_file;
struct inode *h_inode;
err = au_fi_sp_add(file);
if (unlikely(err))
goto out;
dentry = file->f_dentry;
AuDbg("%.*s\n", AuDLNPair(dentry));
/*
* try copying-up.
* operate on the ro branch is not an error.
*/
au_cpup_sp(dentry); /* ignore */
/* prepare h_file */
err = au_do_open_nondir(file, vfsub_file_flags(file));
if (unlikely(err))
goto out_del;
sb = dentry->d_sb;
h_file = au_hf_top(file);
h_inode = file_inode(h_file);
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
si_read_unlock(sb);
/* open this fifo in aufs */
err = h_inode->i_fop->open(file_inode(file), file);
si_noflush_read_lock(sb);
fi_write_lock(file);
di_read_lock_child(dentry, AuLock_IR);
if (!err) {
au_init_fop_sp(file);
goto out; /* success */
}
out_del:
au_fi_sp_del(file);
out:
return err;
}
static int aufs_open_sp(struct inode *inode, struct file *file)
{
int err;
struct super_block *sb;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
err = au_do_open(file, au_do_open_sp, /*fidir*/NULL);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
void au_init_special_fop(struct inode *inode, umode_t mode, dev_t rdev)
{
init_special_inode(inode, mode, rdev);
switch (mode & S_IFMT) {
case S_IFIFO:
inode->i_fop = &au_sp_fop[AuSp_FIFO].fop;
/*FALLTHROUGH*/
case S_IFCHR:
case S_IFBLK:
case S_IFSOCK:
break;
default:
AuDebugOn(1);
}
}
int au_special_file(umode_t mode)
{
int ret;
ret = 0;
switch (mode & S_IFMT) {
case S_IFIFO:
#if 0
case S_IFCHR:
case S_IFBLK:
case S_IFSOCK:
#endif
ret = 1;
}
return ret;
}