Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

21
fs/coda/Kconfig Normal file
View file

@ -0,0 +1,21 @@
config CODA_FS
tristate "Coda file system support (advanced network fs)"
depends on INET
help
Coda is an advanced network file system, similar to NFS in that it
enables you to mount file systems of a remote server and access them
with regular Unix commands as if they were sitting on your hard
disk. Coda has several advantages over NFS: support for
disconnected operation (e.g. for laptops), read/write server
replication, security model for authentication and encryption,
persistent client caches and write back caching.
If you say Y here, your Linux box will be able to act as a Coda
*client*. You will need user level code as well, both for the
client and server. Servers are currently user level, i.e. they need
no kernel support. Please read
<file:Documentation/filesystems/coda.txt> and check out the Coda
home page <http://www.coda.cs.cmu.edu/>.
To compile the coda client support as a module, choose M here: the
module will be called coda.

12
fs/coda/Makefile Normal file
View file

@ -0,0 +1,12 @@
#
# Makefile for the Linux Coda filesystem routines.
#
obj-$(CONFIG_CODA_FS) += coda.o
coda-objs := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o \
coda_linux.o symlink.o pioctl.o sysctl.o
# If you want debugging output, please uncomment the following line.
# ccflags-y := -DDEBUG -DDEBUG_SMB_MALLOC=1

118
fs/coda/cache.c Normal file
View file

@ -0,0 +1,118 @@
/*
* Cache operations for Coda.
* For Linux 2.1: (C) 1997 Carnegie Mellon University
* For Linux 2.3: (C) 2000 Carnegie Mellon University
*
* Carnegie Mellon encourages users of this code to contribute improvements
* to the Coda project http://www.coda.cs.cmu.edu/ <coda@cs.cmu.edu>.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
#include "coda_cache.h"
static atomic_t permission_epoch = ATOMIC_INIT(0);
/* replace or extend an acl cache hit */
void coda_cache_enter(struct inode *inode, int mask)
{
struct coda_inode_info *cii = ITOC(inode);
spin_lock(&cii->c_lock);
cii->c_cached_epoch = atomic_read(&permission_epoch);
if (!uid_eq(cii->c_uid, current_fsuid())) {
cii->c_uid = current_fsuid();
cii->c_cached_perm = mask;
} else
cii->c_cached_perm |= mask;
spin_unlock(&cii->c_lock);
}
/* remove cached acl from an inode */
void coda_cache_clear_inode(struct inode *inode)
{
struct coda_inode_info *cii = ITOC(inode);
spin_lock(&cii->c_lock);
cii->c_cached_epoch = atomic_read(&permission_epoch) - 1;
spin_unlock(&cii->c_lock);
}
/* remove all acl caches */
void coda_cache_clear_all(struct super_block *sb)
{
atomic_inc(&permission_epoch);
}
/* check if the mask has been matched against the acl already */
int coda_cache_check(struct inode *inode, int mask)
{
struct coda_inode_info *cii = ITOC(inode);
int hit;
spin_lock(&cii->c_lock);
hit = (mask & cii->c_cached_perm) == mask &&
uid_eq(cii->c_uid, current_fsuid()) &&
cii->c_cached_epoch == atomic_read(&permission_epoch);
spin_unlock(&cii->c_lock);
return hit;
}
/* Purging dentries and children */
/* The following routines drop dentries which are not
in use and flag dentries which are in use to be
zapped later.
The flags are detected by:
- coda_dentry_revalidate (for lookups) if the flag is C_PURGE
- coda_dentry_delete: to remove dentry from the cache when d_count
falls to zero
- an inode method coda_revalidate (for attributes) if the
flag is C_VATTR
*/
/* this won't do any harm: just flag all children */
static void coda_flag_children(struct dentry *parent, int flag)
{
struct dentry *de;
spin_lock(&parent->d_lock);
list_for_each_entry(de, &parent->d_subdirs, d_child) {
/* don't know what to do with negative dentries */
if (de->d_inode )
coda_flag_inode(de->d_inode, flag);
}
spin_unlock(&parent->d_lock);
return;
}
void coda_flag_inode_children(struct inode *inode, int flag)
{
struct dentry *alias_de;
if ( !inode || !S_ISDIR(inode->i_mode))
return;
alias_de = d_find_alias(inode);
if (!alias_de)
return;
coda_flag_children(alias_de, flag);
shrink_dcache_parent(alias_de);
dput(alias_de);
}

168
fs/coda/cnode.c Normal file
View file

@ -0,0 +1,168 @@
/* cnode related routines for the coda kernel code
(C) 1996 Peter Braam
*/
#include <linux/types.h>
#include <linux/string.h>
#include <linux/time.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
{
return memcmp(fid1, fid2, sizeof(*fid1)) == 0;
}
static const struct inode_operations coda_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.put_link = page_put_link,
.setattr = coda_setattr,
};
/* cnode.c */
static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
{
coda_vattr_to_iattr(inode, attr);
if (S_ISREG(inode->i_mode)) {
inode->i_op = &coda_file_inode_operations;
inode->i_fop = &coda_file_operations;
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &coda_dir_inode_operations;
inode->i_fop = &coda_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &coda_symlink_inode_operations;
inode->i_data.a_ops = &coda_symlink_aops;
inode->i_mapping = &inode->i_data;
} else
init_special_inode(inode, inode->i_mode, huge_decode_dev(attr->va_rdev));
}
static int coda_test_inode(struct inode *inode, void *data)
{
struct CodaFid *fid = (struct CodaFid *)data;
struct coda_inode_info *cii = ITOC(inode);
return coda_fideq(&cii->c_fid, fid);
}
static int coda_set_inode(struct inode *inode, void *data)
{
struct CodaFid *fid = (struct CodaFid *)data;
struct coda_inode_info *cii = ITOC(inode);
cii->c_fid = *fid;
return 0;
}
struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
struct coda_vattr * attr)
{
struct inode *inode;
struct coda_inode_info *cii;
unsigned long hash = coda_f2i(fid);
inode = iget5_locked(sb, hash, coda_test_inode, coda_set_inode, fid);
if (!inode)
return ERR_PTR(-ENOMEM);
if (inode->i_state & I_NEW) {
cii = ITOC(inode);
/* we still need to set i_ino for things like stat(2) */
inode->i_ino = hash;
/* inode is locked and unique, no need to grab cii->c_lock */
cii->c_mapcount = 0;
unlock_new_inode(inode);
}
/* always replace the attributes, type might have changed */
coda_fill_inode(inode, attr);
return inode;
}
/* this is effectively coda_iget:
- get attributes (might be cached)
- get the inode for the fid using vfs iget
- link the two up if this is needed
- fill in the attributes
*/
struct inode *coda_cnode_make(struct CodaFid *fid, struct super_block *sb)
{
struct coda_vattr attr;
struct inode *inode;
int error;
/* We get inode numbers from Venus -- see venus source */
error = venus_getattr(sb, fid, &attr);
if (error)
return ERR_PTR(error);
inode = coda_iget(sb, fid, &attr);
if (IS_ERR(inode))
pr_warn("%s: coda_iget failed\n", __func__);
return inode;
}
/* Although we treat Coda file identifiers as immutable, there is one
* special case for files created during a disconnection where they may
* not be globally unique. When an identifier collision is detected we
* first try to flush the cached inode from the kernel and finally
* resort to renaming/rehashing in-place. Userspace remembers both old
* and new values of the identifier to handle any in-flight upcalls.
* The real solution is to use globally unique UUIDs as identifiers, but
* retrofitting the existing userspace code for this is non-trivial. */
void coda_replace_fid(struct inode *inode, struct CodaFid *oldfid,
struct CodaFid *newfid)
{
struct coda_inode_info *cii = ITOC(inode);
unsigned long hash = coda_f2i(newfid);
BUG_ON(!coda_fideq(&cii->c_fid, oldfid));
/* replace fid and rehash inode */
/* XXX we probably need to hold some lock here! */
remove_inode_hash(inode);
cii->c_fid = *newfid;
inode->i_ino = hash;
__insert_inode_hash(inode, hash);
}
/* convert a fid to an inode. */
struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb)
{
struct inode *inode;
unsigned long hash = coda_f2i(fid);
if ( !sb ) {
pr_warn("%s: no sb!\n", __func__);
return NULL;
}
inode = ilookup5(sb, hash, coda_test_inode, fid);
if ( !inode )
return NULL;
/* we should never see newly created inodes because we intentionally
* fail in the initialization callback */
BUG_ON(inode->i_state & I_NEW);
return inode;
}
/* the CONTROL inode is made without asking attributes from Venus */
struct inode *coda_cnode_makectl(struct super_block *sb)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_ino = CTL_INO;
inode->i_op = &coda_ioctl_inode_operations;
inode->i_fop = &coda_ioctl_operations;
inode->i_mode = 0444;
return inode;
}
return ERR_PTR(-ENOMEM);
}

22
fs/coda/coda_cache.h Normal file
View file

@ -0,0 +1,22 @@
/* Coda filesystem -- Linux Minicache
*
* Copyright (C) 1989 - 1997 Carnegie Mellon University
*
* Carnegie Mellon University encourages users of this software to
* contribute improvements to the Coda project. Contact Peter Braam
* <coda@cs.cmu.edu>
*/
#ifndef _CFSNC_HEADER_
#define _CFSNC_HEADER_
/* credential cache */
void coda_cache_enter(struct inode *inode, int mask);
void coda_cache_clear_inode(struct inode *);
void coda_cache_clear_all(struct super_block *sb);
int coda_cache_check(struct inode *inode, int mask);
/* for downcalls and attributes and lookups */
void coda_flag_inode_children(struct inode *inode, int flag);
#endif /* _CFSNC_HEADER_ */

58
fs/coda/coda_fs_i.h Normal file
View file

