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

aufs: copy-up 3/7, internal file I/O



The internal file read/write for copy-up in kernelspace.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 3be12838
...@@ -14,6 +14,7 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ...@@ -14,6 +14,7 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
wkq.o vfsub.o dcsub.o \ wkq.o vfsub.o dcsub.o \
cpup.o whout.o \ cpup.o whout.o \
dinfo.o dentry.o \ dinfo.o dentry.o \
file.o \
dir.o \ dir.o \
iinfo.o inode.o i_op.o iinfo.o inode.o i_op.o
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "dcsub.h" #include "dcsub.h"
#include "dentry.h" #include "dentry.h"
#include "dir.h" #include "dir.h"
#include "file.h"
#include "fstype.h" #include "fstype.h"
#include "hbl.h" #include "hbl.h"
#include "inode.h" #include "inode.h"
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
* branch management * branch management
*/ */
#include <linux/file.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include "aufs.h" #include "aufs.h"
...@@ -21,6 +20,8 @@ static void au_br_do_free(struct au_branch *br) ...@@ -21,6 +20,8 @@ static void au_br_do_free(struct au_branch *br)
au_xino_put(br); au_xino_put(br);
AuLCntZero(au_lcnt_read(&br->br_nfiles, /*do_rev*/0));
au_lcnt_fin(&br->br_nfiles, /*do_sync*/0);
AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0)); AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0));
au_lcnt_fin(&br->br_count, /*do_sync*/0); au_lcnt_fin(&br->br_count, /*do_sync*/0);
...@@ -38,6 +39,7 @@ static void au_br_do_free(struct au_branch *br) ...@@ -38,6 +39,7 @@ static void au_br_do_free(struct au_branch *br)
path_put(&br->br_path); path_put(&br->br_path);
lockdep_on(); lockdep_on();
au_kfree_rcu(wbr); au_kfree_rcu(wbr);
au_lcnt_wait_for_fin(&br->br_nfiles);
au_lcnt_wait_for_fin(&br->br_count); au_lcnt_wait_for_fin(&br->br_count);
/* I don't know why, but percpu_refcount requires this */ /* I don't know why, but percpu_refcount requires this */
/* synchronize_rcu(); */ /* synchronize_rcu(); */
...@@ -336,6 +338,7 @@ static int au_br_init(struct au_branch *br, struct super_block *sb, ...@@ -336,6 +338,7 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
err = 0; err = 0;
br->br_perm = add->perm; br->br_perm = add->perm;
br->br_path = add->path; /* set first, path_get() later */ br->br_path = add->path; /* set first, path_get() later */
au_lcnt_init(&br->br_nfiles, /*release*/NULL);
au_lcnt_init(&br->br_count, /*release*/NULL); au_lcnt_init(&br->br_count, /*release*/NULL);
br->br_id = au_new_br_id(sb); br->br_id = au_new_br_id(sb);
AuDebugOn(br->br_id < 0); AuDebugOn(br->br_id < 0);
......
...@@ -72,6 +72,7 @@ struct au_branch { ...@@ -72,6 +72,7 @@ struct au_branch {
int br_perm; int br_perm;
struct path br_path; struct path br_path;
au_lcnt_t br_nfiles; /* opened files */
au_lcnt_t br_count; /* in-use for other */ au_lcnt_t br_count; /* in-use for other */
struct au_wbr *br_wbr; struct au_wbr *br_wbr;
...@@ -99,6 +100,25 @@ static inline struct super_block *au_br_sb(struct au_branch *br) ...@@ -99,6 +100,25 @@ static inline struct super_block *au_br_sb(struct au_branch *br)
return au_br_mnt(br)->mnt_sb; return au_br_mnt(br)->mnt_sb;
} }
static inline int au_br_rdonly(struct au_branch *br)
{
return (sb_rdonly(au_br_sb(br))
|| !au_br_writable(br->br_perm))
? -EROFS : 0;
}
static inline int au_br_test_oflag(int oflag, struct au_branch *br)
{
int err, exec_flag;
err = 0;
exec_flag = oflag & __FMODE_EXEC;
if (unlikely(exec_flag && path_noexec(&br->br_path)))
err = -EACCES;
return err;
}
static inline void au_xino_get(struct au_branch *br) static inline void au_xino_get(struct au_branch *br)
{ {
struct au_xino *xi; struct au_xino *xi;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* handling file/dir, and address_space operation
*/
#include <linux/fsnotify.h>
#include "aufs.h"
/* drop flags for writing */
unsigned int au_file_roflags(unsigned int flags)
{
flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);
flags |= O_RDONLY | O_NOATIME;
return flags;
}
/* common functions to regular file and dir */
struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
struct file *file)
{
struct file *h_file;
struct dentry *h_dentry;
struct inode *h_inode;
struct super_block *sb;
struct au_branch *br;
struct path h_path;
int err;
/* a race condition can happen between open and unlink/rmdir */
h_file = ERR_PTR(-ENOENT);
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry || d_is_negative(h_dentry))
goto out;
h_inode = d_inode(h_dentry);
spin_lock(&h_dentry->d_lock);
err = (!d_unhashed(dentry) && d_unlinked(h_dentry))
/* || !d_inode(dentry)->i_nlink */
;
spin_unlock(&h_dentry->d_lock);
if (unlikely(err))
goto out;
sb = dentry->d_sb;
br = au_sbr(sb, bindex);
err = au_br_test_oflag(flags, br);
h_file = ERR_PTR(err);
if (unlikely(err))
goto out;
/* drop flags for writing */
if (au_test_ro(sb, bindex, d_inode(dentry)))
flags = au_file_roflags(flags);
flags &= ~O_CREAT;
au_lcnt_inc(&br->br_nfiles);
h_path.dentry = h_dentry;
h_path.mnt = au_br_mnt(br);
h_file = vfsub_dentry_open(&h_path, flags);
if (IS_ERR(h_file))
goto out_br;
if (flags & __FMODE_EXEC) {
err = deny_write_access(h_file);
if (unlikely(err)) {
fput(h_file);
h_file = ERR_PTR(err);
goto out_br;
}
}
fsnotify_open(h_file);
goto out; /* success */
out_br:
au_lcnt_dec(&br->br_nfiles);
out:
return h_file;
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* file operations
*/
#ifndef __AUFS_FILE_H__
#define __AUFS_FILE_H__
#ifdef __KERNEL__
#include <linux/file.h>
#include <linux/fs.h>
#include "rwsem.h"
struct au_branch;
struct au_hfile {
struct file *hf_file;
struct au_branch *hf_br;
};
struct au_finfo {
atomic_t fi_generation;
struct au_rwsem fi_rwsem;
aufs_bindex_t fi_btop;
struct au_hfile fi_htop;
struct rcu_head rcu;
} ____cacheline_aligned_in_smp;
/* ---------------------------------------------------------------------- */
/* file.c */
unsigned int au_file_roflags(unsigned int flags);
struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
struct file *file);
/* finfo.c */
void au_hfput(struct au_hfile *hf, int execed);
void au_set_h_fptr(struct file *file, aufs_bindex_t bindex,
struct file *h_file);
void au_update_figen(struct file *file);
void au_fi_init_once(void *_fi);
void au_finfo_fin(struct file *file);
int au_finfo_init(struct file *file);
/* ---------------------------------------------------------------------- */
static inline struct au_finfo *au_fi(struct file *file)
{
return file->private_data;
}
/* ---------------------------------------------------------------------- */
#define fi_read_lock(f) au_rw_read_lock(&au_fi(f)->fi_rwsem)
#define fi_write_lock(f) au_rw_write_lock(&au_fi(f)->fi_rwsem)
#define fi_read_trylock(f) au_rw_read_trylock(&au_fi(f)->fi_rwsem)
#define fi_write_trylock(f) au_rw_write_trylock(&au_fi(f)->fi_rwsem)
/*
#define fi_read_trylock_nested(f) \
au_rw_read_trylock_nested(&au_fi(f)->fi_rwsem)
#define fi_write_trylock_nested(f) \
au_rw_write_trylock_nested(&au_fi(f)->fi_rwsem)
*/
#define fi_read_unlock(f) au_rw_read_unlock(&au_fi(f)->fi_rwsem)
#define fi_write_unlock(f) au_rw_write_unlock(&au_fi(f)->fi_rwsem)
#define fi_downgrade_lock(f) au_rw_dgrade_lock(&au_fi(f)->fi_rwsem)
#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
/* ---------------------------------------------------------------------- */
/* todo: hard/soft set? */
static inline aufs_bindex_t au_fbtop(struct file *file)
{
FiMustAnyLock(file);
return au_fi(file)->fi_btop;
}
static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex)
{
FiMustWriteLock(file);
au_fi(file)->fi_btop = bindex;
}
static inline struct file *au_hf_top(struct file *file)
{
FiMustAnyLock(file);
return au_fi(file)->fi_htop.hf_file;
}
/* todo: memory barrier? */
static inline unsigned int au_figen(struct file *f)
{
return atomic_read(&au_fi(f)->fi_generation);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_FILE_H__ */
...@@ -19,6 +19,33 @@ struct inode *au_igrab(struct inode *inode) ...@@ -19,6 +19,33 @@ struct inode *au_igrab(struct inode *inode)
return inode; return inode;
} }
/* ---------------------------------------------------------------------- */
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
struct inode *inode)
{
int err;
struct inode *hi;
err = au_br_rdonly(au_sbr(sb, bindex));
/* pseudo-link after flushed may happen out of bounds */
if (!err
&& inode
&& au_ibtop(inode) <= bindex
&& bindex <= au_ibbot(inode)) {
/*
* permission check is unnecessary since vfsub routine
* will be called later
*/
hi = au_h_iptr(inode, bindex);
if (hi)
err = IS_IMMUTABLE(hi) ? -EROFS : 0;
}
return err;
}
int au_test_h_perm(struct inode *h_inode, int mask) int au_test_h_perm(struct inode *h_inode, int mask)
{ {
if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
......
...@@ -88,6 +88,8 @@ static inline struct au_iinfo *au_ii(struct inode *inode) ...@@ -88,6 +88,8 @@ static inline struct au_iinfo *au_ii(struct inode *inode)
/* inode.c */ /* inode.c */
struct inode *au_igrab(struct inode *inode); struct inode *au_igrab(struct inode *inode);
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
struct inode *inode);
int au_test_h_perm(struct inode *h_inode, int mask); int au_test_h_perm(struct inode *h_inode, int mask);
int au_test_h_perm_sio(struct inode *h_inode, int mask); int au_test_h_perm_sio(struct inode *h_inode, int mask);
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
* mount options/flags * mount options/flags
*/ */
#include <linux/file.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/types.h> /* a distribution requires */ #include <linux/types.h> /* a distribution requires */
#include <linux/parser.h> #include <linux/parser.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/uaccess.h>
#include "aufs.h" #include "aufs.h"
struct file *vfsub_dentry_open(struct path *path, int flags) struct file *vfsub_dentry_open(struct path *path, int flags)
...@@ -180,6 +181,67 @@ out: ...@@ -180,6 +181,67 @@ out:
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* todo: support mmap_sem? */
ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
loff_t *ppos)
{
ssize_t err;
lockdep_off();
err = vfs_read(file, ubuf, count, ppos);
lockdep_on();
return err;
}
/* todo: kernel_read()? */
ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos)
{
ssize_t err;
mm_segment_t oldfs;
union {
void *k;
char __user *u;
} buf;
buf.k = kbuf;
oldfs = get_fs();
set_fs(KERNEL_DS);
err = vfsub_read_u(file, buf.u, count, ppos);
set_fs(oldfs);
return err;
}
ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
loff_t *ppos)
{
ssize_t err;
lockdep_off();
err = vfs_write(file, ubuf, count, ppos);
lockdep_on();
return err;
}
ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos)
{
ssize_t err;
mm_segment_t oldfs;
union {
void *k;
const char __user *u;
} buf;
buf.k = kbuf;
oldfs = get_fs();
set_fs(KERNEL_DS);
err = vfsub_write_u(file, buf.u, count, ppos);
set_fs(oldfs);
return err;
}
/* ---------------------------------------------------------------------- */
struct notify_change_args { struct notify_change_args {
int *errp; int *errp;
struct path *path; struct path *path;
......
...@@ -88,6 +88,15 @@ int vfsub_rmdir(struct inode *dir, struct path *path); ...@@ -88,6 +88,15 @@ int vfsub_rmdir(struct inode *dir, struct path *path);
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
loff_t *ppos);
ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos);
ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
loff_t *ppos);
ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos);
static inline loff_t vfsub_f_size_read(struct file *file) static inline loff_t vfsub_f_size_read(struct file *file)
{ {
return i_size_read(file_inode(file)); return i_size_read(file_inode(file));
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
* + remount with xino/noxino options * + remount with xino/noxino options
*/ */
#include <linux/file.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/statfs.h> #include <linux/statfs.h>
......
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