diff options
Diffstat (limited to 'fs/coda')
| -rw-r--r-- | fs/coda/Kconfig | 21 | ||||
| -rw-r--r-- | fs/coda/Makefile | 2 | ||||
| -rw-r--r-- | fs/coda/cache.c | 40 | ||||
| -rw-r--r-- | fs/coda/cnode.c | 66 | ||||
| -rw-r--r-- | fs/coda/coda_cache.h | 22 | ||||
| -rw-r--r-- | fs/coda/coda_fs_i.h | 58 | ||||
| -rw-r--r-- | fs/coda/coda_int.h | 6 | ||||
| -rw-r--r-- | fs/coda/coda_linux.c | 21 | ||||
| -rw-r--r-- | fs/coda/coda_linux.h | 106 | ||||
| -rw-r--r-- | fs/coda/dir.c | 342 | ||||
| -rw-r--r-- | fs/coda/file.c | 85 | ||||
| -rw-r--r-- | fs/coda/inode.c | 181 | ||||
| -rw-r--r-- | fs/coda/pioctl.c | 89 | ||||
| -rw-r--r-- | fs/coda/psdev.c | 123 | ||||
| -rw-r--r-- | fs/coda/symlink.c | 7 | ||||
| -rw-r--r-- | fs/coda/sysctl.c | 30 | ||||
| -rw-r--r-- | fs/coda/upcall.c | 147 |
17 files changed, 747 insertions, 599 deletions
diff --git a/fs/coda/Kconfig b/fs/coda/Kconfig new file mode 100644 index 00000000000..c0e5a7fad06 --- /dev/null +++ b/fs/coda/Kconfig @@ -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. diff --git a/fs/coda/Makefile b/fs/coda/Makefile index 6c22e61da39..1bab69a0d34 100644 --- a/fs/coda/Makefile +++ b/fs/coda/Makefile @@ -9,4 +9,4 @@ coda-objs := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o \ # If you want debugging output, please uncomment the following line. -# EXTRA_CFLAGS += -DDEBUG -DDEBUG_SMB_MALLOC=1 +# ccflags-y := -DDEBUG -DDEBUG_SMB_MALLOC=1 diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 8a2370341c7..1da168c61d3 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -17,12 +17,12 @@ #include <linux/string.h> #include <linux/list.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <linux/coda.h> -#include <linux/coda_linux.h> #include <linux/coda_psdev.h> -#include <linux/coda_fs_i.h> -#include <linux/coda_cache.h> +#include "coda_linux.h" +#include "coda_cache.h" static atomic_t permission_epoch = ATOMIC_INIT(0); @@ -31,19 +31,23 @@ 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 (cii->c_uid != current->fsuid) { - cii->c_uid = current->fsuid; + 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 */ @@ -57,13 +61,15 @@ void coda_cache_clear_all(struct super_block *sb) int coda_cache_check(struct inode *inode, int mask) { struct coda_inode_info *cii = ITOC(inode); - int hit; + int hit; - hit = (mask & cii->c_cached_perm) == mask && - cii->c_uid == current->fsuid && - cii->c_cached_epoch == atomic_read(&permission_epoch); + 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; + return hit; } @@ -83,19 +89,15 @@ int coda_cache_check(struct inode *inode, int mask) /* this won't do any harm: just flag all children */ static void coda_flag_children(struct dentry *parent, int flag) { - struct list_head *child; struct dentry *de; - spin_lock(&dcache_lock); - list_for_each(child, &parent->d_subdirs) - { - de = list_entry(child, struct dentry, d_u.d_child); + spin_lock(&parent->d_lock); + list_for_each_entry(de, &parent->d_subdirs, d_u.d_child) { /* don't know what to do with negative dentries */ - if ( ! de->d_inode ) - continue; - coda_flag_inode(de->d_inode, flag); + if (de->d_inode ) + coda_flag_inode(de->d_inode, flag); } - spin_unlock(&dcache_lock); + spin_unlock(&parent->d_lock); return; } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index a7a780929ee..7740b1c871c 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -7,9 +7,8 @@ #include <linux/time.h> #include <linux/coda.h> -#include <linux/coda_linux.h> -#include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> +#include "coda_linux.h" static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2) { @@ -45,13 +44,15 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) static int coda_test_inode(struct inode *inode, void *data) { struct CodaFid *fid = (struct CodaFid *)data; - return coda_fideq(&(ITOC(inode)->c_fid), fid); + 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; - ITOC(inode)->c_fid = *fid; + struct coda_inode_info *cii = ITOC(inode); + cii->c_fid = *fid; return 0; } @@ -71,6 +72,7 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid, 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); } @@ -86,35 +88,38 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid, - link the two up if this is needed - fill in the attributes */ -int coda_cnode_make(struct inode **inode, struct CodaFid *fid, struct super_block *sb) +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 ) { - *inode = NULL; - return error; - } - - *inode = coda_iget(sb, fid, &attr); - if ( IS_ERR(*inode) ) { - printk("coda_cnode_make: coda_iget failed\n"); - return PTR_ERR(*inode); - } - return 0; + 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; + struct coda_inode_info *cii = ITOC(inode); unsigned long hash = coda_f2i(newfid); - cii = ITOC(inode); - BUG_ON(!coda_fideq(&cii->c_fid, oldfid)); /* replace fid and rehash inode */ @@ -132,7 +137,7 @@ struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb) unsigned long hash = coda_f2i(fid); if ( !sb ) { - printk("coda_fid_to_inode: no sb!\n"); + pr_warn("%s: no sb!\n", __func__); return NULL; } @@ -148,19 +153,16 @@ struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb) } /* the CONTROL inode is made without asking attributes from Venus */ -int coda_cnode_makectl(struct inode **inode, struct super_block *sb) +struct inode *coda_cnode_makectl(struct super_block *sb) { - int error = -ENOMEM; - - *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; - error = 0; + 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 error; + return ERR_PTR(-ENOMEM); } diff --git a/fs/coda/coda_cache.h b/fs/coda/coda_cache.h new file mode 100644 index 00000000000..c910b5eb1ce --- /dev/null +++ b/fs/coda/coda_cache.h @@ -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_ */ diff --git a/fs/coda/coda_fs_i.h b/fs/coda/coda_fs_i.h new file mode 100644 index 00000000000..c6407521321 --- /dev/null +++ b/fs/coda/coda_fs_i.h @@ -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 diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h index 8ccd5ed81d9..381c993b142 100644 --- a/fs/coda/coda_int.h +++ b/fs/coda/coda_int.h @@ -2,6 +2,7 @@ #define _CODA_INT_ struct dentry; +struct file; extern struct file_system_type coda_fs_type; extern unsigned long coda_timeout; @@ -9,9 +10,8 @@ extern int coda_hard; extern int coda_fake_statfs; void coda_destroy_inodecache(void); -int coda_init_inodecache(void); -int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, - int datasync); +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); diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 95a54253c04..2849f41e72a 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -17,9 +17,8 @@ #include <linux/string.h> #include <linux/coda.h> -#include <linux/coda_linux.h> #include <linux/coda_psdev.h> -#include <linux/coda_fs_i.h> +#include "coda_linux.h" /* initialize the debugging variables */ int coda_fake_statfs; @@ -28,11 +27,9 @@ int coda_fake_statfs; char * coda_f2s(struct CodaFid *f) { static char s[60]; -#ifdef CONFIG_CODA_FS_OLD_API - sprintf(s, "(%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2]); -#else + sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]); -#endif + return s; } @@ -103,11 +100,11 @@ void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) if (attr->va_mode != (u_short) -1) inode->i_mode = attr->va_mode | inode_type; if (attr->va_uid != -1) - inode->i_uid = (uid_t) attr->va_uid; + inode->i_uid = make_kuid(&init_user_ns, (uid_t) attr->va_uid); if (attr->va_gid != -1) - inode->i_gid = (gid_t) attr->va_gid; + inode->i_gid = make_kgid(&init_user_ns, (gid_t) attr->va_gid); if (attr->va_nlink != -1) - inode->i_nlink = attr->va_nlink; + set_nlink(inode, attr->va_nlink); if (attr->va_size != -1) inode->i_size = attr->va_size; if (attr->va_size != -1) @@ -134,7 +131,7 @@ void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) unsigned int valid; /* clean out */ - vattr->va_mode = (umode_t) -1; + vattr->va_mode = -1; vattr->va_uid = (vuid_t) -1; vattr->va_gid = (vgid_t) -1; vattr->va_size = (off_t) -1; @@ -174,10 +171,10 @@ void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) vattr->va_mode = iattr->ia_mode; } if ( valid & ATTR_UID ) { - vattr->va_uid = (vuid_t) iattr->ia_uid; + vattr->va_uid = (vuid_t) from_kuid(&init_user_ns, iattr->ia_uid); } if ( valid & ATTR_GID ) { - vattr->va_gid = (vgid_t) iattr->ia_gid; + vattr->va_gid = (vgid_t) from_kgid(&init_user_ns, iattr->ia_gid); } if ( valid & ATTR_SIZE ) { vattr->va_size = iattr->ia_size; diff --git a/fs/coda/coda_linux.h b/fs/coda/coda_linux.h new file mode 100644 index 00000000000..d42b725b1d2 --- /dev/null +++ b/fs/coda/coda_linux.h @@ -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 diff --git a/fs/coda/dir.c b/fs/coda/dir.c index f89ff083079..cd8a63238b1 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -12,45 +12,45 @@ #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/smp_lock.h> +#include <linux/spinlock.h> +#include <linux/namei.h> #include <asm/uaccess.h> #include <linux/coda.h> -#include <linux/coda_linux.h> #include <linux/coda_psdev.h> -#include <linux/coda_fs_i.h> -#include <linux/coda_cache.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, int mode, struct nameidata *nd); -static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, struct nameidata *nd); +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, int mode); +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, void *buf, filldir_t filldir); +static int coda_readdir(struct file *file, struct dir_context *ctx); /* dentry ops */ -static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd); -static int coda_dentry_delete(struct dentry *); +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 *coda_file, void *buf, - filldir_t filldir); +static int coda_venus_readdir(struct file *, struct dir_context *); /* same as fs/bad_inode.c */ static int coda_return_EIO(void) @@ -59,7 +59,7 @@ static int coda_return_EIO(void) } #define CODA_EIO_ERROR ((void *) (coda_return_EIO)) -static struct dentry_operations coda_dentry_operations = +const struct dentry_operations coda_dentry_operations = { .d_revalidate = coda_dentry_revalidate, .d_delete = coda_dentry_delete, @@ -84,7 +84,7 @@ const struct inode_operations coda_dir_inode_operations = const struct file_operations coda_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, - .readdir = coda_readdir, + .iterate = coda_readdir, .open = coda_open, .release = coda_release, .fsync = coda_fsync, @@ -93,69 +93,64 @@ const struct file_operations coda_dir_operations = { /* inode operations for directories */ /* access routines: lookup, readlink, permission */ -static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd) +static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsigned int flags) { - struct inode *inode = NULL; - struct CodaFid resfid = { { 0, } }; - int type = 0; - int error = 0; + 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) { - printk(KERN_ERR "name too long: lookup, %s (%*s)\n", + 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)) { - error = coda_cnode_makectl(&inode, dir->i_sb); + inode = coda_cnode_makectl(sb); type = CODA_NOCACHE; - goto exit; + } 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); } - lock_kernel(); - - error = venus_lookup(dir->i_sb, coda_i2f(dir), name, length, - &type, &resfid); - if (!error) - error = coda_cnode_make(&inode, &resfid, dir->i_sb); - - unlock_kernel(); - - if (error && error != -ENOENT) - return ERR_PTR(error); - -exit: - entry->d_op = &coda_dentry_operations; - - if (inode && (type & CODA_NOCACHE)) + 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, struct nameidata *nd) +int coda_permission(struct inode *inode, int mask) { - int error = 0; + int error; + + if (mask & MAY_NOT_BLOCK) + return -ECHILD; + + mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (!mask) - return 0; + return 0; - lock_kernel(); + if ((mask & MAY_EXEC) && !execute_ok(inode)) + return -EACCES; if (coda_cache_check(inode, mask)) - goto out; + return 0; - error = venus_access(inode->i_sb, coda_i2f(inode), mask); + error = venus_access(inode->i_sb, coda_i2f(inode), mask); if (!error) coda_cache_enter(inode, mask); - out: - unlock_kernel(); return error; } @@ -192,46 +187,39 @@ static inline void coda_dir_drop_nlink(struct inode *dir) } /* creation routines: create, mknod, mkdir, link, symlink */ -static int coda_create(struct inode *dir, struct dentry *de, int mode, struct nameidata *nd) +static int coda_create(struct inode *dir, struct dentry *de, umode_t mode, bool excl) { - int error=0; + 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; - lock_kernel(); - - if (coda_isroot(dir) && coda_iscontrol(name, length)) { - unlock_kernel(); + 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 ) { - unlock_kernel(); - d_drop(de); - return error; - } + if (error) + goto err_out; inode = coda_iget(dir->i_sb, &newfid, &attrs); - if ( IS_ERR(inode) ) { - unlock_kernel(); - d_drop(de); - return PTR_ERR(inode); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); + goto err_out; } /* invalidate the directory cnode's attributes */ coda_dir_update_mtime(dir); - unlock_kernel(); d_instantiate(de, inode); return 0; +err_out: + d_drop(de); + return error; } -static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) +static int coda_mkdir(struct inode *dir, struct dentry *de, umode_t mode) { struct inode *inode; struct coda_vattr attrs; @@ -240,36 +228,29 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) int error; struct CodaFid newfid; - lock_kernel(); - - if (coda_isroot(dir) && coda_iscontrol(name, len)) { - unlock_kernel(); + 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 ) { - unlock_kernel(); - d_drop(de); - return error; - } + if (error) + goto err_out; inode = coda_iget(dir->i_sb, &newfid, &attrs); - if ( IS_ERR(inode) ) { - unlock_kernel(); - d_drop(de); - return PTR_ERR(inode); + 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); - unlock_kernel(); 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 */ @@ -281,52 +262,38 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, int len = de->d_name.len; int error; - lock_kernel(); - - if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { - unlock_kernel(); + 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); - goto out; + return error; } coda_dir_update_mtime(dir_inode); - atomic_inc(&inode->i_count); + ihold(inode); d_instantiate(de, inode); inc_nlink(inode); - -out: - unlock_kernel(); - return(error); + return 0; } static int coda_symlink(struct inode *dir_inode, struct dentry *de, const char *symname) { - const char *name = de->d_name.name; + const char *name = de->d_name.name; int len = de->d_name.len; int symlen; - int error = 0; - - lock_kernel(); + int error; - if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { - unlock_kernel(); + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) return -EPERM; - } symlen = strlen(symname); - if ( symlen > CODA_MAXPATHLEN ) { - unlock_kernel(); + if (symlen > CODA_MAXPATHLEN) return -ENAMETOOLONG; - } /* * This entry is now negative. Since we do not create @@ -337,53 +304,44 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, symname, symlen); /* mtime is no good anymore */ - if ( !error ) + if (!error) coda_dir_update_mtime(dir_inode); - unlock_kernel(); return error; } /* destruction routines: unlink, rmdir */ -int coda_unlink(struct inode *dir, struct dentry *de) +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; - lock_kernel(); - error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); - if ( error ) { - unlock_kernel(); + if (error) return error; - } coda_dir_update_mtime(dir); drop_nlink(de->d_inode); - unlock_kernel(); return 0; } -int coda_rmdir(struct inode *dir, struct dentry *de) +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; - lock_kernel(); - error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); if (!error) { /* VFS may delete the child */ if (de->d_inode) - de->d_inode->i_nlink = 0; + clear_nlink(de->d_inode); /* fix the link count of the parent */ coda_dir_drop_nlink(dir); coda_dir_update_mtime(dir); } - unlock_kernel(); return error; } @@ -397,15 +355,12 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, int new_length = new_dentry->d_name.len; int error; - lock_kernel(); - 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) ) { + 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); } @@ -417,14 +372,12 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, coda_flag_inode(new_dir, C_VATTR); } } - unlock_kernel(); - return error; } /* file operations for directories */ -int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) +static int coda_readdir(struct file *coda_file, struct dir_context *ctx) { struct coda_file_info *cfi; struct file *host_file; @@ -434,33 +387,19 @@ int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - if (!host_file->f_op) - return -ENOTDIR; - - if (host_file->f_op->readdir) - { - /* potemkin case: we were handed a directory inode. - * We can't use vfs_readdir because we have to keep the file - * position in sync between the coda_file and the host_file. - * and as such we need grab the inode mutex. */ - struct inode *host_inode = host_file->f_path.dentry->d_inode; - + if (host_file->f_op->iterate) { + struct inode *host_inode = file_inode(host_file); mutex_lock(&host_inode->i_mutex); - host_file->f_pos = coda_file->f_pos; - ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { - ret = host_file->f_op->readdir(host_file, buf, filldir); + ret = host_file->f_op->iterate(host_file, ctx); file_accessed(host_file); } - - coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); + return ret; } - else /* Venus: we must read Venus dirents from a file */ - ret = coda_venus_readdir(coda_file, buf, filldir); - - 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) @@ -483,17 +422,14 @@ static inline unsigned int CDT2DT(unsigned char cdt) } /* support routines */ -static int coda_venus_readdir(struct file *coda_file, void *buf, - filldir_t filldir) +static int coda_venus_readdir(struct file *coda_file, struct dir_context *ctx) { - int result = 0; /* # of entries returned */ 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 = - (unsigned long)(&((struct venus_dirent *)0)->d_name); + unsigned long vdir_size = offsetof(struct venus_dirent, d_name); unsigned int type; struct qstr name; ino_t ino; @@ -509,42 +445,31 @@ static int coda_venus_readdir(struct file *coda_file, void *buf, vdir = kmalloc(sizeof(*vdir), GFP_KERNEL); if (!vdir) return -ENOMEM; - if (coda_file->f_pos == 0) { - ret = filldir(buf, ".", 1, 0, de->d_inode->i_ino, DT_DIR); - if (ret < 0) - goto out; - result++; - coda_file->f_pos++; - } - if (coda_file->f_pos == 1) { - ret = filldir(buf, "..", 2, 1, de->d_parent->d_inode->i_ino, DT_DIR); - if (ret < 0) - goto out; - result++; - coda_file->f_pos++; - } + if (!dir_emit_dots(coda_file, ctx)) + goto out; + while (1) { /* read entries from the directory file */ - ret = kernel_read(host_file, coda_file->f_pos - 2, (char *)vdir, + ret = kernel_read(host_file, ctx->pos - 2, (char *)vdir, sizeof(*vdir)); if (ret < 0) { - printk(KERN_ERR "coda readdir: read dir %s failed %d\n", - coda_f2s(&cii->c_fid), ret); + 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) { - printk(KERN_ERR "coda readdir: short read on %s\n", - coda_f2s(&cii->c_fid)); + 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) { - printk(KERN_ERR "coda readdir: invalid dir %s\n", - coda_f2s(&cii->c_fid)); + pr_err("%s: invalid dir %s\n", + __func__, coda_f2s(&cii->c_fid)); ret = -EBADF; break; } @@ -554,44 +479,36 @@ static int coda_venus_readdir(struct file *coda_file, void *buf, /* Make sure we skip '.' and '..', we already got those */ if (name.name[0] == '.' && (name.len == 1 || - (vdir->d_name[1] == '.' && name.len == 2))) + (name.name[1] == '.' && name.len == 2))) vdir->d_fileno = name.len = 0; /* skip null entries */ if (vdir->d_fileno && name.len) { - /* try to look up this entry in the dcache, that way - * userspace doesn't have to worry about breaking - * getcwd by having mismatched inode numbers for - * internal volume mountpoints. */ - ino = find_inode_number(de, &name); - if (!ino) ino = vdir->d_fileno; - + ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); - ret = filldir(buf, name.name, name.len, - coda_file->f_pos, ino, type); - /* failure means no space for filling in this round */ - if (ret < 0) break; - result++; + 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. */ - coda_file->f_pos += vdir->d_reclen; + ctx->pos += vdir->d_reclen; } out: kfree(vdir); - return result ? result : ret; + return 0; } /* called when a cache lookup succeeds */ -static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) +static int coda_dentry_revalidate(struct dentry *de, unsigned int flags) { - struct inode *inode = de->d_inode; + struct inode *inode; struct coda_inode_info *cii; - if (!inode) - return 1; - lock_kernel(); - if (coda_isroot(inode)) + if (flags & LOOKUP_RCU) + return -ECHILD; + + inode = de->d_inode; + if (!inode || coda_isroot(inode)) goto out; if (is_bad_inode(inode)) goto bad; @@ -606,18 +523,17 @@ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) if (cii->c_flags & C_FLUSH) coda_flag_inode_children(inode, C_FLUSH); - if (atomic_read(&de->d_count) > 1) + 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: - unlock_kernel(); return 0; out: - unlock_kernel(); return 1; } @@ -625,7 +541,7 @@ out: * 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(struct dentry * dentry) +static int coda_dentry_delete(const struct dentry * dentry) { int flags; @@ -647,23 +563,21 @@ static int coda_dentry_delete(struct dentry * dentry) * cache manager Venus issues a downcall to the kernel when this * happens */ -int coda_revalidate_inode(struct dentry *dentry) +int coda_revalidate_inode(struct inode *inode) { struct coda_vattr attr; - int error = 0; + int error; int old_mode; ino_t old_ino; - struct inode *inode = dentry->d_inode; struct coda_inode_info *cii = ITOC(inode); - lock_kernel(); - if ( !cii->c_flags ) - goto ok; + 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 ) - goto return_bad; + if (error) + return -EIO; /* this inode may be lost if: - it's ino changed @@ -675,24 +589,20 @@ int coda_revalidate_inode(struct dentry *dentry) coda_vattr_to_iattr(inode, &attr); if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) { - printk("Coda: inode %ld, fid %s changed type!\n", - inode->i_ino, coda_f2s(&(cii->c_fid))); + 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) - goto return_bad; + 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); } - -ok: - unlock_kernel(); return 0; - -return_bad: - unlock_kernel(); - return -EIO; } diff --git a/fs/coda/file.c b/fs/coda/file.c index 29137ff3ca6..9e83b779021 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -13,16 +13,17 @@ #include <linux/file.h> #include <linux/fs.h> #include <linux/stat.h> +#include <linux/cred.h> #include <linux/errno.h> -#include <linux/smp_lock.h> +#include <linux/spinlock.h> #include <linux/string.h> +#include <linux/slab.h> #include <asm/uaccess.h> #include <linux/coda.h> -#include <linux/coda_linux.h> -#include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> +#include "coda_linux.h" #include "coda_int.h" static ssize_t @@ -35,7 +36,7 @@ coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *p BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - if (!host_file->f_op || !host_file->f_op->read) + if (!host_file->f_op->read) return -EINVAL; return host_file->f_op->read(host_file, buf, count, ppos); @@ -46,6 +47,8 @@ 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; @@ -53,16 +56,17 @@ coda_file_splice_read(struct file *coda_file, loff_t *ppos, BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - if (!host_file->f_op || !host_file->f_op->splice_read) - return -EINVAL; + splice_read = host_file->f_op->splice_read; + if (!splice_read) + splice_read = default_file_splice_read; - return host_file->f_op->splice_read(host_file, ppos, pipe, count,flags); + 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 = coda_file->f_path.dentry->d_inode; + struct inode *host_inode, *coda_inode = file_inode(coda_file); struct coda_file_info *cfi; struct file *host_file; ssize_t ret; @@ -71,10 +75,11 @@ coda_file_write(struct file *coda_file, const char __user *buf, size_t count, lo BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - if (!host_file->f_op || !host_file->f_op->write) + if (!host_file->f_op->write) return -EINVAL; - host_inode = host_file->f_path.dentry->d_inode; + 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); @@ -83,6 +88,7 @@ coda_file_write(struct file *coda_file, const char __user *buf, size_t count, lo 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; } @@ -99,24 +105,29 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma) BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - if (!host_file->f_op || !host_file->f_op->mmap) + if (!host_file->f_op->mmap) return -ENODEV; - coda_inode = coda_file->f_path.dentry->d_inode; - host_inode = host_file->f_path.dentry->d_inode; + 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) + 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 = ITOC(coda_inode); cii->c_mapcount++; cfi->cfi_mapcount++; + spin_unlock(&cii->c_lock); return host_file->f_op->mmap(host_file, vma); } @@ -133,8 +144,6 @@ int coda_open(struct inode *coda_inode, struct file *coda_file) if (!cfi) return -ENOMEM; - lock_kernel(); - error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, &host_file); if (!host_file) @@ -142,7 +151,6 @@ int coda_open(struct inode *coda_inode, struct file *coda_file) if (error) { kfree(cfi); - unlock_kernel(); return error; } @@ -154,8 +162,6 @@ int coda_open(struct inode *coda_inode, struct file *coda_file) BUG_ON(coda_file->private_data != NULL); coda_file->private_data = cfi; - - unlock_kernel(); return 0; } @@ -166,66 +172,59 @@ int coda_release(struct inode *coda_inode, struct file *coda_file) struct coda_file_info *cfi; struct coda_inode_info *cii; struct inode *host_inode; - int err = 0; - - lock_kernel(); + 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_uid); + coda_flags, coda_file->f_cred->fsuid); - host_inode = cfi->cfi_container->f_path.dentry->d_inode; + 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; - unlock_kernel(); - /* 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, struct dentry *coda_dentry, int datasync) +int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync) { struct file *host_file; - struct dentry *host_dentry; - struct inode *host_inode, *coda_inode = coda_dentry->d_inode; + struct inode *coda_inode = file_inode(coda_file); struct coda_file_info *cfi; - int err = 0; + 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; - if (host_file->f_op && host_file->f_op->fsync) { - host_dentry = host_file->f_path.dentry; - host_inode = host_dentry->d_inode; - mutex_lock(&host_inode->i_mutex); - err = host_file->f_op->fsync(host_file, host_dentry, datasync); - mutex_unlock(&host_inode->i_mutex); - } - - if ( !err && !datasync ) { - lock_kernel(); + err = vfs_fsync(host_file, datasync); + if (!err && !datasync) err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); - unlock_kernel(); - } + mutex_unlock(&coda_inode->i_mutex); return err; } diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 2f58dfc7008..fe3afb2de88 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -15,26 +15,27 @@ #include <linux/stat.h> #include <linux/errno.h> #include <linux/unistd.h> -#include <linux/smp_lock.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 <asm/system.h> #include <asm/uaccess.h> #include <linux/fs.h> #include <linux/vmalloc.h> #include <linux/coda.h> -#include <linux/coda_linux.h> #include <linux/coda_psdev.h> -#include <linux/coda_fs_i.h> -#include <linux/coda_cache.h> +#include "coda_linux.h" +#include "coda_cache.h" #include "coda_int.h" /* VFS super_block ops */ -static void coda_clear_inode(struct inode *); +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); @@ -43,29 +44,36 @@ static struct kmem_cache * coda_inode_cachep; static struct inode *coda_alloc_inode(struct super_block *sb) { struct coda_inode_info *ei; - ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, GFP_KERNEL); + 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 = 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_destroy_inode(struct inode *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 init_once(struct kmem_cache * cachep, void *foo) +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 coda_init_inodecache(void) +int __init coda_init_inodecache(void) { coda_inode_cachep = kmem_cache_create("coda_inode_cache", sizeof(struct coda_inode_info), @@ -78,11 +86,17 @@ int coda_init_inodecache(void) 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; } @@ -92,7 +106,7 @@ static const struct super_operations coda_super_operations = { .alloc_inode = coda_alloc_inode, .destroy_inode = coda_destroy_inode, - .clear_inode = coda_clear_inode, + .evict_inode = coda_evict_inode, .put_super = coda_put_super, .statfs = coda_statfs, .remount_fs = coda_remount, @@ -100,73 +114,83 @@ static const struct super_operations coda_super_operations = static int get_device_index(struct coda_mount_data *data) { - struct file *file; + struct fd f; struct inode *inode; int idx; - if(data == NULL) { - printk("coda_read_super: Bad mount data\n"); + if (data == NULL) { + pr_warn("%s: Bad mount data\n", __func__); return -1; } - if(data->version != CODA_MOUNT_VERSION) { - printk("coda_read_super: Bad mount version\n"); + if (data->version != CODA_MOUNT_VERSION) { + pr_warn("%s: Bad mount version\n", __func__); return -1; } - file = fget(data->fd); - inode = NULL; - if(file) - inode = file->f_path.dentry->d_inode; - - if(!inode || !S_ISCHR(inode->i_mode) || - imajor(inode) != CODA_PSDEV_MAJOR) { - if(file) - fput(file); - - printk("coda_read_super: Bad file\n"); - 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); - fput(file); + fdput(f); - if(idx < 0 || idx >= MAX_CODADEVS) { - printk("coda_read_super: Bad minor number\n"); + 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 = 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; - printk(KERN_INFO "coda_read_super: device index: %i\n", idx); + pr_info("%s: device index: %i\n", __func__, idx); vc = &coda_comms[idx]; + mutex_lock(&vc->vc_mutex); + if (!vc->vc_inuse) { - printk("coda_read_super: No pseudo device\n"); - return -EINVAL; + pr_warn("%s: No pseudo device\n", __func__); + error = -EINVAL; + goto unlock_out; } - if ( vc->vc_sb ) { - printk("coda_read_super: Device already mounted\n"); - return -EBUSY; + 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; @@ -174,55 +198,68 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) 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 ) { - printk("coda_read_super: coda_get_rootfid failed with %d\n", - error); + pr_warn("%s: coda_get_rootfid failed with %d\n", + __func__, error); goto error; } - printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid)); + pr_info("%s: rootfid is %s\n", __func__, coda_f2s(&fid)); /* make root inode */ - error = coda_cnode_make(&root, &fid, sb); - if ( error || !root ) { - printk("Failure of coda_cnode_make for root: error %d\n", error); - goto error; + 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; } - printk("coda_read_super: rootinode is %ld dev %s\n", - root->i_ino, root->i_sb->s_id); - sb->s_root = d_alloc_root(root); - if (!sb->s_root) + 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: - if (root) - iput(root); - if (vc) - vc->vc_sb = NULL; + } + return 0; - return -EINVAL; +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) { - coda_vcp(sb)->vc_sb = NULL; + 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); - printk("Coda: Bye bye.\n"); + pr_info("Bye bye.\n"); } -static void coda_clear_inode(struct inode *inode) +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); + int err = coda_revalidate_inode(dentry->d_inode); if (!err) generic_fillattr(dentry->d_inode, stat); return err; @@ -234,8 +271,6 @@ int coda_setattr(struct dentry *de, struct iattr *iattr) struct coda_vattr vattr; int error; - lock_kernel(); - memset(&vattr, 0, sizeof(vattr)); inode->i_ctime = CURRENT_TIME_SEC; @@ -245,13 +280,10 @@ int coda_setattr(struct dentry *de, struct iattr *iattr) /* Venus is responsible for truncating the container-file!!! */ error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr); - if ( !error ) { + if (!error) { coda_vattr_to_iattr(inode, &vattr); coda_cache_clear_inode(inode); } - - unlock_kernel(); - return error; } @@ -265,12 +297,8 @@ static int coda_statfs(struct dentry *dentry, struct kstatfs *buf) { int error; - lock_kernel(); - error = venus_statfs(dentry, buf); - unlock_kernel(); - if (error) { /* fake something like AFS does */ buf->f_blocks = 9000000; @@ -290,17 +318,18 @@ static int coda_statfs(struct dentry *dentry, struct kstatfs *buf) /* init_coda: used by filesystems.c to register coda */ -static int coda_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) +static struct dentry *coda_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - return get_sb_nodev(fs_type, flags, data, coda_fill_super, mnt); + return mount_nodev(fs_type, flags, data, coda_fill_super); } struct file_system_type coda_fs_type = { .owner = THIS_MODULE, .name = "coda", - .get_sb = coda_get_sb, + .mount = coda_mount, .kill_sb = kill_anon_super, .fs_flags = FS_BINARY_MOUNTDATA, }; +MODULE_ALIAS_FS("coda"); diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c21a1f552a6..3f5de96bbb5 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -1,6 +1,6 @@ /* * Pioctl operations for Coda. - * Original version: (C) 1996 Peter Braam + * 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 @@ -19,77 +19,72 @@ #include <asm/uaccess.h> #include <linux/coda.h> -#include <linux/coda_linux.h> -#include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> +#include "coda_linux.h" + /* pioctl ops */ -static int coda_ioctl_permission(struct inode *inode, int mask, - struct nameidata *nd); -static int coda_pioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long user_data); +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 = -{ +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, - .ioctl = coda_pioctl, + .unlocked_ioctl = coda_pioctl, + .llseek = noop_llseek, }; /* the coda pioctl inode ops */ -static int coda_ioctl_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int coda_ioctl_permission(struct inode *inode, int mask) { - return 0; + return (mask & MAY_EXEC) ? -EACCES : 0; } -static int coda_pioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long user_data) +static long coda_pioctl(struct file *filp, unsigned int cmd, + unsigned long user_data) { - struct nameidata nd; - int error; + struct path path; + int error; struct PioctlData data; - struct inode *target_inode = NULL; - struct coda_inode_info *cnp; + 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_walk(data.path, &nd); - } else { - error = user_path_walk_link(data.path, &nd); - } - - if ( error ) { + /* 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; - } else { - target_inode = nd.path.dentry->d_inode; - } - + + target_inode = path.dentry->d_inode; + /* return if it is not a Coda inode */ - if ( target_inode->i_sb != inode->i_sb ) { - path_put(&nd.path); - return -EINVAL; + if (target_inode->i_sb != inode->i_sb) { + error = -EINVAL; + goto out; } /* now proceed to make the upcall */ - cnp = ITOC(target_inode); + cnp = ITOC(target_inode); error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); - - path_put(&nd.path); - return error; +out: + path_put(&path); + return error; } - diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index e3eb3556622..5c1e4242368 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -2,7 +2,7 @@ * An implementation of a loadable kernel mode driver providing * multiple kernel/user space bidirectional communications links. * - * Author: Alan Cox <alan@redhat.com> + * 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 @@ -22,6 +22,7 @@ #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> @@ -34,18 +35,18 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/device.h> +#include <linux/pid_namespace.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/poll.h> #include <asm/uaccess.h> #include <linux/coda.h> -#include <linux/coda_linux.h> -#include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> +#include "coda_linux.h" + #include "coda_int.h" /* statistics */ @@ -66,14 +67,15 @@ static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) 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 int coda_psdev_ioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long arg) +static long coda_psdev_ioctl(struct file * filp, unsigned int cmd, unsigned long arg) { unsigned int data; @@ -108,25 +110,18 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, return -EFAULT; if (DOWNCALL(hdr.opcode)) { - struct super_block *sb = NULL; - union outputArgs *dcbuf; + union outputArgs *dcbuf; int size = sizeof(*dcbuf); - sb = vcp->vc_sb; - if ( !sb ) { - count = nbytes; - goto out; - } - if ( nbytes < sizeof(struct coda_out_hdr) ) { - printk("coda_downcall opc %d uniq %d, not enough!\n", - hdr.opcode, hdr.unique); + pr_warn("coda_downcall opc %d uniq %d, not enough!\n", + hdr.opcode, hdr.unique); count = nbytes; goto out; } if ( nbytes > size ) { - printk("Coda: downcall opc %d, uniq %d, too much!", - hdr.opcode, hdr.unique); + pr_warn("downcall opc %d, uniq %d, too much!", + hdr.opcode, hdr.unique); nbytes = size; } CODA_ALLOC(dcbuf, union outputArgs *, nbytes); @@ -137,13 +132,12 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, } /* what downcall errors does Venus handle ? */ - lock_kernel(); - error = coda_downcall(hdr.opcode, dcbuf, sb); - unlock_kernel(); + error = coda_downcall(vcp, hdr.opcode, dcbuf); CODA_FREE(dcbuf, nbytes); if (error) { - printk("psdev_write: coda_downcall error: %d\n", error); + pr_warn("%s: coda_downcall error: %d\n", + __func__, error); retval = error; goto out; } @@ -152,7 +146,7 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, } /* Look for the message on the processing queue. */ - lock_kernel(); + 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) { @@ -161,31 +155,32 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, break; } } - unlock_kernel(); + mutex_unlock(&vcp->vc_mutex); if (!req) { - printk("psdev_write: msg (%d, %d) not found\n", - hdr.opcode, hdr.unique); + 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) { - printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n", - req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique); + 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 |= REQ_ABORT; + 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 |= REQ_WRITE; + req->uc_outSize = nbytes; + req->uc_flags |= CODA_REQ_WRITE; count = nbytes; /* Convert filedescriptor into a file handle */ @@ -216,7 +211,7 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, if (nbytes == 0) return 0; - lock_kernel(); + mutex_lock(&vcp->vc_mutex); add_wait_queue(&vcp->vc_waitq, &wait); set_current_state(TASK_INTERRUPTIBLE); @@ -230,7 +225,9 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, retval = -ERESTARTSYS; break; } + mutex_unlock(&vcp->vc_mutex); schedule(); + mutex_lock(&vcp->vc_mutex); } set_current_state(TASK_RUNNING); @@ -245,8 +242,8 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, /* Move the input args into userspace */ count = req->uc_inSize; if (nbytes < req->uc_inSize) { - printk ("psdev_read: Venus read %ld bytes of %d in message\n", - (long)nbytes, req->uc_inSize); + pr_warn("%s: Venus read %ld bytes of %d in message\n", + __func__, (long)nbytes, req->uc_inSize); count = nbytes; } @@ -254,8 +251,8 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, retval = -EFAULT; /* If request was not a signal, enqueue and don't free */ - if (!(req->uc_flags & REQ_ASYNC)) { - req->uc_flags |= REQ_READ; + if (!(req->uc_flags & CODA_REQ_ASYNC)) { + req->uc_flags |= CODA_REQ_READ; list_add_tail(&(req->uc_chain), &vcp->vc_processing); goto out; } @@ -263,7 +260,7 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); kfree(req); out: - unlock_kernel(); + mutex_unlock(&vcp->vc_mutex); return (count ? count : retval); } @@ -272,14 +269,20 @@ 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; - lock_kernel(); - err = -EBUSY; vcp = &coda_comms[idx]; + mutex_lock(&vcp->vc_mutex); + if (!vcp->vc_inuse) { vcp->vc_inuse++; @@ -293,7 +296,7 @@ static int coda_psdev_open(struct inode * inode, struct file * file) err = 0; } - unlock_kernel(); + mutex_unlock(&vcp->vc_mutex); return err; } @@ -304,36 +307,36 @@ static int coda_psdev_release(struct inode * inode, struct file * file) struct upc_req *req, *tmp; if (!vcp || !vcp->vc_inuse ) { - printk("psdev_release: Not open.\n"); + pr_warn("%s: Not open.\n", __func__); return -1; } - lock_kernel(); + 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 & REQ_ASYNC) { + if (req->uc_flags & CODA_REQ_ASYNC) { CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); kfree(req); continue; } - req->uc_flags |= REQ_ABORT; + 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 |= REQ_ABORT; + req->uc_flags |= CODA_REQ_ABORT; wake_up(&req->uc_sleep); } file->private_data = NULL; vcp->vc_inuse--; - unlock_kernel(); + mutex_unlock(&vcp->vc_mutex); return 0; } @@ -343,17 +346,18 @@ static const struct file_operations coda_psdev_fops = { .read = coda_psdev_read, .write = coda_psdev_write, .poll = coda_psdev_poll, - .ioctl = coda_psdev_ioctl, + .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)) { - printk(KERN_ERR "coda_psdev: unable to get major %d\n", - CODA_PSDEV_MAJOR); + pr_err("%s: unable to get major %d\n", + __func__, CODA_PSDEV_MAJOR); return -EIO; } coda_psdev_class = class_create(THIS_MODULE, "coda"); @@ -361,9 +365,11 @@ static int init_coda_psdev(void) err = PTR_ERR(coda_psdev_class); goto out_chrdev; } - for (i = 0; i < MAX_CODADEVS; i++) + 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), "cfs%d", i); + MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i); + } coda_sysctl_init(); goto out; @@ -377,11 +383,7 @@ 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"); -#ifdef CONFIG_CODA_FS_OLD_API -MODULE_VERSION("5.3.21"); -#else MODULE_VERSION("6.6"); -#endif static int __init init_coda(void) { @@ -393,13 +395,13 @@ static int __init init_coda(void) goto out2; status = init_coda_psdev(); if ( status ) { - printk("Problem (%d) in init_coda_psdev\n", status); + pr_warn("Problem (%d) in init_coda_psdev\n", status); goto out1; } status = register_filesystem(&coda_fs_type); if (status) { - printk("coda: failed to register filesystem!\n"); + pr_warn("failed to register filesystem!\n"); goto out; } return 0; @@ -420,9 +422,8 @@ static void __exit exit_coda(void) int err, i; err = unregister_filesystem(&coda_fs_type); - if ( err != 0 ) { - printk("coda: failed to unregister filesystem\n"); - } + 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); diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index 4513b725845..ab94ef63cae 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -14,12 +14,11 @@ #include <linux/stat.h> #include <linux/errno.h> #include <linux/pagemap.h> -#include <linux/smp_lock.h> #include <linux/coda.h> -#include <linux/coda_linux.h> #include <linux/coda_psdev.h> -#include <linux/coda_fs_i.h> + +#include "coda_linux.h" static int coda_symlink_filler(struct file *file, struct page *page) { @@ -29,11 +28,9 @@ static int coda_symlink_filler(struct file *file, struct page *page) unsigned int len = PAGE_SIZE; char *p = kmap(page); - lock_kernel(); cii = ITOC(inode); error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len); - unlock_kernel(); if (error) goto fail; SetPageUptodate(page); diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index 81b7771c646..34218a8a28c 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -11,39 +11,36 @@ #include "coda_int.h" +#ifdef CONFIG_SYSCTL static struct ctl_table_header *fs_table_header; -static ctl_table coda_table[] = { +static struct ctl_table coda_table[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "timeout", .data = &coda_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec + .proc_handler = proc_dointvec }, { - .ctl_name = CTL_UNNUMBERED, .procname = "hard", .data = &coda_hard, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec + .proc_handler = proc_dointvec }, { - .ctl_name = CTL_UNNUMBERED, .procname = "fake_statfs", .data = &coda_fake_statfs, .maxlen = sizeof(int), .mode = 0600, - .proc_handler = &proc_dointvec + .proc_handler = proc_dointvec }, {} }; -static ctl_table fs_table[] = { +static struct ctl_table fs_table[] = { { - .ctl_name = CTL_UNNUMBERED, .procname = "coda", .mode = 0555, .child = coda_table @@ -51,21 +48,26 @@ static ctl_table fs_table[] = { {} }; - void coda_sysctl_init(void) { -#ifdef CONFIG_SYSCTL if ( !fs_table_header ) fs_table_header = register_sysctl_table(fs_table); -#endif } void coda_sysctl_clean(void) { -#ifdef CONFIG_SYSCTL if ( fs_table_header ) { unregister_sysctl_table(fs_table_header); fs_table_header = NULL; } -#endif } + +#else +void coda_sysctl_init(void) +{ +} + +void coda_sysctl_clean(void) +{ +} +#endif diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 359e531094d..21fcf8dcb9c 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -14,7 +14,6 @@ * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. */ -#include <asm/system.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/types.h> @@ -26,15 +25,16 @@ #include <linux/stat.h> #include <linux/errno.h> #include <linux/string.h> +#include <linux/slab.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <linux/vmalloc.h> #include <linux/vfs.h> #include <linux/coda.h> -#include <linux/coda_linux.h> #include <linux/coda_psdev.h> -#include <linux/coda_fs_i.h> -#include <linux/coda_cache.h> +#include "coda_linux.h" +#include "coda_cache.h" #include "coda_int.h" @@ -50,14 +50,10 @@ static void *alloc_upcall(int opcode, int size) return ERR_PTR(-ENOMEM); inp->ih.opcode = opcode; - inp->ih.pid = current->pid; - inp->ih.pgid = task_pgrp_nr(current); -#ifdef CONFIG_CODA_FS_OLD_API - memset(&inp->ih.cred, 0, sizeof(struct coda_cred)); - inp->ih.cred.cr_fsuid = current->fsuid; -#else - inp->ih.uid = current->fsuid; -#endif + 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; } @@ -161,25 +157,16 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, } int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) + kuid_t uid) { union inputArgs *inp; union outputArgs *outp; int insize, outsize, error; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred = { 0, }; - cred.cr_fsuid = uid; -#endif insize = SIZE(release); UPARG(CODA_CLOSE); -#ifdef CONFIG_CODA_FS_OLD_API - memcpy(&(inp->ih.cred), &cred, sizeof(cred)); -#else - inp->ih.uid = uid; -#endif - + inp->ih.uid = from_kuid(&init_user_ns, uid); inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; @@ -521,8 +508,8 @@ int venus_pioctl(struct super_block *sb, struct CodaFid *fid, 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) ) { + if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data, + data->vi.in, data->vi.in_size)) { error = -EINVAL; goto exit; } @@ -531,8 +518,8 @@ int venus_pioctl(struct super_block *sb, struct CodaFid *fid, &outsize, inp); if (error) { - printk("coda_pioctl: Venus returns: %d for %s\n", - error, coda_f2s(fid)); + pr_warn("%s: Venus returns: %d for %s\n", + __func__, error, coda_f2s(fid)); goto exit; } @@ -616,9 +603,10 @@ static void coda_unblock_signals(sigset_t *old) (((r)->uc_opcode != CODA_CLOSE && \ (r)->uc_opcode != CODA_STORE && \ (r)->uc_opcode != CODA_RELEASE) || \ - (r)->uc_flags & REQ_READ)) + (r)->uc_flags & CODA_REQ_READ)) -static inline void coda_waitfor_upcall(struct upc_req *req) +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; @@ -636,7 +624,7 @@ static inline void coda_waitfor_upcall(struct upc_req *req) set_current_state(TASK_UNINTERRUPTIBLE); /* got a reply */ - if (req->uc_flags & (REQ_WRITE | REQ_ABORT)) + if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT)) break; if (blocked && time_after(jiffies, timeout) && @@ -651,10 +639,12 @@ static inline void coda_waitfor_upcall(struct upc_req *req) break; } + mutex_unlock(&vcp->vc_mutex); if (blocked) schedule_timeout(HZ); else schedule(); + mutex_lock(&vcp->vc_mutex); } if (blocked) coda_unblock_signals(&old); @@ -679,18 +669,23 @@ static int coda_upcall(struct venus_comm *vcp, { union outputArgs *out; union inputArgs *sig_inputArgs; - struct upc_req *req, *sig_req; - int error = 0; + struct upc_req *req = NULL, *sig_req; + int error; + + mutex_lock(&vcp->vc_mutex); if (!vcp->vc_inuse) { - printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n"); - return -ENXIO; + 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) - return -ENOMEM; + if (!req) { + error = -ENOMEM; + goto exit; + } req->uc_data = (void *)buffer; req->uc_flags = 0; @@ -717,10 +712,10 @@ static int coda_upcall(struct venus_comm *vcp, * ENODEV. */ /* Go to sleep. Wake up on signals only after the timeout. */ - coda_waitfor_upcall(req); + coda_waitfor_upcall(vcp, req); /* Op went through, interrupt or not... */ - if (req->uc_flags & REQ_WRITE) { + 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; @@ -729,18 +724,18 @@ static int coda_upcall(struct venus_comm *vcp, } error = -EINTR; - if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) { - printk(KERN_WARNING "coda: Unexpected interruption.\n"); + 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 & REQ_READ)) + if (!(req->uc_flags & CODA_REQ_READ)) goto exit; /* Venus saw the upcall, make sure we can send interrupt signal */ if (!vcp->vc_inuse) { - printk(KERN_INFO "coda: Venus dead, not sending signal.\n"); + pr_info("Venus dead, not sending signal.\n"); goto exit; } @@ -759,7 +754,7 @@ static int coda_upcall(struct venus_comm *vcp, sig_inputArgs->ih.opcode = CODA_SIGNAL; sig_inputArgs->ih.unique = req->uc_unique; - sig_req->uc_flags = REQ_ASYNC; + 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); @@ -771,6 +766,7 @@ static int coda_upcall(struct venus_comm *vcp, exit: kfree(req); + mutex_unlock(&vcp->vc_mutex); return error; } @@ -808,21 +804,24 @@ exit: * * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */ -int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) +int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out) { struct inode *inode = NULL; - struct CodaFid *fid, *newfid; + struct CodaFid *fid = NULL, *newfid; + struct super_block *sb; /* Handle invalidation requests. */ - if ( !sb || !sb->s_root) - return 0; + 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); + coda_flag_inode(sb->s_root->d_inode, C_FLUSH); break; case CODA_PURGEUSER: @@ -831,45 +830,53 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_ZAPDIR: fid = &out->coda_zapdir.CodaFid; - inode = coda_fid_to_inode(fid, sb); - if (inode) { - coda_flag_inode_children(inode, C_PURGE); - coda_flag_inode(inode, C_VATTR); - } break; case CODA_ZAPFILE: fid = &out->coda_zapfile.CodaFid; - inode = coda_fid_to_inode(fid, sb); - if (inode) - coda_flag_inode(inode, C_VATTR); 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); - if (inode) { - 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); +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: - fid = &out->coda_replace.OldFid; newfid = &out->coda_replace.NewFid; - inode = coda_fid_to_inode(fid, sb); - if (inode) - coda_replace_fid(inode, fid, newfid); + coda_replace_fid(inode, fid, newfid); break; } - - if (inode) - iput(inode); - + iput(inode); return 0; } |
