/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 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, see .
*/
/*
* branch filesystems and xino for them
*/
#ifndef __AUFS_BRANCH_H__
#define __AUFS_BRANCH_H__
#ifdef __KERNEL__
#include
#include "dirren.h"
#include "dynop.h"
#include "lcnt.h"
#include "rwsem.h"
#include "super.h"
/* ---------------------------------------------------------------------- */
/* a xino file */
struct au_xino {
struct file **xi_file;
unsigned int xi_nfile;
struct {
spinlock_t spin;
ino_t *array;
int total;
/* reserved for future use */
/* unsigned long *bitmap; */
wait_queue_head_t wqh;
} xi_nondir;
struct mutex xi_mtx; /* protects xi_file array */
struct hlist_bl_head xi_writing;
atomic_t xi_truncating;
struct kref xi_kref;
};
/* File-based Hierarchical Storage Management */
struct au_br_fhsm {
#ifdef CONFIG_AUFS_FHSM
struct mutex bf_lock;
unsigned long bf_jiffy;
struct aufs_stfs bf_stfs;
int bf_readable;
#endif
};
/* members for writable branch only */
enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};
struct au_wbr {
struct au_rwsem wbr_wh_rwsem;
struct dentry *wbr_wh[AuBrWh_Last];
atomic_t wbr_wh_running;
#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */
#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */
#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */
/* mfs mode */
unsigned long long wbr_bytes;
};
/* ext2 has 3 types of operations at least, ext3 has 4 */
#define AuBrDynOp (AuDyLast * 4)
#ifdef CONFIG_AUFS_HFSNOTIFY
/* support for asynchronous destruction */
struct au_br_hfsnotify {
struct fsnotify_group *hfsn_group;
};
#endif
/* sysfs entries */
struct au_brsysfs {
char name[16];
struct attribute attr;
};
enum {
AuBrSysfs_BR,
AuBrSysfs_BRID,
AuBrSysfs_Last
};
/* protected by superblock rwsem */
struct au_branch {
struct au_xino *br_xino;
aufs_bindex_t br_id;
int br_perm;
struct path br_path;
spinlock_t br_dykey_lock;
struct au_dykey *br_dykey[AuBrDynOp];
au_lcnt_t br_nfiles; /* opened files */
au_lcnt_t br_count; /* in-use for other */
struct au_wbr *br_wbr;
struct au_br_fhsm *br_fhsm;
#ifdef CONFIG_AUFS_HFSNOTIFY
struct au_br_hfsnotify *br_hfsn;
#endif
#ifdef CONFIG_SYSFS
/* entries under sysfs per mount-point */
struct au_brsysfs br_sysfs[AuBrSysfs_Last];
#endif
#ifdef CONFIG_DEBUG_FS
struct dentry *br_dbgaufs; /* xino */
#endif
struct au_dr_br br_dirren;
};
/* ---------------------------------------------------------------------- */
static inline struct vfsmount *au_br_mnt(struct au_branch *br)
{
return br->br_path.mnt;
}
static inline struct dentry *au_br_dentry(struct au_branch *br)
{
return br->br_path.dentry;
}
static inline struct super_block *au_br_sb(struct au_branch *br)
{
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_hnotifyable(int brperm __maybe_unused)
{
#ifdef CONFIG_AUFS_HNOTIFY
return !(brperm & AuBrPerm_RR);
#else
return 0;
#endif
}
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)
{
struct au_xino *xi;
xi = br->br_xino;
if (xi)
kref_get(&xi->xi_kref);
}
static inline int au_xino_count(struct au_branch *br)
{
int v;
struct au_xino *xi;
v = 0;
xi = br->br_xino;
if (xi)
v = kref_read(&xi->xi_kref);
return v;
}
/* ---------------------------------------------------------------------- */
/* branch.c */
struct au_sbinfo;
void au_br_free(struct au_sbinfo *sinfo);
int au_br_index(struct super_block *sb, aufs_bindex_t br_id);
struct au_opt_add;
int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount);
struct au_opt_del;
int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount);
long au_ibusy_ioctl(struct file *file, unsigned long arg);
#ifdef CONFIG_COMPAT
long au_ibusy_compat_ioctl(struct file *file, unsigned long arg);
#endif
struct au_opt_mod;
int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
int *do_refresh);
struct aufs_stfs;
int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs);
/* xino.c */
static const loff_t au_loff_max = LLONG_MAX;
aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry);
struct file *au_xino_create(struct super_block *sb, char *fpath, int silent,
int wbrtop);
struct file *au_xino_create2(struct super_block *sb, struct path *base,
struct file *copy_src);
struct au_xi_new {
struct au_xino *xi; /* switch between xino and xigen */
int idx;
struct path *base;
struct file *copy_src;
};
struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew);
int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
ino_t *ino);
int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
ino_t ino);
ssize_t xino_fread(vfs_readf_t func, struct file *file, void *buf, size_t size,
loff_t *pos);
ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf,
size_t size, loff_t *pos);
int au_xib_trunc(struct super_block *sb);
int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin);
struct au_xino *au_xino_alloc(unsigned int nfile);
int au_xino_put(struct au_branch *br);
struct file *au_xino_file1(struct au_xino *xi);
struct au_opt_xino;
void au_xino_clr(struct super_block *sb);
int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount);
struct file *au_xino_def(struct super_block *sb);
int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino,
struct path *base);
ino_t au_xino_new_ino(struct super_block *sb);
void au_xino_delete_inode(struct inode *inode, const int unlinked);
void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex,
ino_t h_ino, int idx);
int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
int *idx);
int au_xino_path(struct seq_file *seq, struct file *file);
/* ---------------------------------------------------------------------- */
/* @idx is signed to accept -1 meaning the first file */
static inline struct file *au_xino_file(struct au_xino *xi, int idx)
{
struct file *file;
file = NULL;
if (!xi)
goto out;
if (idx >= 0) {
if (idx < xi->xi_nfile)
file = xi->xi_file[idx];
} else
file = au_xino_file1(xi);
out:
return file;
}
/* ---------------------------------------------------------------------- */
/* Superblock to branch */
static inline
aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
{
return au_sbr(sb, bindex)->br_id;
}
static inline
struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
{
return au_br_mnt(au_sbr(sb, bindex));
}
static inline
struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
{
return au_br_sb(au_sbr(sb, bindex));
}
static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
{
return au_sbr(sb, bindex)->br_perm;
}
static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex)
{
return au_br_whable(au_sbr_perm(sb, bindex));
}
/* ---------------------------------------------------------------------- */
#define wbr_wh_read_lock(wbr) au_rw_read_lock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_lock(wbr) au_rw_write_lock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_read_trylock(wbr) au_rw_read_trylock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_trylock(wbr) au_rw_write_trylock(&(wbr)->wbr_wh_rwsem)
/*
#define wbr_wh_read_trylock_nested(wbr) \
au_rw_read_trylock_nested(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_trylock_nested(wbr) \
au_rw_write_trylock_nested(&(wbr)->wbr_wh_rwsem)
*/
#define wbr_wh_read_unlock(wbr) au_rw_read_unlock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_unlock(wbr) au_rw_write_unlock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_downgrade_lock(wbr) au_rw_dgrade_lock(&(wbr)->wbr_wh_rwsem)
#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&(wbr)->wbr_wh_rwsem)
#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&(wbr)->wbr_wh_rwsem)
#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&(wbr)->wbr_wh_rwsem)
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_FHSM
static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm)
{
mutex_init(&brfhsm->bf_lock);
brfhsm->bf_jiffy = 0;
brfhsm->bf_readable = 0;
}
static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm)
{
mutex_destroy(&brfhsm->bf_lock);
}
#else
AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm)
AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm)
#endif
#endif /* __KERNEL__ */
#endif /* __AUFS_BRANCH_H__ */