@ -0,0 +1,58 @@
/*
* coda_fs_i.h
*
* Copyright (C) 1998 Carnegie Mellon University
*
*/
#ifndef _LINUX_CODA_FS_I
#define _LINUX_CODA_FS_I
#include <linux/types.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/coda.h>
/*
* coda fs inode data
* c_lock protects accesses to c_flags, c_mapcount, c_cached_epoch, c_uid and
* c_cached_perm.
* vfs_inode is set only when the inode is created and never changes.
* c_fid is set when the inode is created and should be considered immutable.
*/
struct coda_inode_info {
struct CodaFid c_fid; /* Coda identifier */
u_short c_flags; /* flags (see below) */
unsigned int c_mapcount; /* nr of times this inode is mapped */
unsigned int c_cached_epoch; /* epoch for cached permissions */
kuid_t c_uid; /* fsuid for cached permissions */
unsigned int c_cached_perm; /* cached access permissions */
spinlock_t c_lock;
struct inode vfs_inode;
};
/*
* coda fs file private data
*/
#define CODA_MAGIC 0xC0DAC0DA
struct coda_file_info {
int cfi_magic; /* magic number */
struct file *cfi_container; /* container file for this cnode */
unsigned int cfi_mapcount; /* nr of times this file is mapped */
};
#define CODA_FTOC(file) ((struct coda_file_info *)((file)->private_data))
/* flags */
#define C_VATTR 0x1 /* Validity of vattr in inode */
#define C_FLUSH 0x2 /* used after a flush */
#define C_DYING 0x4 /* from venus (which died) */
#define C_PURGE 0x8
struct inode *coda_cnode_make(struct CodaFid *, struct super_block *);
struct inode *coda_iget(struct super_block *sb, struct CodaFid *fid, struct coda_vattr *attr);
struct inode *coda_cnode_makectl(struct super_block *sb);
struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb);
void coda_replace_fid(struct inode *, struct CodaFid *, struct CodaFid *);
#endif

20
fs/coda/coda_int.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef _CODA_INT_
#define _CODA_INT_
struct dentry;
struct file;
extern struct file_system_type coda_fs_type;
extern unsigned long coda_timeout;
extern int coda_hard;
extern int coda_fake_statfs;
void coda_destroy_inodecache(void);
int __init coda_init_inodecache(void);
int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync);
void coda_sysctl_init(void);
void coda_sysctl_clean(void);
#endif /* _CODA_INT_ */

192
fs/coda/coda_linux.c Normal file
View file

@ -0,0 +1,192 @@
/*
* Inode operations for Coda filesystem
* Original version: (C) 1996 P. Braam and M. Callahan
* Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users to contribute improvements to
* the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
/* initialize the debugging variables */
int coda_fake_statfs;
/* print a fid */
char * coda_f2s(struct CodaFid *f)
{
static char s[60];
sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]);
return s;
}
/* recognize special .CONTROL name */
int coda_iscontrol(const char *name, size_t length)
{
return ((CODA_CONTROLLEN == length) &&
(strncmp(name, CODA_CONTROL, CODA_CONTROLLEN) == 0));
}
/* recognize /coda inode */
int coda_isroot(struct inode *i)
{
return ( i->i_sb->s_root->d_inode == i );
}
unsigned short coda_flags_to_cflags(unsigned short flags)
{
unsigned short coda_flags = 0;
if ((flags & O_ACCMODE) == O_RDONLY)
coda_flags |= C_O_READ;
if ((flags & O_ACCMODE) == O_RDWR)
coda_flags |= C_O_READ | C_O_WRITE;
if ((flags & O_ACCMODE) == O_WRONLY)
coda_flags |= C_O_WRITE;
if (flags & O_TRUNC)
coda_flags |= C_O_TRUNC;
if (flags & O_CREAT)
coda_flags |= C_O_CREAT;
if (flags & O_EXCL)
coda_flags |= C_O_EXCL;
return coda_flags;
}
/* utility functions below */
void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
{
int inode_type;
/* inode's i_flags, i_ino are set by iget
XXX: is this all we need ??
*/
switch (attr->va_type) {
case C_VNON:
inode_type = 0;
break;
case C_VREG:
inode_type = S_IFREG;
break;
case C_VDIR:
inode_type = S_IFDIR;
break;
case C_VLNK:
inode_type = S_IFLNK;
break;
default:
inode_type = 0;
}
inode->i_mode |= inode_type;
if (attr->va_mode != (u_short) -1)
inode->i_mode = attr->va_mode | inode_type;
if (attr->va_uid != -1)
inode->i_uid = make_kuid(&init_user_ns, (uid_t) attr->va_uid);
if (attr->va_gid != -1)
inode->i_gid = make_kgid(&init_user_ns, (gid_t) attr->va_gid);
if (attr->va_nlink != -1)
set_nlink(inode, attr->va_nlink);
if (attr->va_size != -1)
inode->i_size = attr->va_size;
if (attr->va_size != -1)
inode->i_blocks = (attr->va_size + 511) >> 9;
if (attr->va_atime.tv_sec != -1)
inode->i_atime = attr->va_atime;
if (attr->va_mtime.tv_sec != -1)
inode->i_mtime = attr->va_mtime;
if (attr->va_ctime.tv_sec != -1)
inode->i_ctime = attr->va_ctime;
}
/*
* BSD sets attributes that need not be modified to -1.
* Linux uses the valid field to indicate what should be
* looked at. The BSD type field needs to be deduced from linux
* mode.
* So we have to do some translations here.
*/
void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
{
unsigned int valid;
/* clean out */
vattr->va_mode = -1;
vattr->va_uid = (vuid_t) -1;
vattr->va_gid = (vgid_t) -1;
vattr->va_size = (off_t) -1;
vattr->va_atime.tv_sec = (time_t) -1;
vattr->va_atime.tv_nsec = (time_t) -1;
vattr->va_mtime.tv_sec = (time_t) -1;
vattr->va_mtime.tv_nsec = (time_t) -1;
vattr->va_ctime.tv_sec = (time_t) -1;
vattr->va_ctime.tv_nsec = (time_t) -1;
vattr->va_type = C_VNON;
vattr->va_fileid = -1;
vattr->va_gen = -1;
vattr->va_bytes = -1;
vattr->va_nlink = -1;
vattr->va_blocksize = -1;
vattr->va_rdev = -1;
vattr->va_flags = 0;
/* determine the type */
#if 0
mode = iattr->ia_mode;
if ( S_ISDIR(mode) ) {
vattr->va_type = C_VDIR;
} else if ( S_ISREG(mode) ) {
vattr->va_type = C_VREG;
} else if ( S_ISLNK(mode) ) {
vattr->va_type = C_VLNK;
} else {
/* don't do others */
vattr->va_type = C_VNON;
}
#endif
/* set those vattrs that need change */
valid = iattr->ia_valid;
if ( valid & ATTR_MODE ) {
vattr->va_mode = iattr->ia_mode;
}
if ( valid & ATTR_UID ) {
vattr->va_uid = (vuid_t) from_kuid(&init_user_ns, iattr->ia_uid);
}
if ( valid & ATTR_GID ) {
vattr->va_gid = (vgid_t) from_kgid(&init_user_ns, iattr->ia_gid);
}
if ( valid & ATTR_SIZE ) {
vattr->va_size = iattr->ia_size;
}
if ( valid & ATTR_ATIME ) {
vattr->va_atime = iattr->ia_atime;
}
if ( valid & ATTR_MTIME ) {
vattr->va_mtime = iattr->ia_mtime;
}
if ( valid & ATTR_CTIME ) {
vattr->va_ctime = iattr->ia_ctime;
}
}

106
fs/coda/coda_linux.h Normal file
View file

