408 lines
10 KiB
C
408 lines
10 KiB
C
/*
|
|
* Copyright (c) 2003-2014 Erez Zadok
|
|
* Copyright (c) 2003-2006 Charles P. Wright
|
|
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
|
|
* Copyright (c) 2005 Arun M. Krishnakumar
|
|
* Copyright (c) 2004-2006 David P. Quigley
|
|
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
|
|
* Copyright (c) 2003 Puja Gupta
|
|
* Copyright (c) 2003 Harikesavan Krishnan
|
|
* Copyright (c) 2003-2014 Stony Brook University
|
|
* Copyright (c) 2003-2014 The Research Foundation of SUNY
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#ifndef _FANOUT_H_
|
|
#define _FANOUT_H_
|
|
|
|
/*
|
|
* Inode to private data
|
|
*
|
|
* Since we use containers and the struct inode is _inside_ the
|
|
* unionfs_inode_info structure, UNIONFS_I will always (given a non-NULL
|
|
* inode pointer), return a valid non-NULL pointer.
|
|
*/
|
|
static inline struct unionfs_inode_info *UNIONFS_I(const struct inode *inode)
|
|
{
|
|
return container_of(inode, struct unionfs_inode_info, vfs_inode);
|
|
}
|
|
|
|
#define ibstart(ino) (UNIONFS_I(ino)->bstart)
|
|
#define ibend(ino) (UNIONFS_I(ino)->bend)
|
|
|
|
/* Dentry to private data */
|
|
#define UNIONFS_D(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata)
|
|
#define dbstart(dent) (UNIONFS_D(dent)->bstart)
|
|
#define dbend(dent) (UNIONFS_D(dent)->bend)
|
|
#define dbopaque(dent) (UNIONFS_D(dent)->bopaque)
|
|
|
|
/* Superblock to private data */
|
|
#define UNIONFS_SB(super) ((struct unionfs_sb_info *)(super)->s_fs_info)
|
|
#define sbstart(sb) 0
|
|
#define sbend(sb) (UNIONFS_SB(sb)->bend)
|
|
#define sbmax(sb) (UNIONFS_SB(sb)->bend + 1)
|
|
#define sbhbid(sb) (UNIONFS_SB(sb)->high_branch_id)
|
|
|
|
/* File to private Data */
|
|
#define UNIONFS_F(file) ((struct unionfs_file_info *)((file)->private_data))
|
|
#define fbstart(file) (UNIONFS_F(file)->bstart)
|
|
#define fbend(file) (UNIONFS_F(file)->bend)
|
|
|
|
/* macros to manipulate branch IDs in stored in our superblock */
|
|
static inline int branch_id(struct super_block *sb, int index)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
return UNIONFS_SB(sb)->data[index].branch_id;
|
|
}
|
|
|
|
static inline void set_branch_id(struct super_block *sb, int index, int val)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
UNIONFS_SB(sb)->data[index].branch_id = val;
|
|
}
|
|
|
|
static inline void new_branch_id(struct super_block *sb, int index)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
set_branch_id(sb, index, ++UNIONFS_SB(sb)->high_branch_id);
|
|
}
|
|
|
|
/*
|
|
* Find new index of matching branch with an existing superblock of a known
|
|
* (possibly old) id. This is needed because branches could have been
|
|
* added/deleted causing the branches of any open files to shift.
|
|
*
|
|
* @sb: the new superblock which may have new/different branch IDs
|
|
* @id: the old/existing id we're looking for
|
|
* Returns index of newly found branch (0 or greater), -1 otherwise.
|
|
*/
|
|
static inline int branch_id_to_idx(struct super_block *sb, int id)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sbmax(sb); i++) {
|
|
if (branch_id(sb, i) == id)
|
|
return i;
|
|
}
|
|
/* in the non-ODF code, this should really never happen */
|
|
printk(KERN_WARNING "unionfs: cannot find branch with id %d\n", id);
|
|
return -1;
|
|
}
|
|
|
|
/* File to lower file. */
|
|
static inline struct file *unionfs_lower_file(const struct file *f)
|
|
{
|
|
BUG_ON(!f);
|
|
return UNIONFS_F(f)->lower_files[fbstart(f)];
|
|
}
|
|
|
|
static inline struct file *unionfs_lower_file_idx(const struct file *f,
|
|
int index)
|
|
{
|
|
BUG_ON(!f || index < 0);
|
|
return UNIONFS_F(f)->lower_files[index];
|
|
}
|
|
|
|
static inline void unionfs_set_lower_file_idx(struct file *f, int index,
|
|
struct file *val)
|
|
{
|
|
BUG_ON(!f || index < 0);
|
|
UNIONFS_F(f)->lower_files[index] = val;
|
|
/* save branch ID (may be redundant?) */
|
|
UNIONFS_F(f)->saved_branch_ids[index] =
|
|
branch_id((f)->f_path.dentry->d_sb, index);
|
|
}
|
|
|
|
static inline void unionfs_set_lower_file(struct file *f, struct file *val)
|
|
{
|
|
BUG_ON(!f);
|
|
unionfs_set_lower_file_idx((f), fbstart(f), (val));
|
|
}
|
|
|
|
/* Inode to lower inode. */
|
|
static inline struct inode *unionfs_lower_inode(const struct inode *i)
|
|
{
|
|
BUG_ON(!i);
|
|
return UNIONFS_I(i)->lower_inodes[ibstart(i)];
|
|
}
|
|
|
|
static inline struct inode *unionfs_lower_inode_idx(const struct inode *i,
|
|
int index)
|
|
{
|
|
BUG_ON(!i || index < 0);
|
|
return UNIONFS_I(i)->lower_inodes[index];
|
|
}
|
|
|
|
static inline void unionfs_set_lower_inode_idx(struct inode *i, int index,
|
|
struct inode *val)
|
|
{
|
|
BUG_ON(!i || index < 0);
|
|
UNIONFS_I(i)->lower_inodes[index] = val;
|
|
}
|
|
|
|
static inline void unionfs_set_lower_inode(struct inode *i, struct inode *val)
|
|
{
|
|
BUG_ON(!i);
|
|
UNIONFS_I(i)->lower_inodes[ibstart(i)] = val;
|
|
}
|
|
|
|
/* Superblock to lower superblock. */
|
|
static inline struct super_block *unionfs_lower_super(
|
|
const struct super_block *sb)
|
|
{
|
|
BUG_ON(!sb);
|
|
return UNIONFS_SB(sb)->data[sbstart(sb)].sb;
|
|
}
|
|
|
|
static inline struct super_block *unionfs_lower_super_idx(
|
|
const struct super_block *sb,
|
|
int index)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
return UNIONFS_SB(sb)->data[index].sb;
|
|
}
|
|
|
|
static inline void unionfs_set_lower_super_idx(struct super_block *sb,
|
|
int index,
|
|
struct super_block *val)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
UNIONFS_SB(sb)->data[index].sb = val;
|
|
}
|
|
|
|
static inline void unionfs_set_lower_super(struct super_block *sb,
|
|
struct super_block *val)
|
|
{
|
|
BUG_ON(!sb);
|
|
UNIONFS_SB(sb)->data[sbstart(sb)].sb = val;
|
|
}
|
|
|
|
/* Branch count macros. */
|
|
static inline int branch_count(const struct super_block *sb, int index)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
return atomic_read(&UNIONFS_SB(sb)->data[index].open_files);
|
|
}
|
|
|
|
static inline void set_branch_count(struct super_block *sb, int index, int val)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
atomic_set(&UNIONFS_SB(sb)->data[index].open_files, val);
|
|
}
|
|
|
|
static inline void branchget(struct super_block *sb, int index)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
atomic_inc(&UNIONFS_SB(sb)->data[index].open_files);
|
|
}
|
|
|
|
static inline void branchput(struct super_block *sb, int index)
|
|
{
|
|
BUG_ON(!sb || index < 0);
|
|
atomic_dec(&UNIONFS_SB(sb)->data[index].open_files);
|
|
}
|
|
|
|
/* Dentry macros */
|
|
static inline void unionfs_set_lower_dentry_idx(struct dentry *dent, int index,
|
|
struct dentry *val)
|
|
{
|
|
BUG_ON(!dent || index < 0);
|
|
UNIONFS_D(dent)->lower_paths[index].dentry = val;
|
|
}
|
|
|
|
static inline struct dentry *unionfs_lower_dentry_idx(
|
|
const struct dentry *dent,
|
|
int index)
|
|
{
|
|
BUG_ON(!dent || index < 0);
|
|
return UNIONFS_D(dent)->lower_paths[index].dentry;
|
|
}
|
|
|
|
static inline struct dentry *unionfs_lower_dentry(const struct dentry *dent)
|
|
{
|
|
BUG_ON(!dent);
|
|
return unionfs_lower_dentry_idx(dent, dbstart(dent));
|
|
}
|
|
|
|
static inline void unionfs_set_lower_mnt_idx(struct dentry *dent, int index,
|
|
struct vfsmount *mnt)
|
|
{
|
|
BUG_ON(!dent || index < 0);
|
|
UNIONFS_D(dent)->lower_paths[index].mnt = mnt;
|
|
}
|
|
|
|
static inline struct vfsmount *unionfs_lower_mnt_idx(
|
|
const struct dentry *dent,
|
|
int index)
|
|
{
|
|
BUG_ON(!dent || index < 0);
|
|
return UNIONFS_D(dent)->lower_paths[index].mnt;
|
|
}
|
|
|
|
static inline struct vfsmount *unionfs_lower_mnt(const struct dentry *dent)
|
|
{
|
|
BUG_ON(!dent);
|
|
return unionfs_lower_mnt_idx(dent, dbstart(dent));
|
|
}
|
|
|
|
/* Macros for locking a dentry. */
|
|
enum unionfs_dentry_lock_class {
|
|
UNIONFS_DMUTEX_NORMAL,
|
|
UNIONFS_DMUTEX_ROOT,
|
|
UNIONFS_DMUTEX_PARENT,
|
|
UNIONFS_DMUTEX_CHILD,
|
|
UNIONFS_DMUTEX_WHITEOUT,
|
|
UNIONFS_DMUTEX_REVAL_PARENT, /* for file/dentry revalidate */
|
|
UNIONFS_DMUTEX_REVAL_CHILD, /* for file/dentry revalidate */
|
|
};
|
|
|
|
static inline void unionfs_lock_dentry(struct dentry *d,
|
|
unsigned int subclass)
|
|
{
|
|
BUG_ON(!d);
|
|
mutex_lock_nested(&UNIONFS_D(d)->lock, subclass);
|
|
}
|
|
|
|
static inline void unionfs_unlock_dentry(struct dentry *d)
|
|
{
|
|
BUG_ON(!d);
|
|
mutex_unlock(&UNIONFS_D(d)->lock);
|
|
}
|
|
|
|
static inline struct dentry *unionfs_lock_parent(struct dentry *d,
|
|
unsigned int subclass)
|
|
{
|
|
struct dentry *p;
|
|
|
|
BUG_ON(!d);
|
|
p = dget_parent(d);
|
|
if (p != d)
|
|
mutex_lock_nested(&UNIONFS_D(p)->lock, subclass);
|
|
return p;
|
|
}
|
|
|
|
static inline void unionfs_unlock_parent(struct dentry *d, struct dentry *p)
|
|
{
|
|
BUG_ON(!d);
|
|
BUG_ON(!p);
|
|
if (p != d) {
|
|
BUG_ON(!mutex_is_locked(&UNIONFS_D(p)->lock));
|
|
mutex_unlock(&UNIONFS_D(p)->lock);
|
|
}
|
|
dput(p);
|
|
}
|
|
|
|
static inline void verify_locked(struct dentry *d)
|
|
{
|
|
BUG_ON(!d);
|
|
BUG_ON(!mutex_is_locked(&UNIONFS_D(d)->lock));
|
|
}
|
|
|
|
/* macros to put lower objects */
|
|
|
|
/*
|
|
* iput lower inodes of an unionfs dentry, from bstart to bend. If
|
|
* @free_lower is true, then also kfree the memory used to hold the lower
|
|
* object pointers.
|
|
*/
|
|
static inline void iput_lowers(struct inode *inode,
|
|
int bstart, int bend, bool free_lower)
|
|
{
|
|
struct inode *lower_inode;
|
|
int bindex;
|
|
|
|
BUG_ON(!inode);
|
|
BUG_ON(!UNIONFS_I(inode));
|
|
BUG_ON(bstart < 0);
|
|
|
|
for (bindex = bstart; bindex <= bend; bindex++) {
|
|
lower_inode = unionfs_lower_inode_idx(inode, bindex);
|
|
if (lower_inode) {
|
|
unionfs_set_lower_inode_idx(inode, bindex, NULL);
|
|
/* see Documentation/filesystems/unionfs/issues.txt */
|
|
lockdep_off();
|
|
iput(lower_inode);
|
|
lockdep_on();
|
|
}
|
|
}
|
|
|
|
if (free_lower) {
|
|
kfree(UNIONFS_I(inode)->lower_inodes);
|
|
UNIONFS_I(inode)->lower_inodes = NULL;
|
|
}
|
|
}
|
|
|
|
/* iput all lower inodes, and reset start/end branch indices to -1 */
|
|
static inline void iput_lowers_all(struct inode *inode, bool free_lower)
|
|
{
|
|
int bstart, bend;
|
|
|
|
BUG_ON(!inode);
|
|
BUG_ON(!UNIONFS_I(inode));
|
|
bstart = ibstart(inode);
|
|
bend = ibend(inode);
|
|
BUG_ON(bstart < 0);
|
|
|
|
iput_lowers(inode, bstart, bend, free_lower);
|
|
ibstart(inode) = ibend(inode) = -1;
|
|
}
|
|
|
|
/*
|
|
* dput/mntput all lower dentries and vfsmounts of an unionfs dentry, from
|
|
* bstart to bend. If @free_lower is true, then also kfree the memory used
|
|
* to hold the lower object pointers.
|
|
*
|
|
* XXX: implement using path_put VFS macros
|
|
*/
|
|
static inline void path_put_lowers(struct dentry *dentry,
|
|
int bstart, int bend, bool free_lower)
|
|
{
|
|
struct dentry *lower_dentry;
|
|
struct vfsmount *lower_mnt;
|
|
int bindex;
|
|
|
|
BUG_ON(!dentry);
|
|
BUG_ON(!UNIONFS_D(dentry));
|
|
BUG_ON(bstart < 0);
|
|
|
|
for (bindex = bstart; bindex <= bend; bindex++) {
|
|
lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
|
|
if (lower_dentry) {
|
|
unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
|
|
dput(lower_dentry);
|
|
}
|
|
lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
|
|
if (lower_mnt) {
|
|
unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
|
|
mntput(lower_mnt);
|
|
}
|
|
}
|
|
|
|
if (free_lower) {
|
|
kfree(UNIONFS_D(dentry)->lower_paths);
|
|
UNIONFS_D(dentry)->lower_paths = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* dput/mntput all lower dentries and vfsmounts, and reset start/end branch
|
|
* indices to -1.
|
|
*/
|
|
static inline void path_put_lowers_all(struct dentry *dentry, bool free_lower)
|
|
{
|
|
int bstart, bend;
|
|
|
|
BUG_ON(!dentry);
|
|
BUG_ON(!UNIONFS_D(dentry));
|
|
bstart = dbstart(dentry);
|
|
bend = dbend(dentry);
|
|
BUG_ON(bstart < 0);
|
|
|
|
path_put_lowers(dentry, bstart, bend, free_lower);
|
|
dbstart(dentry) = dbend(dentry) = -1;
|
|
}
|
|
|
|
#endif /* not _FANOUT_H */
|