@ -0,0 +1,106 @@
/*
* Coda File System, Linux Kernel module
*
* Original version, adapted from cfs_mach.c, (C) Carnegie Mellon University
* Linux modifications (C) 1996, Peter J. Braam
* Rewritten for Linux 2.1 (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon University encourages users of this software to
* contribute improvements to the Coda project.
*/
#ifndef _LINUX_CODA_FS
#define _LINUX_CODA_FS
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/fs.h>
#include "coda_fs_i.h"
/* operations */
extern const struct inode_operations coda_dir_inode_operations;
extern const struct inode_operations coda_file_inode_operations;
extern const struct inode_operations coda_ioctl_inode_operations;
extern const struct dentry_operations coda_dentry_operations;
extern const struct address_space_operations coda_file_aops;
extern const struct address_space_operations coda_symlink_aops;
extern const struct file_operations coda_dir_operations;
extern const struct file_operations coda_file_operations;
extern const struct file_operations coda_ioctl_operations;
/* operations shared over more than one file */
int coda_open(struct inode *i, struct file *f);
int coda_release(struct inode *i, struct file *f);
int coda_permission(struct inode *inode, int mask);
int coda_revalidate_inode(struct inode *);
int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
int coda_setattr(struct dentry *, struct iattr *);
/* this file: heloers */
char *coda_f2s(struct CodaFid *f);
int coda_isroot(struct inode *i);
int coda_iscontrol(const char *name, size_t length);
void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
unsigned short coda_flags_to_cflags(unsigned short);
/* sysctl.h */
void coda_sysctl_init(void);
void coda_sysctl_clean(void);
#define CODA_ALLOC(ptr, cast, size) do { \
if (size < PAGE_SIZE) \
ptr = kzalloc((unsigned long) size, GFP_KERNEL); \
else \
ptr = (cast)vzalloc((unsigned long) size); \
if (!ptr) \
pr_warn("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \
} while (0)
#define CODA_FREE(ptr,size) \
do { if (size < PAGE_SIZE) kfree((ptr)); else vfree((ptr)); } while (0)
/* inode to cnode access functions */
static inline struct coda_inode_info *ITOC(struct inode *inode)
{
return list_entry(inode, struct coda_inode_info, vfs_inode);
}
static __inline__ struct CodaFid *coda_i2f(struct inode *inode)
{
return &(ITOC(inode)->c_fid);
}
static __inline__ char *coda_i2s(struct inode *inode)
{
return coda_f2s(&(ITOC(inode)->c_fid));
}
/* this will not zap the inode away */
static __inline__ void coda_flag_inode(struct inode *inode, int flag)
{
struct coda_inode_info *cii = ITOC(inode);
spin_lock(&cii->c_lock);
cii->c_flags |= flag;
spin_unlock(&cii->c_lock);
}
#endif

607
fs/coda/dir.c Normal file
View file

@ -0,0 +1,607 @@
/*
* Directory operations for Coda filesystem
* Original version: (C) 1996 P. Braam and M. Callahan
* Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users to contribute improvements to
* the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/namei.h>
#include <linux/uaccess.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
#include "coda_cache.h"
#include "coda_int.h"
/* dir inode-ops */
static int coda_create(struct inode *dir, struct dentry *new, umode_t mode, bool excl);
static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, unsigned int flags);
static int coda_link(struct dentry *old_dentry, struct inode *dir_inode,
struct dentry *entry);
static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
const char *symname);
static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, umode_t mode);
static int coda_rmdir(struct inode *dir_inode, struct dentry *entry);
static int coda_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry);
/* dir file-ops */
static int coda_readdir(struct file *file, struct dir_context *ctx);
/* dentry ops */
static int coda_dentry_revalidate(struct dentry *de, unsigned int flags);
static int coda_dentry_delete(const struct dentry *);
/* support routines */
static int coda_venus_readdir(struct file *, struct dir_context *);
/* same as fs/bad_inode.c */
static int coda_return_EIO(void)
{
return -EIO;
}
#define CODA_EIO_ERROR ((void *) (coda_return_EIO))
const struct dentry_operations coda_dentry_operations =
{
.d_revalidate = coda_dentry_revalidate,
.d_delete = coda_dentry_delete,
};
const struct inode_operations coda_dir_inode_operations =
{
.create = coda_create,
.lookup = coda_lookup,
.link = coda_link,
.unlink = coda_unlink,
.symlink = coda_symlink,
.mkdir = coda_mkdir,
.rmdir = coda_rmdir,
.mknod = CODA_EIO_ERROR,
.rename = coda_rename,
.permission = coda_permission,
.getattr = coda_getattr,
.setattr = coda_setattr,
};
const struct file_operations coda_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate = coda_readdir,
.open = coda_open,
.release = coda_release,
.fsync = coda_fsync,
};
/* inode operations for directories */
/* access routines: lookup, readlink, permission */
static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsigned int flags)
{
struct super_block *sb = dir->i_sb;
const char *name = entry->d_name.name;
size_t length = entry->d_name.len;
struct inode *inode;
int type = 0;
if (length > CODA_MAXNAMLEN) {
pr_err("name too long: lookup, %s (%*s)\n",
coda_i2s(dir), (int)length, name);
return ERR_PTR(-ENAMETOOLONG);
}
/* control object, create inode on the fly */
if (coda_isroot(dir) && coda_iscontrol(name, length)) {
inode = coda_cnode_makectl(sb);
type = CODA_NOCACHE;
} else {
struct CodaFid fid = { { 0, } };
int error = venus_lookup(sb, coda_i2f(dir), name, length,
&type, &fid);
inode = !error ? coda_cnode_make(&fid, sb) : ERR_PTR(error);
}
if (!IS_ERR(inode) && (type & CODA_NOCACHE))
coda_flag_inode(inode, C_VATTR | C_PURGE);
if (inode == ERR_PTR(-ENOENT))
inode = NULL;
return d_splice_alias(inode, entry);
}
int coda_permission(struct inode *inode, int mask)
{
int error;
if (mask & MAY_NOT_BLOCK)
return -ECHILD;
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
if (!mask)
return 0;
if ((mask & MAY_EXEC) && !execute_ok(inode))
return -EACCES;
if (coda_cache_check(inode, mask))
return 0;
error = venus_access(inode->i_sb, coda_i2f(inode), mask);
if (!error)
coda_cache_enter(inode, mask);
return error;
}
static inline void coda_dir_update_mtime(struct inode *dir)
{
#ifdef REQUERY_VENUS_FOR_MTIME
/* invalidate the directory cnode's attributes so we refetch the
* attributes from venus next time the inode is referenced */
coda_flag_inode(dir, C_VATTR);
#else
/* optimistically we can also act as if our nose bleeds. The
* granularity of the mtime is coarse anyways so we might actually be
* right most of the time. Note: we only do this for directories. */
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
#endif
}
/* we have to wrap inc_nlink/drop_nlink because sometimes userspace uses a
* trick to fool GNU find's optimizations. If we can't be sure of the link
* (because of volume mount points) we set i_nlink to 1 which forces find
* to consider every child as a possible directory. We should also never
* see an increment or decrement for deleted directories where i_nlink == 0 */
static inline void coda_dir_inc_nlink(struct inode *dir)
{
if (dir->i_nlink >= 2)
inc_nlink(dir);
}
static inline void coda_dir_drop_nlink(struct inode *dir)
{
if (dir->i_nlink > 2)
drop_nlink(dir);
}
/* creation routines: create, mknod, mkdir, link, symlink */
static int coda_create(struct inode *dir, struct dentry *de, umode_t mode, bool excl)
{
int error;
const char *name=de->d_name.name;
int length=de->d_name.len;
struct inode *inode;
struct CodaFid newfid;
struct coda_vattr attrs;
if (coda_isroot(dir) && coda_iscontrol(name, length))
return -EPERM;
error = venus_create(dir->i_sb, coda_i2f(dir), name, length,
0, mode, &newfid, &attrs);
if (error)
goto err_out;
inode = coda_iget(dir->i_sb, &newfid, &attrs);
if (IS_ERR(inode)) {
error = PTR_ERR(inode);
goto err_out;
}
/* invalidate the directory cnode's attributes */
coda_dir_update_mtime(dir);
d_instantiate(de, inode);
return 0;
err_out:
d_drop(de);
return error;
}
static int coda_mkdir(struct inode *dir, struct dentry *de, umode_t mode)
{
struct inode *inode;
struct coda_vattr attrs;
const char *name = de->d_name.name;
int len = de->d_name.len;
int error;
struct CodaFid newfid;
if (coda_isroot(dir) && coda_iscontrol(name, len))
return -EPERM;
attrs.va_mode = mode;
error = venus_mkdir(dir->i_sb, coda_i2f(dir),
name, len, &newfid, &attrs);
if (error)
goto err_out;
inode = coda_iget(dir->i_sb, &newfid, &attrs);
if (IS_ERR(inode)) {
error = PTR_ERR(inode);
goto err_out;
}
/* invalidate the directory cnode's attributes */
coda_dir_inc_nlink(dir);
coda_dir_update_mtime(dir);
d_instantiate(de, inode);
return 0;
err_out:
d_drop(de);
return error;
}
/* try to make de an entry in dir_inodde linked to source_de */
static int coda_link(struct dentry *source_de, struct inode *dir_inode,
struct dentry *de)
{
struct inode *inode = source_de->d_inode;
const char * name = de->d_name.name;
int len = de->d_name.len;
int error;
if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
return -EPERM;
error = venus_link(dir_inode->i_sb, coda_i2f(inode),
coda_i2f(dir_inode), (const char *)name, len);
if (error) {
d_drop(de);
return error;
}
coda_dir_update_mtime(dir_inode);
ihold(inode);
d_instantiate(de, inode);
inc_nlink(inode);
return 0;
}
static int coda_symlink(struct inode *dir_inode, struct dentry *de,
const char *symname)
{
const char *name = de->d_name.name;
int len = de->d_name.len;
int symlen;
int error;
if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
return -EPERM;
symlen = strlen(symname);
if (symlen > CODA_MAXPATHLEN)
return -ENAMETOOLONG;
/*
* This entry is now negative. Since we do not create
* an inode for the entry we have to drop it.
*/
d_drop(de);
error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len,
symname, symlen);
/* mtime is no good anymore */
if (!error)
coda_dir_update_mtime(dir_inode);
return error;
}
/* destruction routines: unlink, rmdir */
static int coda_unlink(struct inode *dir, struct dentry *de)
{
int error;
const char *name = de->d_name.name;
int len = de->d_name.len;
error = venus_remove(dir->i_sb, coda_i2f(dir), name, len);
if (error)
return error;
coda_dir_update_mtime(dir);
drop_nlink(de->d_inode);
return 0;
}
static int coda_rmdir(struct inode *dir, struct dentry *de)
{
const char *name = de->d_name.name;
int len = de->d_name.len;
int error;
error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
if (!error) {
/* VFS may delete the child */
if (de->d_inode)
clear_nlink(de->d_inode);
/* fix the link count of the parent */
coda_dir_drop_nlink(dir);
coda_dir_update_mtime(dir);
}
return error;
}
/* rename */
static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
const char *old_name = old_dentry->d_name.name;
const char *new_name = new_dentry->d_name.name;
int old_length = old_dentry->d_name.len;
int new_length = new_dentry->d_name.len;
int error;
error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
coda_i2f(new_dir), old_length, new_length,
(const char *) old_name, (const char *)new_name);
if (!error) {
if (new_dentry->d_inode) {
if (S_ISDIR(new_dentry->d_inode->i_mode)) {
coda_dir_drop_nlink(old_dir);
coda_dir_inc_nlink(new_dir);
}
coda_dir_update_mtime(old_dir);
coda_dir_update_mtime(new_dir);
coda_flag_inode(new_dentry->d_inode, C_VATTR);
} else {
coda_flag_inode(old_dir, C_VATTR);
coda_flag_inode(new_dir, C_VATTR);
}
}
return error;
}
/* file operations for directories */
static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
{
struct coda_file_info *cfi;
struct file *host_file;
int ret;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
if (host_file->f_op->iterate) {
struct inode *host_inode = file_inode(host_file);
mutex_lock(&host_inode->i_mutex);
ret = -ENOENT;
if (!IS_DEADDIR(host_inode)) {
ret = host_file->f_op->iterate(host_file, ctx);
file_accessed(host_file);
}
mutex_unlock(&host_inode->i_mutex);
return ret;
}
/* Venus: we must read Venus dirents from a file */
return coda_venus_readdir(coda_file, ctx);
}
static inline unsigned int CDT2DT(unsigned char cdt)
{
unsigned int dt;
switch(cdt) {
case CDT_UNKNOWN: dt = DT_UNKNOWN; break;
case CDT_FIFO: dt = DT_FIFO; break;
case CDT_CHR: dt = DT_CHR; break;
case CDT_DIR: dt = DT_DIR; break;
case CDT_BLK: dt = DT_BLK; break;
case CDT_REG: dt = DT_REG; break;
case CDT_LNK: dt = DT_LNK; break;
case CDT_SOCK: dt = DT_SOCK; break;
case CDT_WHT: dt = DT_WHT; break;
default: dt = DT_UNKNOWN; break;
}
return dt;
}
/* support routines */
static int coda_venus_readdir(struct file *coda_file, struct dir_context *ctx)
{
struct coda_file_info *cfi;
struct coda_inode_info *cii;
struct file *host_file;
struct dentry *de;
struct venus_dirent *vdir;
unsigned long vdir_size = offsetof(struct venus_dirent, d_name);
unsigned int type;
struct qstr name;
ino_t ino;
int ret;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
de = coda_file->f_path.dentry;
cii = ITOC(de->d_inode);
vdir = kmalloc(sizeof(*vdir), GFP_KERNEL);
if (!vdir) return -ENOMEM;
if (!dir_emit_dots(coda_file, ctx))
goto out;
while (1) {
/* read entries from the directory file */
ret = kernel_read(host_file, ctx->pos - 2, (char *)vdir,
sizeof(*vdir));
if (ret < 0) {
pr_err("%s: read dir %s failed %d\n",
__func__, coda_f2s(&cii->c_fid), ret);
break;
}
if (ret == 0) break; /* end of directory file reached */
/* catch truncated reads */
if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) {
pr_err("%s: short read on %s\n",
__func__, coda_f2s(&cii->c_fid));
ret = -EBADF;
break;
}
/* validate whether the directory file actually makes sense */
if (vdir->d_reclen < vdir_size + vdir->d_namlen) {
pr_err("%s: invalid dir %s\n",
__func__, coda_f2s(&cii->c_fid));
ret = -EBADF;
break;
}
name.len = vdir->d_namlen;
name.name = vdir->d_name;
/* Make sure we skip '.' and '..', we already got those */
if (name.name[0] == '.' && (name.len == 1 ||
(name.name[1] == '.' && name.len == 2)))
vdir->d_fileno = name.len = 0;
/* skip null entries */
if (vdir->d_fileno && name.len) {
ino = vdir->d_fileno;
type = CDT2DT(vdir->d_type);
if (!dir_emit(ctx, name.name, name.len, ino, type))
break;
}
/* we'll always have progress because d_reclen is unsigned and
* we've already established it is non-zero. */
ctx->pos += vdir->d_reclen;
}
out:
kfree(vdir);
return 0;
}
/* called when a cache lookup succeeds */
static int coda_dentry_revalidate(struct dentry *de, unsigned int flags)
{
struct inode *inode;
struct coda_inode_info *cii;
if (flags & LOOKUP_RCU)
return -ECHILD;
inode = de->d_inode;
if (!inode || coda_isroot(inode))
goto out;
if (is_bad_inode(inode))
goto bad;
cii = ITOC(de->d_inode);
if (!(cii->c_flags & (C_PURGE | C_FLUSH)))
goto out;
shrink_dcache_parent(de);
/* propagate for a flush */
if (cii->c_flags & C_FLUSH)
coda_flag_inode_children(inode, C_FLUSH);
if (d_count(de) > 1)
/* pretend it's valid, but don't change the flags */
goto out;
/* clear the flags. */
spin_lock(&cii->c_lock);
cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
spin_unlock(&cii->c_lock);
bad:
return 0;
out:
return 1;
}
/*
* This is the callback from dput() when d_count is going to 0.
* We use this to unhash dentries with bad inodes.
*/
static int coda_dentry_delete(const struct dentry * dentry)
{
int flags;
if (!dentry->d_inode)
return 0;
flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE;
if (is_bad_inode(dentry->d_inode) || flags) {
return 1;
}
return 0;
}
/*
* This is called when we want to check if the inode has
* changed on the server. Coda makes this easy since the
* cache manager Venus issues a downcall to the kernel when this
* happens
*/
int coda_revalidate_inode(struct inode *inode)
{
struct coda_vattr attr;
int error;
int old_mode;
ino_t old_ino;
struct coda_inode_info *cii = ITOC(inode);
if (!cii->c_flags)
return 0;
if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) {
error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
if (error)
return -EIO;
/* this inode may be lost if:
- it's ino changed
- type changes must be permitted for repair and
missing mount points.
*/
old_mode = inode->i_mode;
old_ino = inode->i_ino;
coda_vattr_to_iattr(inode, &attr);
if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) {
pr_warn("inode %ld, fid %s changed type!\n",
inode->i_ino, coda_f2s(&(cii->c_fid)));
}
/* the following can happen when a local fid is replaced
with a global one, here we lose and declare the inode bad */
if (inode->i_ino != old_ino)
return -EIO;
coda_flag_inode_children(inode, C_FLUSH);
spin_lock(&cii->c_lock);
cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
spin_unlock(&cii->c_lock);
}
return 0;
}

242
fs/coda/file.c Normal file
View file

@ -0,0 +1,242 @@
/*
* File operations for Coda.
* Original version: (C) 1996 Peter Braam
* Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users of this code to contribute improvements
* to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/cred.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
#include "coda_int.h"
static ssize_t
coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *ppos)
{
struct coda_file_info *cfi;
struct file *host_file;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
if (!host_file->f_op->read)
return -EINVAL;
return host_file->f_op->read(host_file, buf, count, ppos);
}
static ssize_t
coda_file_splice_read(struct file *coda_file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t count,
unsigned int flags)
{
ssize_t (*splice_read)(struct file *, loff_t *,
struct pipe_inode_info *, size_t, unsigned int);
struct coda_file_info *cfi;
struct file *host_file;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
splice_read = host_file->f_op->splice_read;
if (!splice_read)
splice_read = default_file_splice_read;
return splice_read(host_file, ppos, pipe, count, flags);
}
static ssize_t
coda_file_write(struct file *coda_file, const char __user *buf, size_t count, loff_t *ppos)
{
struct inode *host_inode, *coda_inode = file_inode(coda_file);
struct coda_file_info *cfi;
struct file *host_file;
ssize_t ret;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
if (!host_file->f_op->write)
return -EINVAL;
host_inode = file_inode(host_file);
file_start_write(host_file);
mutex_lock(&coda_inode->i_mutex);
ret = host_file->f_op->write(host_file, buf, count, ppos);
coda_inode->i_size = host_inode->i_size;
coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9;
coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME_SEC;
mutex_unlock(&coda_inode->i_mutex);
file_end_write(host_file);
return ret;
}
static int
coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
{
struct coda_file_info *cfi;
struct coda_inode_info *cii;
struct file *host_file;
struct inode *coda_inode, *host_inode;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
if (!host_file->f_op->mmap)
return -ENODEV;
coda_inode = file_inode(coda_file);
host_inode = file_inode(host_file);
cii = ITOC(coda_inode);
spin_lock(&cii->c_lock);
coda_file->f_mapping = host_file->f_mapping;
if (coda_inode->i_mapping == &coda_inode->i_data)
coda_inode->i_mapping = host_inode->i_mapping;
/* only allow additional mmaps as long as userspace isn't changing
* the container file on us! */
else if (coda_inode->i_mapping != host_inode->i_mapping) {
spin_unlock(&cii->c_lock);
return -EBUSY;
}
/* keep track of how often the coda_inode/host_file has been mmapped */
cii->c_mapcount++;
cfi->cfi_mapcount++;
spin_unlock(&cii->c_lock);
return host_file->f_op->mmap(host_file, vma);
}
int coda_open(struct inode *coda_inode, struct file *coda_file)
{
struct file *host_file = NULL;
int error;
unsigned short flags = coda_file->f_flags & (~O_EXCL);
unsigned short coda_flags = coda_flags_to_cflags(flags);
struct coda_file_info *cfi;
cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL);
if (!cfi)
return -ENOMEM;
error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
&host_file);
if (!host_file)
error = -EIO;
if (error) {
kfree(cfi);
return error;
}
host_file->f_flags |= coda_file->f_flags & (O_APPEND | O_SYNC);
cfi->cfi_magic = CODA_MAGIC;
cfi->cfi_mapcount = 0;
cfi->cfi_container = host_file;
BUG_ON(coda_file->private_data != NULL);
coda_file->private_data = cfi;
return 0;
}
int coda_release(struct inode *coda_inode, struct file *coda_file)
{
unsigned short flags = (coda_file->f_flags) & (~O_EXCL);
unsigned short coda_flags = coda_flags_to_cflags(flags);
struct coda_file_info *cfi;
struct coda_inode_info *cii;
struct inode *host_inode;
int err;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
coda_flags, coda_file->f_cred->fsuid);
host_inode = file_inode(cfi->cfi_container);
cii = ITOC(coda_inode);
/* did we mmap this file? */
spin_lock(&cii->c_lock);
if (coda_inode->i_mapping == &host_inode->i_data) {
cii->c_mapcount -= cfi->cfi_mapcount;
if (!cii->c_mapcount)
coda_inode->i_mapping = &coda_inode->i_data;
}
spin_unlock(&cii->c_lock);
fput(cfi->cfi_container);
kfree(coda_file->private_data);
coda_file->private_data = NULL;
/* VFS fput ignores the return value from file_operations->release, so
* there is no use returning an error here */
return 0;
}
int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync)
{
struct file *host_file;
struct inode *coda_inode = file_inode(coda_file);
struct coda_file_info *cfi;
int err;
if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
S_ISLNK(coda_inode->i_mode)))
return -EINVAL;
err = filemap_write_and_wait_range(coda_inode->i_mapping, start, end);
if (err)
return err;
mutex_lock(&coda_inode->i_mutex);
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
err = vfs_fsync(host_file, datasync);
if (!err && !datasync)
err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
mutex_unlock(&coda_inode->i_mutex);
return err;
}
const struct file_operations coda_file_operations = {
.llseek = generic_file_llseek,
.read = coda_file_read,
.write = coda_file_write,
.mmap = coda_file_mmap,
.open = coda_open,
.release = coda_release,
.fsync = coda_fsync,
.splice_read = coda_file_splice_read,
};

333
fs/coda/inode.c Normal file
View file

@ -0,0 +1,333 @@
/*
* Super block/filesystem wide operations
*
* Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
* Michael Callahan <callahan@maths.ox.ac.uk>
*
* Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu>
* Copyright (C) Carnegie Mellon University
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/file.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/pid_namespace.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
#include "coda_cache.h"
#include "coda_int.h"
/* VFS super_block ops */
static void coda_evict_inode(struct inode *);
static void coda_put_super(struct super_block *);
static int coda_statfs(struct dentry *dentry, struct kstatfs *buf);
static struct kmem_cache * coda_inode_cachep;
static struct inode *coda_alloc_inode(struct super_block *sb)
{
struct coda_inode_info *ei;
ei = kmem_cache_alloc(coda_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
memset(&ei->c_fid, 0, sizeof(struct CodaFid));
ei->c_flags = 0;
ei->c_uid = GLOBAL_ROOT_UID;
ei->c_cached_perm = 0;
spin_lock_init(&ei->c_lock);
return &ei->vfs_inode;
}
static void coda_i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(coda_inode_cachep, ITOC(inode));
}
static void coda_destroy_inode(struct inode *inode)
{
call_rcu(&inode->i_rcu, coda_i_callback);
}
static void init_once(void *foo)
{
struct coda_inode_info *ei = (struct coda_inode_info *) foo;
inode_init_once(&ei->vfs_inode);
}
int __init coda_init_inodecache(void)
{
coda_inode_cachep = kmem_cache_create("coda_inode_cache",
sizeof(struct coda_inode_info),
0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
init_once);
if (coda_inode_cachep == NULL)
return -ENOMEM;
return 0;
}
void coda_destroy_inodecache(void)
{
/*
* Make sure all delayed rcu free inodes are flushed before we
* destroy cache.
*/
rcu_barrier();
kmem_cache_destroy(coda_inode_cachep);
}
static int coda_remount(struct super_block *sb, int *flags, char *data)
{
sync_filesystem(sb);
*flags |= MS_NOATIME;
return 0;
}
/* exported operations */
static const struct super_operations coda_super_operations =
{
.alloc_inode = coda_alloc_inode,
.destroy_inode = coda_destroy_inode,
.evict_inode = coda_evict_inode,
.put_super = coda_put_super,
.statfs = coda_statfs,
.remount_fs = coda_remount,
};
static int get_device_index(struct coda_mount_data *data)
{
struct fd f;
struct inode *inode;
int idx;
if (data == NULL) {
pr_warn("%s: Bad mount data\n", __func__);
return -1;
}
if (data->version != CODA_MOUNT_VERSION) {
pr_warn("%s: Bad mount version\n", __func__);
return -1;
}
f = fdget(data->fd);
if (!f.file)
goto Ebadf;
inode = file_inode(f.file);
if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
fdput(f);
goto Ebadf;
}
idx = iminor(inode);
fdput(f);
if (idx < 0 || idx >= MAX_CODADEVS) {
pr_warn("%s: Bad minor number\n", __func__);
return -1;
}
return idx;
Ebadf:
pr_warn("%s: Bad file\n", __func__);
return -1;
}
static int coda_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *root = NULL;
struct venus_comm *vc;
struct CodaFid fid;
int error;
int idx;
if (task_active_pid_ns(current) != &init_pid_ns)
return -EINVAL;
idx = get_device_index((struct coda_mount_data *) data);
/* Ignore errors in data, for backward compatibility */
if(idx == -1)
idx = 0;
pr_info("%s: device index: %i\n", __func__, idx);
vc = &coda_comms[idx];
mutex_lock(&vc->vc_mutex);
if (!vc->vc_inuse) {
pr_warn("%s: No pseudo device\n", __func__);
error = -EINVAL;
goto unlock_out;
}
if (vc->vc_sb) {
pr_warn("%s: Device already mounted\n", __func__);
error = -EBUSY;
goto unlock_out;
}
error = bdi_setup_and_register(&vc->bdi, "coda", BDI_CAP_MAP_COPY);
if (error)
goto unlock_out;
vc->vc_sb = sb;
mutex_unlock(&vc->vc_mutex);
sb->s_fs_info = vc;
sb->s_flags |= MS_NOATIME;
sb->s_blocksize = 4096; /* XXXXX what do we put here?? */
sb->s_blocksize_bits = 12;
sb->s_magic = CODA_SUPER_MAGIC;
sb->s_op = &coda_super_operations;
sb->s_d_op = &coda_dentry_operations;
sb->s_bdi = &vc->bdi;
/* get root fid from Venus: this needs the root inode */
error = venus_rootfid(sb, &fid);
if ( error ) {
pr_warn("%s: coda_get_rootfid failed with %d\n",
__func__, error);
goto error;
}
pr_info("%s: rootfid is %s\n", __func__, coda_f2s(&fid));
/* make root inode */
root = coda_cnode_make(&fid, sb);
if (IS_ERR(root)) {
error = PTR_ERR(root);
pr_warn("Failure of coda_cnode_make for root: error %d\n",
error);
goto error;
}
pr_info("%s: rootinode is %ld dev %s\n",
__func__, root->i_ino, root->i_sb->s_id);
sb->s_root = d_make_root(root);
if (!sb->s_root) {
error = -EINVAL;
goto error;
}
return 0;
error:
mutex_lock(&vc->vc_mutex);
bdi_destroy(&vc->bdi);
vc->vc_sb = NULL;
sb->s_fs_info = NULL;
unlock_out:
mutex_unlock(&vc->vc_mutex);
return error;
}
static void coda_put_super(struct super_block *sb)
{
struct venus_comm *vcp = coda_vcp(sb);
mutex_lock(&vcp->vc_mutex);
bdi_destroy(&vcp->bdi);
vcp->vc_sb = NULL;
sb->s_fs_info = NULL;
mutex_unlock(&vcp->vc_mutex);
pr_info("Bye bye.\n");
}
static void coda_evict_inode(struct inode *inode)
{
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
coda_cache_clear_inode(inode);
}
int coda_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
int err = coda_revalidate_inode(dentry->d_inode);
if (!err)
generic_fillattr(dentry->d_inode, stat);
return err;
}
int coda_setattr(struct dentry *de, struct iattr *iattr)
{
struct inode *inode = de->d_inode;
struct coda_vattr vattr;
int error;
memset(&vattr, 0, sizeof(vattr));
inode->i_ctime = CURRENT_TIME_SEC;
coda_iattr_to_vattr(iattr, &vattr);
vattr.va_type = C_VNON; /* cannot set type */
/* Venus is responsible for truncating the container-file!!! */
error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr);
if (!error) {
coda_vattr_to_iattr(inode, &vattr);
coda_cache_clear_inode(inode);
}
return error;
}
const struct inode_operations coda_file_inode_operations = {
.permission = coda_permission,
.getattr = coda_getattr,
.setattr = coda_setattr,
};
static int coda_statfs(struct dentry *dentry, struct kstatfs *buf)
{
int error;
error = venus_statfs(dentry, buf);
if (error) {
/* fake something like AFS does */
buf->f_blocks = 9000000;
buf->f_bfree = 9000000;
buf->f_bavail = 9000000;
buf->f_files = 9000000;
buf->f_ffree = 9000000;
}
/* and fill in the rest */
buf->f_type = CODA_SUPER_MAGIC;
buf->f_bsize = 4096;
buf->f_namelen = CODA_MAXNAMLEN;
return 0;
}
/* init_coda: used by filesystems.c to register coda */
static struct dentry *coda_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_nodev(fs_type, flags, data, coda_fill_super);
}
struct file_system_type coda_fs_type = {
.owner = THIS_MODULE,
.name = "coda",
.mount = coda_mount,
.kill_sb = kill_anon_super,
.fs_flags = FS_BINARY_MOUNTDATA,
};
MODULE_ALIAS_FS("coda");

90
fs/coda/pioctl.c Normal file
View file

@ -0,0 +1,90 @@
/*
* Pioctl operations for Coda.
* Original version: (C) 1996 Peter Braam
* Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users of this code to contribute improvements
* to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/namei.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
/* pioctl ops */
static int coda_ioctl_permission(struct inode *inode, int mask);
static long coda_pioctl(struct file *filp, unsigned int cmd,
unsigned long user_data);
/* exported from this file */
const struct inode_operations coda_ioctl_inode_operations = {
.permission = coda_ioctl_permission,
.setattr = coda_setattr,
};
const struct file_operations coda_ioctl_operations = {
.owner = THIS_MODULE,
.unlocked_ioctl = coda_pioctl,
.llseek = noop_llseek,
};
/* the coda pioctl inode ops */
static int coda_ioctl_permission(struct inode *inode, int mask)
{
return (mask & MAY_EXEC) ? -EACCES : 0;
}
static long coda_pioctl(struct file *filp, unsigned int cmd,
unsigned long user_data)
{
struct path path;
int error;
struct PioctlData data;
struct inode *inode = file_inode(filp);
struct inode *target_inode = NULL;
struct coda_inode_info *cnp;
/* get the Pioctl data arguments from user space */
if (copy_from_user(&data, (void __user *)user_data, sizeof(data)))
return -EINVAL;
/*
* Look up the pathname. Note that the pathname is in
* user memory, and namei takes care of this
*/
if (data.follow)
error = user_path(data.path, &path);
else
error = user_lpath(data.path, &path);
if (error)
return error;
target_inode = path.dentry->d_inode;
/* return if it is not a Coda inode */
if (target_inode->i_sb != inode->i_sb) {
error = -EINVAL;
goto out;
}
/* now proceed to make the upcall */
cnp = ITOC(target_inode);
error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
out:
path_put(&path);
return error;
}

437
fs/coda/psdev.c Normal file
View file

@ -0,0 +1,437 @@
/*
* An implementation of a loadable kernel mode driver providing
* multiple kernel/user space bidirectional communications links.
*
* Author: Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* This program 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.
*
* Adapted to become the Linux 2.0 Coda pseudo device
* Peter Braam <braam@maths.ox.ac.uk>
* Michael Callahan <mjc@emmy.smith.edu>
*
* Changes for Linux 2.1
* Copyright (c) 1997 Carnegie-Mellon University
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/time.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/pid_namespace.h>
#include <asm/io.h>
#include <asm/poll.h>
#include <linux/uaccess.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
#include "coda_int.h"
/* statistics */
int coda_hard; /* allows signals during upcalls */
unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
struct venus_comm coda_comms[MAX_CODADEVS];
static struct class *coda_psdev_class;
/*
* Device operations
*/
static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
{
struct venus_comm *vcp = (struct venus_comm *) file->private_data;
unsigned int mask = POLLOUT | POLLWRNORM;
poll_wait(file, &vcp->vc_waitq, wait);
mutex_lock(&vcp->vc_mutex);
if (!list_empty(&vcp->vc_pending))
mask |= POLLIN | POLLRDNORM;
mutex_unlock(&vcp->vc_mutex);
return mask;
}
static long coda_psdev_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
unsigned int data;
switch(cmd) {
case CIOC_KERNEL_VERSION:
data = CODA_KERNEL_VERSION;
return put_user(data, (int __user *) arg);
default:
return -ENOTTY;
}
return 0;
}
/*
* Receive a message written by Venus to the psdev
*/
static ssize_t coda_psdev_write(struct file *file, const char __user *buf,
size_t nbytes, loff_t *off)
{
struct venus_comm *vcp = (struct venus_comm *) file->private_data;
struct upc_req *req = NULL;
struct upc_req *tmp;
struct list_head *lh;
struct coda_in_hdr hdr;
ssize_t retval = 0, count = 0;
int error;
/* Peek at the opcode, uniquefier */
if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
return -EFAULT;
if (DOWNCALL(hdr.opcode)) {
union outputArgs *dcbuf;
int size = sizeof(*dcbuf);
if ( nbytes < sizeof(struct coda_out_hdr) ) {
pr_warn("coda_downcall opc %d uniq %d, not enough!\n",
hdr.opcode, hdr.unique);
count = nbytes;
goto out;
}
if ( nbytes > size ) {
pr_warn("downcall opc %d, uniq %d, too much!",
hdr.opcode, hdr.unique);
nbytes = size;
}
CODA_ALLOC(dcbuf, union outputArgs *, nbytes);
if (copy_from_user(dcbuf, buf, nbytes)) {
CODA_FREE(dcbuf, nbytes);
retval = -EFAULT;
goto out;
}
/* what downcall errors does Venus handle ? */
error = coda_downcall(vcp, hdr.opcode, dcbuf);
CODA_FREE(dcbuf, nbytes);
if (error) {
pr_warn("%s: coda_downcall error: %d\n",
__func__, error);
retval = error;
goto out;
}
count = nbytes;
goto out;
}
/* Look for the message on the processing queue. */
mutex_lock(&vcp->vc_mutex);
list_for_each(lh, &vcp->vc_processing) {
tmp = list_entry(lh, struct upc_req , uc_chain);
if (tmp->uc_unique == hdr.unique) {
req = tmp;
list_del(&req->uc_chain);
break;
}
}
mutex_unlock(&vcp->vc_mutex);
if (!req) {
pr_warn("%s: msg (%d, %d) not found\n",
__func__, hdr.opcode, hdr.unique);
retval = -ESRCH;
goto out;
}
/* move data into response buffer. */
if (req->uc_outSize < nbytes) {
pr_warn("%s: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n",
__func__, req->uc_outSize, (long)nbytes,
hdr.opcode, hdr.unique);
nbytes = req->uc_outSize; /* don't have more space! */
}
if (copy_from_user(req->uc_data, buf, nbytes)) {
req->uc_flags |= CODA_REQ_ABORT;
wake_up(&req->uc_sleep);
retval = -EFAULT;
goto out;
}
/* adjust outsize. is this useful ?? */
req->uc_outSize = nbytes;
req->uc_flags |= CODA_REQ_WRITE;
count = nbytes;
/* Convert filedescriptor into a file handle */
if (req->uc_opcode == CODA_OPEN_BY_FD) {
struct coda_open_by_fd_out *outp =
(struct coda_open_by_fd_out *)req->uc_data;
if (!outp->oh.result)
outp->fh = fget(outp->fd);
}
wake_up(&req->uc_sleep);
out:
return(count ? count : retval);
}
/*
* Read a message from the kernel to Venus
*/
static ssize_t coda_psdev_read(struct file * file, char __user * buf,
size_t nbytes, loff_t *off)
{
DECLARE_WAITQUEUE(wait, current);
struct venus_comm *vcp = (struct venus_comm *) file->private_data;
struct upc_req *req;
ssize_t retval = 0, count = 0;
if (nbytes == 0)
return 0;
mutex_lock(&vcp->vc_mutex);
add_wait_queue(&vcp->vc_waitq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (list_empty(&vcp->vc_pending)) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
mutex_unlock(&vcp->vc_mutex);
schedule();
mutex_lock(&vcp->vc_mutex);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&vcp->vc_waitq, &wait);
if (retval)
goto out;
req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
list_del(&req->uc_chain);
/* Move the input args into userspace */
count = req->uc_inSize;
if (nbytes < req->uc_inSize) {
pr_warn("%s: Venus read %ld bytes of %d in message\n",
__func__, (long)nbytes, req->uc_inSize);
count = nbytes;
}
if (copy_to_user(buf, req->uc_data, count))
retval = -EFAULT;
/* If request was not a signal, enqueue and don't free */
if (!(req->uc_flags & CODA_REQ_ASYNC)) {
req->uc_flags |= CODA_REQ_READ;
list_add_tail(&(req->uc_chain), &vcp->vc_processing);
goto out;
}
CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
kfree(req);
out:
mutex_unlock(&vcp->vc_mutex);
return (count ? count : retval);
}
static int coda_psdev_open(struct inode * inode, struct file * file)
{
struct venus_comm *vcp;
int idx, err;
if (task_active_pid_ns(current) != &init_pid_ns)
return -EINVAL;
if (current_user_ns() != &init_user_ns)
return -EINVAL;
idx = iminor(inode);
if (idx < 0 || idx >= MAX_CODADEVS)
return -ENODEV;
err = -EBUSY;
vcp = &coda_comms[idx];
mutex_lock(&vcp->vc_mutex);
if (!vcp->vc_inuse) {
vcp->vc_inuse++;
INIT_LIST_HEAD(&vcp->vc_pending);
INIT_LIST_HEAD(&vcp->vc_processing);
init_waitqueue_head(&vcp->vc_waitq);
vcp->vc_sb = NULL;
vcp->vc_seq = 0;
file->private_data = vcp;
err = 0;
}
mutex_unlock(&vcp->vc_mutex);
return err;
}
static int coda_psdev_release(struct inode * inode, struct file * file)
{
struct venus_comm *vcp = (struct venus_comm *) file->private_data;
struct upc_req *req, *tmp;
if (!vcp || !vcp->vc_inuse ) {
pr_warn("%s: Not open.\n", __func__);
return -1;
}
mutex_lock(&vcp->vc_mutex);
/* Wakeup clients so they can return. */
list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) {
list_del(&req->uc_chain);
/* Async requests need to be freed here */
if (req->uc_flags & CODA_REQ_ASYNC) {
CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
kfree(req);
continue;
}
req->uc_flags |= CODA_REQ_ABORT;
wake_up(&req->uc_sleep);
}
list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) {
list_del(&req->uc_chain);
req->uc_flags |= CODA_REQ_ABORT;
wake_up(&req->uc_sleep);
}
file->private_data = NULL;
vcp->vc_inuse--;
mutex_unlock(&vcp->vc_mutex);
return 0;
}
static const struct file_operations coda_psdev_fops = {
.owner = THIS_MODULE,
.read = coda_psdev_read,
.write = coda_psdev_write,
.poll = coda_psdev_poll,
.unlocked_ioctl = coda_psdev_ioctl,
.open = coda_psdev_open,
.release = coda_psdev_release,
.llseek = noop_llseek,
};
static int init_coda_psdev(void)
{
int i, err = 0;
if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) {
pr_err("%s: unable to get major %d\n",
__func__, CODA_PSDEV_MAJOR);
return -EIO;
}
coda_psdev_class = class_create(THIS_MODULE, "coda");
if (IS_ERR(coda_psdev_class)) {
err = PTR_ERR(coda_psdev_class);
goto out_chrdev;
}
for (i = 0; i < MAX_CODADEVS; i++) {
mutex_init(&(&coda_comms[i])->vc_mutex);
device_create(coda_psdev_class, NULL,
MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i);
}
coda_sysctl_init();
goto out;
out_chrdev:
unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
out:
return err;
}
MODULE_AUTHOR("Jan Harkes, Peter J. Braam");
MODULE_DESCRIPTION("Coda Distributed File System VFS interface");
MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR);
MODULE_LICENSE("GPL");
MODULE_VERSION("6.6");
static int __init init_coda(void)
{
int status;
int i;
status = coda_init_inodecache();
if (status)
goto out2;
status = init_coda_psdev();
if ( status ) {
pr_warn("Problem (%d) in init_coda_psdev\n", status);
goto out1;
}
status = register_filesystem(&coda_fs_type);
if (status) {
pr_warn("failed to register filesystem!\n");
goto out;
}
return 0;
out:
for (i = 0; i < MAX_CODADEVS; i++)
device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
class_destroy(coda_psdev_class);
unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
coda_sysctl_clean();
out1:
coda_destroy_inodecache();
out2:
return status;
}
static void __exit exit_coda(void)
{
int err, i;
err = unregister_filesystem(&coda_fs_type);
if (err != 0)
pr_warn("failed to unregister filesystem\n");
for (i = 0; i < MAX_CODADEVS; i++)
device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
class_destroy(coda_psdev_class);
unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
coda_sysctl_clean();
coda_destroy_inodecache();
}
module_init(init_coda);
module_exit(exit_coda);

50
fs/coda/symlink.c Normal file
View file

@ -0,0 +1,50 @@
/*
* Symlink inode operations for Coda filesystem
* Original version: (C) 1996 P. Braam and M. Callahan
* Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users to contribute improvements to
* the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/pagemap.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
static int coda_symlink_filler(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
int error;
struct coda_inode_info *cii;
unsigned int len = PAGE_SIZE;
char *p = kmap(page);
cii = ITOC(inode);
error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len);
if (error)
goto fail;
SetPageUptodate(page);
kunmap(page);
unlock_page(page);
return 0;
fail:
SetPageError(page);
kunmap(page);
unlock_page(page);
return error;
}
const struct address_space_operations coda_symlink_aops = {
.readpage = coda_symlink_filler,
};

73
fs/coda/sysctl.c Normal file
View file

@ -0,0 +1,73 @@
/*
* Sysctl operations for Coda filesystem
* Original version: (C) 1996 P. Braam and M. Callahan
* Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users to contribute improvements to
* the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
*/
#include <linux/sysctl.h>
#include "coda_int.h"
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *fs_table_header;
static struct ctl_table coda_table[] = {
{
.procname = "timeout",
.data = &coda_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec
},
{
.procname = "hard",
.data = &coda_hard,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec
},
{
.procname = "fake_statfs",
.data = &coda_fake_statfs,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = proc_dointvec
},
{}
};
static struct ctl_table fs_table[] = {
{
.procname = "coda",
.mode = 0555,
.child = coda_table
},
{}
};
void coda_sysctl_init(void)
{
if ( !fs_table_header )
fs_table_header = register_sysctl_table(fs_table);
}
void coda_sysctl_clean(void)
{
if ( fs_table_header ) {
unregister_sysctl_table(fs_table_header);
fs_table_header = NULL;
}
}
#else
void coda_sysctl_init(void)
{
}
void coda_sysctl_clean(void)
{
}
#endif

882
fs/coda/upcall.c Normal file
View file

@ -0,0 +1,882 @@
/*
* Mostly platform independent upcall operations to Venus:
* -- upcalls
* -- upcall routines
*
* Linux 2.0 version
* Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,
* Michael Callahan <callahan@maths.ox.ac.uk>
*
* Redone for Linux 2.1
* Copyright (C) 1997 Carnegie Mellon University
*
* Carnegie Mellon University encourages users of this code to contribute
* improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
*/
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/vfs.h>
#include <linux/coda.h>
#include <linux/coda_psdev.h>
#include "coda_linux.h"
#include "coda_cache.h"
#include "coda_int.h"
static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
union inputArgs *buffer);
static void *alloc_upcall(int opcode, int size)
{
union inputArgs *inp;
CODA_ALLOC(inp, union inputArgs *, size);
if (!inp)
return ERR_PTR(-ENOMEM);
inp->ih.opcode = opcode;
inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);
inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
return (void*)inp;
}
#define UPARG(op)\
do {\
inp = (union inputArgs *)alloc_upcall(op, insize); \
if (IS_ERR(inp)) { return PTR_ERR(inp); }\
outp = (union outputArgs *)(inp); \
outsize = insize; \
} while (0)
#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
#define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
/* the upcalls */
int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(root);
UPARG(CODA_ROOT);
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error)
*fidp = outp->coda_root.VFid;
CODA_FREE(inp, insize);
return error;
}
int venus_getattr(struct super_block *sb, struct CodaFid *fid,
struct coda_vattr *attr)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(getattr);
UPARG(CODA_GETATTR);
inp->coda_getattr.VFid = *fid;
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error)
*attr = outp->coda_getattr.attr;
CODA_FREE(inp, insize);
return error;
}
int venus_setattr(struct super_block *sb, struct CodaFid *fid,
struct coda_vattr *vattr)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(setattr);
UPARG(CODA_SETATTR);
inp->coda_setattr.VFid = *fid;
inp->coda_setattr.attr = *vattr;
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_lookup(struct super_block *sb, struct CodaFid *fid,
const char *name, int length, int * type,
struct CodaFid *resfid)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset;
offset = INSIZE(lookup);
insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
UPARG(CODA_LOOKUP);
inp->coda_lookup.VFid = *fid;
inp->coda_lookup.name = offset;
inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
/* send Venus a null terminated string */
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error) {
*resfid = outp->coda_lookup.VFid;
*type = outp->coda_lookup.vtype;
}
CODA_FREE(inp, insize);
return error;
}
int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
kuid_t uid)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(release);
UPARG(CODA_CLOSE);
inp->ih.uid = from_kuid(&init_user_ns, uid);
inp->coda_close.VFid = *fid;
inp->coda_close.flags = flags;
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_open(struct super_block *sb, struct CodaFid *fid,
int flags, struct file **fh)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(open_by_fd);
UPARG(CODA_OPEN_BY_FD);
inp->coda_open_by_fd.VFid = *fid;
inp->coda_open_by_fd.flags = flags;
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error)
*fh = outp->coda_open_by_fd.fh;
CODA_FREE(inp, insize);
return error;
}
int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
const char *name, int length,
struct CodaFid *newfid, struct coda_vattr *attrs)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset;
offset = INSIZE(mkdir);
insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
UPARG(CODA_MKDIR);
inp->coda_mkdir.VFid = *dirfid;
inp->coda_mkdir.attr = *attrs;
inp->coda_mkdir.name = offset;
/* Venus must get null terminated string */
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error) {
*attrs = outp->coda_mkdir.attr;
*newfid = outp->coda_mkdir.VFid;
}
CODA_FREE(inp, insize);
return error;
}
int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
struct CodaFid *new_fid, size_t old_length,
size_t new_length, const char *old_name,
const char *new_name)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset, s;
offset = INSIZE(rename);
insize = max_t(unsigned int, offset + new_length + old_length + 8,
OUTSIZE(rename));
UPARG(CODA_RENAME);
inp->coda_rename.sourceFid = *old_fid;
inp->coda_rename.destFid = *new_fid;
inp->coda_rename.srcname = offset;
/* Venus must receive an null terminated string */
s = ( old_length & ~0x3) +4; /* round up to word boundary */
memcpy((char *)(inp) + offset, old_name, old_length);
*((char *)inp + offset + old_length) = '\0';
/* another null terminated string for Venus */
offset += s;
inp->coda_rename.destname = offset;
s = ( new_length & ~0x3) +4; /* round up to word boundary */
memcpy((char *)(inp) + offset, new_name, new_length);
*((char *)inp + offset + new_length) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_create(struct super_block *sb, struct CodaFid *dirfid,
const char *name, int length, int excl, int mode,
struct CodaFid *newfid, struct coda_vattr *attrs)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset;
offset = INSIZE(create);
insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
UPARG(CODA_CREATE);
inp->coda_create.VFid = *dirfid;
inp->coda_create.attr.va_mode = mode;
inp->coda_create.excl = excl;
inp->coda_create.mode = mode;
inp->coda_create.name = offset;
/* Venus must get null terminated string */
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error) {
*attrs = outp->coda_create.attr;
*newfid = outp->coda_create.VFid;
}
CODA_FREE(inp, insize);
return error;
}
int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
const char *name, int length)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset;
offset = INSIZE(rmdir);
insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
UPARG(CODA_RMDIR);
inp->coda_rmdir.VFid = *dirfid;
inp->coda_rmdir.name = offset;
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
const char *name, int length)
{
union inputArgs *inp;
union outputArgs *outp;
int error=0, insize, outsize, offset;
offset = INSIZE(remove);
insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
UPARG(CODA_REMOVE);
inp->coda_remove.VFid = *dirfid;
inp->coda_remove.name = offset;
memcpy((char *)(inp) + offset, name, length);
*((char *)inp + offset + length) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_readlink(struct super_block *sb, struct CodaFid *fid,
char *buffer, int *length)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int retlen;
char *result;
insize = max_t(unsigned int,
INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
UPARG(CODA_READLINK);
inp->coda_readlink.VFid = *fid;
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
if (!error) {
retlen = outp->coda_readlink.count;
if ( retlen > *length )
retlen = *length;
*length = retlen;
result = (char *)outp + (long)outp->coda_readlink.data;
memcpy(buffer, result, retlen);
*(buffer + retlen) = '\0';
}
CODA_FREE(inp, insize);
return error;
}
int venus_link(struct super_block *sb, struct CodaFid *fid,
struct CodaFid *dirfid, const char *name, int len )
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset;
offset = INSIZE(link);
insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link));
UPARG(CODA_LINK);
inp->coda_link.sourceFid = *fid;
inp->coda_link.destFid = *dirfid;
inp->coda_link.tname = offset;
/* make sure strings are null terminated */
memcpy((char *)(inp) + offset, name, len);
*((char *)inp + offset + len) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_symlink(struct super_block *sb, struct CodaFid *fid,
const char *name, int len,
const char *symname, int symlen)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int offset, s;
offset = INSIZE(symlink);
insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
UPARG(CODA_SYMLINK);
/* inp->coda_symlink.attr = *tva; XXXXXX */
inp->coda_symlink.VFid = *fid;
/* Round up to word boundary and null terminate */
inp->coda_symlink.srcname = offset;
s = ( symlen & ~0x3 ) + 4;
memcpy((char *)(inp) + offset, symname, symlen);
*((char *)inp + offset + symlen) = '\0';
/* Round up to word boundary and null terminate */
offset += s;
inp->coda_symlink.tname = offset;
s = (len & ~0x3) + 4;
memcpy((char *)(inp) + offset, name, len);
*((char *)inp + offset + len) = '\0';
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_fsync(struct super_block *sb, struct CodaFid *fid)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize=SIZE(fsync);
UPARG(CODA_FSYNC);
inp->coda_fsync.VFid = *fid;
error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
&outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = SIZE(access);
UPARG(CODA_ACCESS);
inp->coda_access.VFid = *fid;
inp->coda_access.flags = mask;
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
CODA_FREE(inp, insize);
return error;
}
int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
unsigned int cmd, struct PioctlData *data)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
int iocsize;
insize = VC_MAXMSGSIZE;
UPARG(CODA_IOCTL);
/* build packet for Venus */
if (data->vi.in_size > VC_MAXDATASIZE) {
error = -EINVAL;
goto exit;
}
if (data->vi.out_size > VC_MAXDATASIZE) {
error = -EINVAL;
goto exit;
}
inp->coda_ioctl.VFid = *fid;
/* the cmd field was mutated by increasing its size field to
* reflect the path and follow args. We need to subtract that
* out before sending the command to Venus. */
inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
/* in->coda_ioctl.rwflag = flag; */
inp->coda_ioctl.len = data->vi.in_size;
inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
/* get the data out of user space */
if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,
data->vi.in, data->vi.in_size)) {
error = -EINVAL;
goto exit;
}
error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
&outsize, inp);
if (error) {
pr_warn("%s: Venus returns: %d for %s\n",
__func__, error, coda_f2s(fid));
goto exit;
}
if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
error = -EINVAL;
goto exit;
}
/* Copy out the OUT buffer. */
if (outp->coda_ioctl.len > data->vi.out_size) {
error = -EINVAL;
goto exit;
}
/* Copy out the OUT buffer. */
if (copy_to_user(data->vi.out,
(char *)outp + (long)outp->coda_ioctl.data,
outp->coda_ioctl.len)) {
error = -EFAULT;
goto exit;
}
exit:
CODA_FREE(inp, insize);
return error;
}
int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
{
union inputArgs *inp;
union outputArgs *outp;
int insize, outsize, error;
insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
UPARG(CODA_STATFS);
error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
if (!error) {
sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
sfs->f_bfree = outp->coda_statfs.stat.f_bfree;
sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
sfs->f_files = outp->coda_statfs.stat.f_files;
sfs->f_ffree = outp->coda_statfs.stat.f_ffree;
}
CODA_FREE(inp, insize);
return error;
}
/*
* coda_upcall and coda_downcall routines.
*/
static void coda_block_signals(sigset_t *old)
{
spin_lock_irq(&current->sighand->siglock);
*old = current->blocked;
sigfillset(&current->blocked);
sigdelset(&current->blocked, SIGKILL);
sigdelset(&current->blocked, SIGSTOP);
sigdelset(&current->blocked, SIGINT);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
static void coda_unblock_signals(sigset_t *old)
{
spin_lock_irq(&current->sighand->siglock);
current->blocked = *old;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
/* Don't allow signals to interrupt the following upcalls before venus
* has seen them,
* - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems)
* - CODA_STORE (to avoid data loss)
*/
#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
(((r)->uc_opcode != CODA_CLOSE && \
(r)->uc_opcode != CODA_STORE && \
(r)->uc_opcode != CODA_RELEASE) || \
(r)->uc_flags & CODA_REQ_READ))
static inline void coda_waitfor_upcall(struct venus_comm *vcp,
struct upc_req *req)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long timeout = jiffies + coda_timeout * HZ;
sigset_t old;
int blocked;
coda_block_signals(&old);
blocked = 1;
add_wait_queue(&req->uc_sleep, &wait);
for (;;) {
if (CODA_INTERRUPTIBLE(req))
set_current_state(TASK_INTERRUPTIBLE);
else
set_current_state(TASK_UNINTERRUPTIBLE);
/* got a reply */
if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
break;
if (blocked && time_after(jiffies, timeout) &&
CODA_INTERRUPTIBLE(req))
{
coda_unblock_signals(&old);
blocked = 0;
}
if (signal_pending(current)) {
list_del(&req->uc_chain);
break;
}
mutex_unlock(&vcp->vc_mutex);
if (blocked)
schedule_timeout(HZ);
else
schedule();
mutex_lock(&vcp->vc_mutex);
}
if (blocked)
coda_unblock_signals(&old);
remove_wait_queue(&req->uc_sleep, &wait);
set_current_state(TASK_RUNNING);
}
/*
* coda_upcall will return an error in the case of
* failed communication with Venus _or_ will peek at Venus
* reply and return Venus' error.
*
* As venus has 2 types of errors, normal errors (positive) and internal
* errors (negative), normal errors are negated, while internal errors
* are all mapped to -EINTR, while showing a nice warning message. (jh)
*/
static int coda_upcall(struct venus_comm *vcp,
int inSize, int *outSize,
union inputArgs *buffer)
{
union outputArgs *out;
union inputArgs *sig_inputArgs;
struct upc_req *req = NULL, *sig_req;
int error;
mutex_lock(&vcp->vc_mutex);
if (!vcp->vc_inuse) {
pr_notice("Venus dead, not sending upcall\n");
error = -ENXIO;
goto exit;
}
/* Format the request message. */
req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
if (!req) {
error = -ENOMEM;
goto exit;
}
req->uc_data = (void *)buffer;
req->uc_flags = 0;
req->uc_inSize = inSize;
req->uc_outSize = *outSize ? *outSize : inSize;
req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
req->uc_unique = ++vcp->vc_seq;
init_waitqueue_head(&req->uc_sleep);
/* Fill in the common input args. */
((union inputArgs *)buffer)->ih.unique = req->uc_unique;
/* Append msg to pending queue and poke Venus. */
list_add_tail(&req->uc_chain, &vcp->vc_pending);
wake_up_interruptible(&vcp->vc_waitq);
/* We can be interrupted while we wait for Venus to process
* our request. If the interrupt occurs before Venus has read
* the request, we dequeue and return. If it occurs after the
* read but before the reply, we dequeue, send a signal
* message, and return. If it occurs after the reply we ignore
* it. In no case do we want to restart the syscall. If it
* was interrupted by a venus shutdown (psdev_close), return
* ENODEV. */
/* Go to sleep. Wake up on signals only after the timeout. */
coda_waitfor_upcall(vcp, req);
/* Op went through, interrupt or not... */
if (req->uc_flags & CODA_REQ_WRITE) {
out = (union outputArgs *)req->uc_data;
/* here we map positive Venus errors to kernel errors */
error = -out->oh.result;
*outSize = req->uc_outSize;
goto exit;
}
error = -EINTR;
if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
pr_warn("Unexpected interruption.\n");
goto exit;
}
/* Interrupted before venus read it. */
if (!(req->uc_flags & CODA_REQ_READ))
goto exit;
/* Venus saw the upcall, make sure we can send interrupt signal */
if (!vcp->vc_inuse) {
pr_info("Venus dead, not sending signal.\n");
goto exit;
}
error = -ENOMEM;
sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
if (!sig_req) goto exit;
CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
if (!sig_req->uc_data) {
kfree(sig_req);
goto exit;
}
error = -EINTR;
sig_inputArgs = (union inputArgs *)sig_req->uc_data;
sig_inputArgs->ih.opcode = CODA_SIGNAL;
sig_inputArgs->ih.unique = req->uc_unique;
sig_req->uc_flags = CODA_REQ_ASYNC;
sig_req->uc_opcode = sig_inputArgs->ih.opcode;
sig_req->uc_unique = sig_inputArgs->ih.unique;
sig_req->uc_inSize = sizeof(struct coda_in_hdr);
sig_req->uc_outSize = sizeof(struct coda_in_hdr);
/* insert at head of queue! */
list_add(&(sig_req->uc_chain), &vcp->vc_pending);
wake_up_interruptible(&vcp->vc_waitq);
exit:
kfree(req);
mutex_unlock(&vcp->vc_mutex);
return error;
}
/*
The statements below are part of the Coda opportunistic
programming -- taken from the Mach/BSD kernel code for Coda.
You don't get correct semantics by stating what needs to be
done without guaranteeing the invariants needed for it to happen.
When will be have time to find out what exactly is going on? (pjb)
*/
/*
* There are 7 cases where cache invalidations occur. The semantics
* of each is listed here:
*
* CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
* CODA_PURGEUSER -- flush all entries from the name cache for a specific user
* This call is a result of token expiration.
*
* The next arise as the result of callbacks on a file or directory.
* CODA_ZAPFILE -- flush the cached attributes for a file.
* CODA_ZAPDIR -- flush the attributes for the dir and
* force a new lookup for all the children
of this dir.
*
* The next is a result of Venus detecting an inconsistent file.
* CODA_PURGEFID -- flush the attribute for the file
* purge it and its children from the dcache
*
* The last allows Venus to replace local fids with global ones
* during reintegration.
*
* CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out)
{
struct inode *inode = NULL;
struct CodaFid *fid = NULL, *newfid;
struct super_block *sb;
/* Handle invalidation requests. */
mutex_lock(&vcp->vc_mutex);
sb = vcp->vc_sb;
if (!sb || !sb->s_root)
goto unlock_out;
switch (opcode) {
case CODA_FLUSH:
coda_cache_clear_all(sb);
shrink_dcache_sb(sb);
if (sb->s_root->d_inode)
coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
break;
case CODA_PURGEUSER:
coda_cache_clear_all(sb);
break;
case CODA_ZAPDIR:
fid = &out->coda_zapdir.CodaFid;
break;
case CODA_ZAPFILE:
fid = &out->coda_zapfile.CodaFid;
break;
case CODA_PURGEFID:
fid = &out->coda_purgefid.CodaFid;
break;
case CODA_REPLACE:
fid = &out->coda_replace.OldFid;
break;
}
if (fid)
inode = coda_fid_to_inode(fid, sb);
unlock_out:
mutex_unlock(&vcp->vc_mutex);
if (!inode)
return 0;
switch (opcode) {
case CODA_ZAPDIR:
coda_flag_inode_children(inode, C_PURGE);
coda_flag_inode(inode, C_VATTR);
break;
case CODA_ZAPFILE:
coda_flag_inode(inode, C_VATTR);
break;
case CODA_PURGEFID:
coda_flag_inode_children(inode, C_PURGE);
/* catch the dentries later if some are still busy */
coda_flag_inode(inode, C_PURGE);
d_prune_aliases(inode);
break;
case CODA_REPLACE:
newfid = &out->coda_replace.NewFid;
coda_replace_fid(inode, fid, newfid);
break;
}
iput(inode);
return 0;
}