mntpt.o \
        proc.o \
        rxrpc.o \
+       security.o \
        server.o \
        super.o \
        vlclient.o \
 
 
 #include <linux/in.h>
 
+#define AFS_MAXCELLNAME        64              /* maximum length of a cell name */
+#define AFS_MAXVOLNAME 64              /* maximum length of a volume name */
+
 typedef unsigned                       afs_volid_t;
 typedef unsigned                       afs_vnodeid_t;
 typedef unsigned long long             afs_dataversion_t;
        } servers[8];
 };
 
+/*
+ * AFS security ACE access mask
+ */
+typedef u32 afs_access_t;
+#define AFS_ACE_READ           0x00000001U     /* - permission to read a file/dir */
+#define AFS_ACE_WRITE          0x00000002U     /* - permission to write/chmod a file */
+#define AFS_ACE_INSERT         0x00000004U     /* - permission to create dirent in a dir */
+#define AFS_ACE_LOOKUP         0x00000008U     /* - permission to lookup a file/dir in a dir */
+#define AFS_ACE_DELETE         0x00000010U     /* - permission to delete a dirent from a dir */
+#define AFS_ACE_LOCK           0x00000020U     /* - permission to lock a file */
+#define AFS_ACE_ADMINISTER     0x00000040U     /* - permission to change ACL */
+#define AFS_ACE_USER_A         0x01000000U     /* - 'A' user-defined permission */
+#define AFS_ACE_USER_B         0x02000000U     /* - 'B' user-defined permission */
+#define AFS_ACE_USER_C         0x04000000U     /* - 'C' user-defined permission */
+#define AFS_ACE_USER_D         0x08000000U     /* - 'D' user-defined permission */
+#define AFS_ACE_USER_E         0x10000000U     /* - 'E' user-defined permission */
+#define AFS_ACE_USER_F         0x20000000U     /* - 'F' user-defined permission */
+#define AFS_ACE_USER_G         0x40000000U     /* - 'G' user-defined permission */
+#define AFS_ACE_USER_H         0x80000000U     /* - 'H' user-defined permission */
+
 /*
  * AFS file status information
  */
        afs_dataversion_t       data_version;   /* current data version */
        unsigned                author;         /* author ID */
        unsigned                owner;          /* owner ID */
-       unsigned                caller_access;  /* access rights for authenticated caller */
-       unsigned                anon_access;    /* access rights for unauthenticated caller */
+       afs_access_t            caller_access;  /* access rights for authenticated caller */
+       afs_access_t            anon_access;    /* access rights for unauthenticated caller */
        umode_t                 mode;           /* UNIX mode */
        struct afs_fid          parent;         /* parent file ID */
        time_t                  mtime_client;   /* last time client changed data */
 
                return; /* someone else is dealing with it */
 
        if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
-               if (afs_vnode_fetch_status(vnode) < 0)
+               if (S_ISDIR(vnode->vfs_inode.i_mode))
+                       afs_clear_permits(vnode);
+
+               if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0)
                        goto out;
 
                if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
 
 
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/key.h>
+#include <linux/ctype.h>
+#include <keys/rxrpc-type.h>
 #include "internal.h"
 
 DECLARE_RWSEM(afs_proc_cells_sem);
 static struct afs_cell *afs_cell_root;
 
 /*
- * create a cell record
- * - "name" is the name of the cell
- * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
+ * allocate a cell record and fill in its name, VL server address list and
+ * allocate an anonymous key
  */
-struct afs_cell *afs_cell_create(const char *name, char *vllist)
+static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
 {
        struct afs_cell *cell;
-       char *next;
+       size_t namelen;
+       char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next;
        int ret;
 
        _enter("%s,%s", name, vllist);
 
        BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
 
+       namelen = strlen(name);
+       if (namelen > AFS_MAXCELLNAME)
+               return ERR_PTR(-ENAMETOOLONG);
+
        /* allocate and initialise a cell record */
-       cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
+       cell = kzalloc(sizeof(struct afs_cell) + namelen + 1, GFP_KERNEL);
        if (!cell) {
                _leave(" = -ENOMEM");
                return ERR_PTR(-ENOMEM);
        }
 
-       down_write(&afs_cells_sem);
+       memcpy(cell->name, name, namelen);
+       cell->name[namelen] = 0;
 
-       memset(cell, 0, sizeof(struct afs_cell));
        atomic_set(&cell->usage, 1);
-
        INIT_LIST_HEAD(&cell->link);
-
        rwlock_init(&cell->servers_lock);
        INIT_LIST_HEAD(&cell->servers);
-
        init_rwsem(&cell->vl_sem);
        INIT_LIST_HEAD(&cell->vl_list);
        spin_lock_init(&cell->vl_lock);
 
-       strcpy(cell->name, name);
-
        /* fill in the VL server list from the rest of the string */
-       ret = -EINVAL;
        do {
                unsigned a, b, c, d;
 
                        *next++ = 0;
 
                if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
-                       goto badaddr;
+                       goto bad_address;
 
                if (a > 255 || b > 255 || c > 255 || d > 255)
-                       goto badaddr;
+                       goto bad_address;
 
                cell->vl_addrs[cell->vl_naddrs++].s_addr =
                        htonl((a << 24) | (b << 16) | (c << 8) | d);
 
-               if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
-                       break;
+       } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (vllist = next));
+
+       /* create a key to represent an anonymous user */
+       memcpy(keyname, "afs@", 4);
+       dp = keyname + 4;
+       cp = cell->name;
+       do {
+               *dp++ = toupper(*cp);
+       } while (*cp++);
+       cell->anonymous_key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
+                                       KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
+       if (IS_ERR(cell->anonymous_key)) {
+               _debug("no key");
+               ret = PTR_ERR(cell->anonymous_key);
+               goto error;
+       }
+
+       ret = key_instantiate_and_link(cell->anonymous_key, NULL, 0,
+                                      NULL, NULL);
+       if (ret < 0) {
+               _debug("instantiate failed");
+               goto error;
+       }
+
+       _debug("anon key %p{%x}",
+              cell->anonymous_key, key_serial(cell->anonymous_key));
+
+       _leave(" = %p", cell);
+       return cell;
+
+bad_address:
+       printk(KERN_ERR "kAFS: bad VL server IP address\n");
+       ret = -EINVAL;
+error:
+       key_put(cell->anonymous_key);
+       kfree(cell);
+       _leave(" = %d", ret);
+       return ERR_PTR(ret);
+}
 
-       } while ((vllist = next));
+/*
+ * create a cell record
+ * - "name" is the name of the cell
+ * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
+ */
+struct afs_cell *afs_cell_create(const char *name, char *vllist)
+{
+       struct afs_cell *cell;
+       int ret;
+
+       _enter("%s,%s", name, vllist);
+
+       cell = afs_cell_alloc(name, vllist);
+       if (IS_ERR(cell)) {
+               _leave(" = %ld", PTR_ERR(cell));
+               return cell;
+       }
+
+       down_write(&afs_cells_sem);
 
        /* add a proc directory for this cell */
        ret = afs_proc_cell_setup(cell);
        _leave(" = %p", cell);
        return cell;
 
-badaddr:
-       printk(KERN_ERR "kAFS: bad VL server IP address\n");
 error:
        up_write(&afs_cells_sem);
+       key_put(cell->anonymous_key);
        kfree(cell);
        _leave(" = %d", ret);
        return ERR_PTR(ret);
        cachefs_relinquish_cookie(cell->cache, 0);
 #endif
 
+       key_put(cell->anonymous_key);
        kfree(cell);
 
        _leave(" [destroyed]");
 
  * CB.CallBack operation type
  */
 static const struct afs_call_type afs_SRXCBCallBack = {
+       .name           = "CB.CallBack",
        .deliver        = afs_deliver_cb_callback,
        .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
  * CB.InitCallBackState operation type
  */
 static const struct afs_call_type afs_SRXCBInitCallBackState = {
+       .name           = "CB.InitCallBackState",
        .deliver        = afs_deliver_cb_init_call_back_state,
        .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
  * CB.Probe operation type
  */
 static const struct afs_call_type afs_SRXCBProbe = {
+       .name           = "CB.Probe",
        .deliver        = afs_deliver_cb_probe,
        .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
 
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
+#include <linux/ctype.h>
 #include "internal.h"
 
 static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
 
 const struct file_operations afs_dir_file_operations = {
        .open           = afs_dir_open,
+       .release        = afs_release,
        .readdir        = afs_dir_readdir,
 };
 
 const struct inode_operations afs_dir_inode_operations = {
        .lookup         = afs_dir_lookup,
+       .permission     = afs_permission,
        .getattr        = afs_inode_getattr,
 #if 0 /* TODO */
        .create         = afs_dir_create,
 /*
  * get a page into the pagecache
  */
-static struct page *afs_dir_get_page(struct inode *dir, unsigned long index)
+static struct page *afs_dir_get_page(struct inode *dir, unsigned long index,
+                                    struct key *key)
 {
        struct page *page;
+       struct file file = {
+               .private_data = key,
+       };
 
        _enter("{%lu},%lu", dir->i_ino, index);
 
-       page = read_mapping_page(dir->i_mapping, index, NULL);
+       page = read_mapping_page(dir->i_mapping, index, &file);
        if (!IS_ERR(page)) {
                wait_on_page_locked(page);
                kmap(page);
        if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
                return -ENOENT;
 
-       _leave(" = 0");
-       return 0;
+       return afs_open(inode, file);
 }
 
 /*
  * iterate through the data blob that lists the contents of an AFS directory
  */
 static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
-                          filldir_t filldir)
+                          filldir_t filldir, struct key *key)
 {
        union afs_dir_block *dblock;
        struct afs_dir_page *dbuf;
                blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1);
 
                /* fetch the appropriate page from the directory */
-               page = afs_dir_get_page(dir, blkoff / PAGE_SIZE);
+               page = afs_dir_get_page(dir, blkoff / PAGE_SIZE, key);
                if (IS_ERR(page)) {
                        ret = PTR_ERR(page);
                        break;
        _enter("{%Ld,{%lu}}",
               file->f_pos, file->f_path.dentry->d_inode->i_ino);
 
+       ASSERT(file->private_data != NULL);
+
        fpos = file->f_pos;
        ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos,
-                             cookie, filldir);
+                             cookie, filldir, file->private_data);
        file->f_pos = fpos;
 
        _leave(" = %d", ret);
  * do a lookup in a directory
  */
 static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
-                        struct afs_fid *fid)
+                        struct afs_fid *fid, struct key *key)
 {
        struct afs_dir_lookup_cookie cookie;
        struct afs_super_info *as;
        cookie.found    = 0;
 
        fpos = 0;
-       ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
+       ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir,
+                             key);
        if (ret < 0) {
                _leave(" = %d [iter]", ret);
                return ret;
        struct afs_vnode *vnode;
        struct afs_fid fid;
        struct inode *inode;
+       struct key *key;
        int ret;
 
        _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
                return ERR_PTR(-ESTALE);
        }
 
-       ret = afs_do_lookup(dir, dentry, &fid);
+       key = afs_request_key(vnode->volume->cell);
+       if (IS_ERR(key)) {
+               _leave(" = %ld [key]", PTR_ERR(key));
+               return ERR_PTR(PTR_ERR(key));
+       }
+
+       ret = afs_do_lookup(dir, dentry, &fid, key);
        if (ret < 0) {
+               key_put(key);
                _leave(" = %d [do]", ret);
                return ERR_PTR(ret);
        }
 
        /* instantiate the dentry */
-       inode = afs_iget(dir->i_sb, &fid);
+       inode = afs_iget(dir->i_sb, key, &fid);
+       key_put(key);
        if (IS_ERR(inode)) {
                _leave(" = %ld", PTR_ERR(inode));
                return ERR_PTR(PTR_ERR(inode));
        struct afs_fid fid;
        struct dentry *parent;
        struct inode *inode, *dir;
+       struct key *key;
        int ret;
 
        vnode = AFS_FS_I(dentry->d_inode);
        _enter("{sb=%p n=%s fl=%lx},",
               dentry->d_sb, dentry->d_name.name, vnode->flags);
 
+       key = afs_request_key(vnode->volume->cell);
+       if (IS_ERR(key))
+               key = NULL;
+
        /* lock down the parent dentry so we can peer at it */
        parent = dget_parent(dentry);
 
                _debug("dir modified");
 
                /* search the directory for this vnode */
-               ret = afs_do_lookup(dir, dentry, &fid);
+               ret = afs_do_lookup(dir, dentry, &fid, key);
                if (ret == -ENOENT) {
                        _debug("%s: dirent not found", dentry->d_name.name);
                        goto not_found;
            test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
                _debug("%s: changed", dentry->d_name.name);
                set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
-               if (afs_vnode_fetch_status(vnode) < 0) {
+               if (afs_vnode_fetch_status(vnode, NULL, key) < 0) {
                        mutex_unlock(&vnode->cb_broken_lock);
                        goto out_bad;
                }
 
 out_valid:
        dput(parent);
+       key_put(key);
        _leave(" = 1 [valid]");
        return 1;
 
        shrink_dcache_parent(dentry);
        d_drop(dentry);
        dput(parent);
+       key_put(key);
 
        _leave(" = 0 [bad]");
        return 0;
 
 #include <linux/pagemap.h>
 #include "internal.h"
 
-#if 0
-static int afs_file_open(struct inode *inode, struct file *file);
-static int afs_file_release(struct inode *inode, struct file *file);
-#endif
-
 static int afs_file_readpage(struct file *file, struct page *page);
 static void afs_file_invalidatepage(struct page *page, unsigned long offset);
 static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
 
+const struct file_operations afs_file_operations = {
+       .open           = afs_open,
+       .release        = afs_release,
+       .llseek         = generic_file_llseek,
+       .read           = do_sync_read,
+       .aio_read       = generic_file_aio_read,
+       .mmap           = generic_file_readonly_mmap,
+       .sendfile       = generic_file_sendfile,
+};
+
 const struct inode_operations afs_file_inode_operations = {
        .getattr        = afs_inode_getattr,
+       .permission     = afs_permission,
 };
 
 const struct address_space_operations afs_fs_aops = {
        .invalidatepage = afs_file_invalidatepage,
 };
 
+/*
+ * open an AFS file or directory and attach a key to it
+ */
+int afs_open(struct inode *inode, struct file *file)
+{
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct key *key;
+
+       _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode);
+
+       key = afs_request_key(vnode->volume->cell);
+       if (IS_ERR(key)) {
+               _leave(" = %ld [key]", PTR_ERR(key));
+               return PTR_ERR(key);
+       }
+
+       file->private_data = key;
+       _leave(" = 0");
+       return 0;
+}
+
+/*
+ * release an AFS file or directory and discard its key
+ */
+int afs_release(struct inode *inode, struct file *file)
+{
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+
+       _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode);
+
+       key_put(file->private_data);
+       _leave(" = 0");
+       return 0;
+}
+
 /*
  * deal with notification that a page was read from the cache
  */
 {
        struct afs_vnode *vnode;
        struct inode *inode;
+       struct key *key;
        size_t len;
        off_t offset;
        int ret;
 
        inode = page->mapping->host;
 
-       _enter("{%lu},{%lu}", inode->i_ino, page->index);
+       ASSERT(file != NULL);
+       key = file->private_data;
+       ASSERT(key != NULL);
+
+       _enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
 
        vnode = AFS_FS_I(inode);
 
 
                /* read the contents of the file from the server into the
                 * page */
-               ret = afs_vnode_fetch_data(vnode, offset, len, page);
+               ret = afs_vnode_fetch_data(vnode, key, offset, len, page);
                if (ret < 0) {
                        if (ret == -ENOENT) {
                                _debug("got NOENT from server"
 
  * FS.FetchStatus operation type
  */
 static const struct afs_call_type afs_RXFSFetchStatus = {
+       .name           = "FS.FetchStatus",
        .deliver        = afs_deliver_fs_fetch_status,
        .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
  * fetch the status information for a file
  */
 int afs_fs_fetch_file_status(struct afs_server *server,
+                            struct key *key,
                             struct afs_vnode *vnode,
                             struct afs_volsync *volsync,
                             const struct afs_wait_mode *wait_mode)
        struct afs_call *call;
        __be32 *bp;
 
-       _enter("");
+       _enter(",%x,,,", key_serial(key));
 
        call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, 120);
        if (!call)
                return -ENOMEM;
 
+       call->key = key;
        call->reply = vnode;
        call->reply2 = volsync;
        call->service_id = FS_SERVICE;
  * FS.FetchData operation type
  */
 static const struct afs_call_type afs_RXFSFetchData = {
+       .name           = "FS.FetchData",
        .deliver        = afs_deliver_fs_fetch_data,
        .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
  * fetch data from a file
  */
 int afs_fs_fetch_data(struct afs_server *server,
+                     struct key *key,
                      struct afs_vnode *vnode,
                      off_t offset, size_t length,
                      struct page *buffer,
        if (!call)
                return -ENOMEM;
 
+       call->key = key;
        call->reply = vnode;
        call->reply2 = volsync;
        call->reply3 = buffer;
  * FS.GiveUpCallBacks operation type
  */
 static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
+       .name           = "FS.GiveUpCallBacks",
        .deliver        = afs_deliver_fs_give_up_callbacks,
        .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 
 /*
  * map the AFS file status to the inode member variables
  */
-static int afs_inode_map_status(struct afs_vnode *vnode)
+static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 {
        struct inode *inode = AFS_VNODE_TO_I(vnode);
 
        case AFS_FTYPE_FILE:
                inode->i_mode   = S_IFREG | vnode->status.mode;
                inode->i_op     = &afs_file_inode_operations;
-               inode->i_fop    = &generic_ro_fops;
+               inode->i_fop    = &afs_file_operations;
                break;
        case AFS_FTYPE_DIR:
                inode->i_mode   = S_IFDIR | vnode->status.mode;
 
        /* check to see whether a symbolic link is really a mountpoint */
        if (vnode->status.type == AFS_FTYPE_SYMLINK) {
-               afs_mntpt_check_symlink(vnode);
+               afs_mntpt_check_symlink(vnode, key);
 
                if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) {
                        inode->i_mode   = S_IFDIR | vnode->status.mode;
 /*
  * inode retrieval
  */
-inline struct inode *afs_iget(struct super_block *sb, struct afs_fid *fid)
+inline struct inode *afs_iget(struct super_block *sb, struct key *key,
+                             struct afs_fid *fid)
 {
        struct afs_iget_data data = { .fid = *fid };
        struct afs_super_info *as;
 
        /* okay... it's a new inode */
        set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
-       ret = afs_vnode_fetch_status(vnode);
+       ret = afs_vnode_fetch_status(vnode, NULL, key);
        if (ret < 0)
                goto bad_inode;
-       ret = afs_inode_map_status(vnode);
+       ret = afs_inode_map_status(vnode, key);
        if (ret < 0)
                goto bad_inode;
 
  */
 void afs_clear_inode(struct inode *inode)
 {
+       struct afs_permits *permits;
        struct afs_vnode *vnode;
 
        vnode = AFS_FS_I(inode);
        vnode->cache = NULL;
 #endif
 
+       mutex_lock(&vnode->permits_lock);
+       permits = vnode->permits;
+       rcu_assign_pointer(vnode->permits, NULL);
+       mutex_unlock(&vnode->permits_lock);
+       if (permits)
+               call_rcu(&permits->rcu, afs_zap_permits);
+
        _leave("");
 }
 
 #include <linux/pagemap.h>
 #include <linux/skbuff.h>
 #include <linux/rxrpc.h>
+#include <linux/key.h>
 #include "afs.h"
 #include "afs_vl.h"
 
        AFS_VL_UNCERTAIN,               /* uncertain state (update failed) */
 } __attribute__((packed)) afs_vlocation_state_t;
 
+struct afs_mount_params {
+       bool                    rwpath;         /* T if the parent should be considered R/W */
+       bool                    force;          /* T to force cell type */
+       afs_voltype_t           type;           /* type of volume requested */
+       int                     volnamesz;      /* size of volume name */
+       const char              *volname;       /* name of volume to mount */
+       struct afs_cell         *cell;          /* cell in which to find volume */
+       struct afs_volume       *volume;        /* volume record */
+       struct key              *key;           /* key to use for secure mounting */
+};
+
 /*
  * definition of how to wait for the completion of an operation
  */
 };
 
 struct afs_call_type {
+       const char *name;
+
        /* deliver request or reply data to an call
         * - returning an error will cause the call to be aborted
         */
  * entry in the cached cell catalogue
  */
 struct afs_cache_cell {
-       char                    name[64];       /* cell name (padded with NULs) */
-       struct in_addr          vl_servers[15]; /* cached cell VL servers */
+       char            name[AFS_MAXCELLNAME];  /* cell name (padded with NULs) */
+       struct in_addr  vl_servers[15];         /* cached cell VL servers */
 };
 
 /*
 struct afs_cell {
        atomic_t                usage;
        struct list_head        link;           /* main cell list link */
+       struct key              *anonymous_key; /* anonymous user key for this cell */
        struct list_head        proc_link;      /* /proc cell list link */
        struct proc_dir_entry   *proc_dir;      /* /proc dir for this cell */
 #ifdef AFS_CACHING_SUPPORT
  * entry in the cached volume location catalogue
  */
 struct afs_cache_vlocation {
-       uint8_t                 name[64 + 1];   /* volume name (lowercase, padded with NULs) */
+       /* volume name (lowercase, padded with NULs) */
+       uint8_t                 name[AFS_MAXVOLNAME + 1];
+
        uint8_t                 nservers;       /* number of entries used in servers[] */
        uint8_t                 vidmask;        /* voltype mask for vid[] */
        uint8_t                 srvtmask[8];    /* voltype masks for servers[] */
 #ifdef AFS_CACHING_SUPPORT
        struct cachefs_cookie   *cache;         /* caching cookie */
 #endif
-
+       struct afs_permits      *permits;       /* cache of permits so far obtained */
+       struct mutex            permits_lock;   /* lock for altering permits list */
        wait_queue_head_t       update_waitq;   /* status fetch waitqueue */
        unsigned                update_cnt;     /* number of outstanding ops that will update the
                                                 * status */
 #define AFS_VNODE_DIR_CHANGED  6               /* set if vnode's parent dir metadata changed */
 #define AFS_VNODE_DIR_MODIFIED 7               /* set if vnode's parent dir data modified */
 
+       long                    acl_order;      /* ACL check count (callback break count) */
+
        /* outstanding callback notification on this file */
        struct rb_node          server_rb;      /* link in server->fs_vnodes */
        struct rb_node          cb_promise;     /* link in server->cb_promises */
        struct work_struct      cb_broken_work; /* work to be done on callback break */
        struct mutex            cb_broken_lock; /* lock against multiple attempts to fix break */
-//     struct list_head        cb_hash_link;   /* link in master callback hash */
        time_t                  cb_expires;     /* time at which callback expires */
        time_t                  cb_expires_at;  /* time used to order cb_promise */
        unsigned                cb_version;     /* callback version */
        bool                    cb_promised;    /* true if promise still holds */
 };
 
+/*
+ * cached security record for one user's attempt to access a vnode
+ */
+struct afs_permit {
+       struct key              *key;           /* RxRPC ticket holding a security context */
+       afs_access_t            access_mask;    /* access mask for this key */
+};
+
+/*
+ * cache of security records from attempts to access a vnode
+ */
+struct afs_permits {
+       struct rcu_head         rcu;            /* disposal procedure */
+       int                     count;          /* number of records */
+       struct afs_permit       permits[0];     /* the permits so far examined */
+};
+
 /*****************************************************************************/
 /*
  * callback.c
 extern const struct inode_operations afs_dir_inode_operations;
 extern const struct file_operations afs_dir_file_operations;
 
+extern int afs_permission(struct inode *, int, struct nameidata *);
+
 /*
  * file.c
  */
 extern const struct address_space_operations afs_fs_aops;
 extern const struct inode_operations afs_file_inode_operations;
+extern const struct file_operations afs_file_operations;
+
+extern int afs_open(struct inode *, struct file *);
+extern int afs_release(struct inode *, struct file *);
 
 #ifdef AFS_CACHING_SUPPORT
 extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **);
 /*
  * fsclient.c
  */
-extern int afs_fs_fetch_file_status(struct afs_server *,
-                                   struct afs_vnode *,
-                                   struct afs_volsync *,
+extern int afs_fs_fetch_file_status(struct afs_server *, struct key *,
+                                   struct afs_vnode *, struct afs_volsync *,
                                    const struct afs_wait_mode *);
 extern int afs_fs_give_up_callbacks(struct afs_server *,
                                    const struct afs_wait_mode *);
-extern int afs_fs_fetch_data(struct afs_server *, struct afs_vnode *, off_t,
-                            size_t, struct page *, struct afs_volsync *,
+extern int afs_fs_fetch_data(struct afs_server *, struct key *,
+                            struct afs_vnode *, off_t, size_t, struct page *,
+                            struct afs_volsync *,
                             const struct afs_wait_mode *);
 
 /*
  * inode.c
  */
-extern struct inode *afs_iget(struct super_block *, struct afs_fid *);
+extern struct inode *afs_iget(struct super_block *, struct key *,
+                             struct afs_fid *);
 extern int afs_inode_getattr(struct vfsmount *, struct dentry *,
                             struct kstat *);
+extern void afs_zap_permits(struct rcu_head *);
 extern void afs_clear_inode(struct inode *);
 
 /*
 extern const struct file_operations afs_mntpt_file_operations;
 extern unsigned long afs_mntpt_expiry_timeout;
 
-extern int afs_mntpt_check_symlink(struct afs_vnode *);
+extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
 extern void afs_mntpt_kill_timer(void);
 extern void afs_umount_begin(struct vfsmount *, int);
 
-/*
- * super.c
- */
-extern int afs_fs_init(void);
-extern void afs_fs_exit(void);
-
 /*
  * proc.c
  */
 extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *,
                            size_t);
 
+/*
+ * security.c
+ */
+extern void afs_clear_permits(struct afs_vnode *);
+extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
+extern struct key *afs_request_key(struct afs_cell *);
+extern int afs_permission(struct inode *, int, struct nameidata *);
+
 /*
  * server.c
  */
 extern void afs_put_server(struct afs_server *);
 extern void __exit afs_purge_servers(void);
 
+/*
+ * super.c
+ */
+extern int afs_fs_init(void);
+extern void afs_fs_exit(void);
+
 /*
  * vlclient.c
  */
 extern struct cachefs_index_def afs_vlocation_cache_index_def;
 #endif
 
-extern int afs_vl_get_entry_by_name(struct in_addr *, const char *,
-                                   struct afs_cache_vlocation *,
+extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *,
+                                   const char *, struct afs_cache_vlocation *,
                                    const struct afs_wait_mode *);
-extern int afs_vl_get_entry_by_id(struct in_addr *, afs_volid_t, afs_voltype_t,
+extern int afs_vl_get_entry_by_id(struct in_addr *, struct key *,
+                                 afs_volid_t, afs_voltype_t,
                                  struct afs_cache_vlocation *,
                                  const struct afs_wait_mode *);
 
 
 extern int __init afs_vlocation_update_init(void);
 extern struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *,
+                                                 struct key *,
                                                  const char *, size_t);
 extern void afs_put_vlocation(struct afs_vlocation *);
 extern void __exit afs_vlocation_purge(void);
        return &vnode->vfs_inode;
 }
 
-extern int afs_vnode_fetch_status(struct afs_vnode *);
-extern int afs_vnode_fetch_data(struct afs_vnode *vnode, off_t, size_t,
-                               struct page *);
+extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *,
+                                 struct key *);
+extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *,
+                               off_t, size_t, struct page *);
 
 /*
  * volume.c
 #define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
 
 extern void afs_put_volume(struct afs_volume *);
-extern struct afs_volume *afs_volume_lookup(const char *, struct afs_cell *,
-                                           int);
+extern struct afs_volume *afs_volume_lookup(struct afs_mount_params *);
 extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *);
 extern int afs_volume_release_fileserver(struct afs_vnode *,
                                         struct afs_server *, int);
 
  * check a symbolic link to see whether it actually encodes a mountpoint
  * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
  */
-int afs_mntpt_check_symlink(struct afs_vnode *vnode)
+int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
 {
+       struct file file = {
+               .private_data = key,
+       };
        struct page *page;
        size_t size;
        char *buf;
        _enter("{%u,%u}", vnode->fid.vnode, vnode->fid.unique);
 
        /* read the contents of the symlink into the pagecache */
-       page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, NULL);
+       page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, &file);
        if (IS_ERR(page)) {
                ret = PTR_ERR(page);
                goto out;
        struct vfsmount *newmnt;
        int err;
 
-       _enter("%p{%s},{%s:%p{%s}}",
+       _enter("%p{%s},{%s:%p{%s},}",
               dentry,
               dentry->d_name.name,
               nd->mnt->mnt_devname,
        err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts);
        switch (err) {
        case 0:
-               path_release(nd);
+               mntput(nd->mnt);
+               dput(nd->dentry);
                nd->mnt = newmnt;
                nd->dentry = dget(newmnt->mnt_root);
                schedule_delayed_work(&afs_mntpt_expiry_timer,
 
 
 static struct socket *afs_socket; /* my RxRPC socket */
 static struct workqueue_struct *afs_async_calls;
+static atomic_t afs_outstanding_calls;
+static atomic_t afs_outstanding_skbs;
 
 static void afs_wake_up_call_waiter(struct afs_call *);
 static int afs_wait_for_call_to_complete(struct afs_call *);
 
 /* asynchronous incoming call initial processing */
 static const struct afs_call_type afs_RXCMxxxx = {
+       .name           = "CB.xxxx",
        .deliver        = afs_deliver_cm_op_id,
        .abort_to_error = afs_abort_to_error,
 };
 
        _debug("dework");
        destroy_workqueue(afs_async_calls);
+
+       ASSERTCMP(atomic_read(&afs_outstanding_skbs), ==, 0);
+       ASSERTCMP(atomic_read(&afs_outstanding_calls), ==, 0);
        _leave("");
 }
 
+/*
+ * note that the data in a socket buffer is now delivered and that the buffer
+ * should be freed
+ */
+static void afs_data_delivered(struct sk_buff *skb)
+{
+       if (!skb) {
+               _debug("DLVR NULL [%d]", atomic_read(&afs_outstanding_skbs));
+               dump_stack();
+       } else {
+               _debug("DLVR %p{%u} [%d]",
+                      skb, skb->mark, atomic_read(&afs_outstanding_skbs));
+               if (atomic_dec_return(&afs_outstanding_skbs) == -1)
+                       BUG();
+               rxrpc_kernel_data_delivered(skb);
+       }
+}
+
+/*
+ * free a socket buffer
+ */
+static void afs_free_skb(struct sk_buff *skb)
+{
+       if (!skb) {
+               _debug("FREE NULL [%d]", atomic_read(&afs_outstanding_skbs));
+               dump_stack();
+       } else {
+               _debug("FREE %p{%u} [%d]",
+                      skb, skb->mark, atomic_read(&afs_outstanding_skbs));
+               if (atomic_dec_return(&afs_outstanding_skbs) == -1)
+                       BUG();
+               rxrpc_kernel_free_skb(skb);
+       }
+}
+
+/*
+ * free a call
+ */
+static void afs_free_call(struct afs_call *call)
+{
+       _debug("DONE %p{%s} [%d]",
+              call, call->type->name, atomic_read(&afs_outstanding_calls));
+       if (atomic_dec_return(&afs_outstanding_calls) == -1)
+               BUG();
+
+       ASSERTCMP(call->rxcall, ==, NULL);
+       ASSERT(!work_pending(&call->async_work));
+       ASSERT(skb_queue_empty(&call->rx_queue));
+       ASSERT(call->type->name != NULL);
+
+       kfree(call->request);
+       kfree(call);
+}
+
 /*
  * allocate a call with flat request and reply buffers
  */
        if (!call)
                goto nomem_call;
 
+       _debug("CALL %p{%s} [%d]",
+              call, type->name, atomic_read(&afs_outstanding_calls));
+       atomic_inc(&afs_outstanding_calls);
+
+       call->type = type;
+       call->request_size = request_size;
+       call->reply_max = reply_size;
+
        if (request_size) {
                call->request = kmalloc(request_size, GFP_NOFS);
                if (!call->request)
-                       goto nomem_request;
+                       goto nomem_free;
        }
 
        if (reply_size) {
                call->buffer = kmalloc(reply_size, GFP_NOFS);
                if (!call->buffer)
-                       goto nomem_buffer;
+                       goto nomem_free;
        }
 
-       call->type = type;
-       call->request_size = request_size;
-       call->reply_max = reply_size;
-
        init_waitqueue_head(&call->waitq);
        skb_queue_head_init(&call->rx_queue);
        return call;
 
-nomem_buffer:
-       kfree(call->request);
-nomem_request:
-       kfree(call);
+nomem_free:
+       afs_free_call(call);
 nomem_call:
        return NULL;
 }
 
        _enter("%x,{%d},", addr->s_addr, ntohs(call->port));
 
+       ASSERT(call->type != NULL);
+       ASSERT(call->type->name != NULL);
+
+       _debug("MAKE %p{%s} [%d]",
+              call, call->type->name, atomic_read(&afs_outstanding_calls));
+
        call->wait_mode = wait_mode;
        INIT_WORK(&call->async_work, afs_process_async_call);
 
        /* create a call */
        rxcall = rxrpc_kernel_begin_call(afs_socket, &srx, call->key,
                                         (unsigned long) call, gfp);
+       call->key = NULL;
        if (IS_ERR(rxcall)) {
                ret = PTR_ERR(rxcall);
                goto error_kill_call;
 error_do_abort:
        rxrpc_kernel_abort_call(rxcall, RX_USER_ABORT);
        rxrpc_kernel_end_call(rxcall);
+       call->rxcall = NULL;
 error_kill_call:
        call->type->destructor(call);
-       ASSERT(skb_queue_empty(&call->rx_queue));
-       kfree(call);
+       afs_free_call(call);
        _leave(" = %d", ret);
        return ret;
 }
 
        _enter("%p,,%u", call, skb->mark);
 
+       _debug("ICPT %p{%u} [%d]",
+              skb, skb->mark, atomic_read(&afs_outstanding_skbs));
+
        ASSERTCMP(sk, ==, afs_socket->sk);
+       atomic_inc(&afs_outstanding_skbs);
 
        if (!call) {
                /* its an incoming call for our callback service */
-               __skb_queue_tail(&afs_incoming_calls, skb);
+               skb_queue_tail(&afs_incoming_calls, skb);
                schedule_work(&afs_collect_incoming_call_work);
        } else {
                /* route the messages directly to the appropriate call */
-               __skb_queue_tail(&call->rx_queue, skb);
+               skb_queue_tail(&call->rx_queue, skb);
                call->wait_mode->rx_wakeup(call);
        }
 
                                call->state = AFS_CALL_ERROR;
                                break;
                        }
-                       rxrpc_kernel_data_delivered(skb);
+                       afs_data_delivered(skb);
                        skb = NULL;
-                       break;
+                       continue;
                case RXRPC_SKB_MARK_FINAL_ACK:
                        _debug("Rcv ACK");
                        call->state = AFS_CALL_COMPLETE;
                        break;
                }
 
-               rxrpc_kernel_free_skb(skb);
+               afs_free_skb(skb);
        }
 
        /* make sure the queue is empty if the call is done with (we might have
         * aborted the call early because of an unmarshalling error) */
        if (call->state >= AFS_CALL_COMPLETE) {
                while ((skb = skb_dequeue(&call->rx_queue)))
-                       rxrpc_kernel_free_skb(skb);
+                       afs_free_skb(skb);
                if (call->incoming) {
                        rxrpc_kernel_end_call(call->rxcall);
+                       call->rxcall = NULL;
                        call->type->destructor(call);
-                       ASSERT(skb_queue_empty(&call->rx_queue));
-                       kfree(call);
+                       afs_free_call(call);
                }
        }
 
                _debug("call incomplete");
                rxrpc_kernel_abort_call(call->rxcall, RX_CALL_DEAD);
                while ((skb = skb_dequeue(&call->rx_queue)))
-                       rxrpc_kernel_free_skb(skb);
+                       afs_free_skb(skb);
        }
 
        _debug("call complete");
        rxrpc_kernel_end_call(call->rxcall);
+       call->rxcall = NULL;
        call->type->destructor(call);
-       ASSERT(skb_queue_empty(&call->rx_queue));
-       kfree(call);
+       afs_free_call(call);
        _leave(" = %d", ret);
        return ret;
 }
 
        _enter("");
 
-       ASSERT(skb_queue_empty(&call->rx_queue));
-       ASSERT(!work_pending(&call->async_work));
-       kfree(call);
+       afs_free_call(call);
 
        _leave("");
 }
 
                /* kill the call */
                rxrpc_kernel_end_call(call->rxcall);
+               call->rxcall = NULL;
                if (call->type->destructor)
                        call->type->destructor(call);
 
                _debug("new call");
 
                /* don't need the notification */
-               rxrpc_kernel_free_skb(skb);
+               afs_free_skb(skb);
 
                if (!call) {
                        call = kzalloc(sizeof(struct afs_call), GFP_KERNEL);
                        init_waitqueue_head(&call->waitq);
                        skb_queue_head_init(&call->rx_queue);
                        call->state = AFS_CALL_AWAIT_OP_ID;
+
+                       _debug("CALL %p{%s} [%d]",
+                              call, call->type->name,
+                              atomic_read(&afs_outstanding_calls));
+                       atomic_inc(&afs_outstanding_calls);
                }
 
                rxcall = rxrpc_kernel_accept_call(afs_socket,
                }
        }
 
-       kfree(call);
+       if (call)
+               afs_free_call(call);
 }
 
 /*
                rxrpc_kernel_end_call(call->rxcall);
                call->rxcall = NULL;
                call->type->destructor(call);
-               ASSERT(skb_queue_empty(&call->rx_queue));
-               kfree(call);
+               afs_free_call(call);
                _leave(" [error]");
                return;
        }
 
--- /dev/null
+/* AFS security handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+#include <keys/rxrpc-type.h>
+#include "internal.h"
+
+/*
+ * get a key
+ */
+struct key *afs_request_key(struct afs_cell *cell)
+{
+       struct key *key;
+
+       _enter("{%x}", key_serial(cell->anonymous_key));
+
+       _debug("key %s", cell->anonymous_key->description);
+       key = request_key(&key_type_rxrpc, cell->anonymous_key->description,
+                         NULL);
+       if (IS_ERR(key)) {
+               if (PTR_ERR(key) != -ENOKEY) {
+                       _leave(" = %ld", PTR_ERR(key));
+                       return key;
+               }
+
+               /* act as anonymous user */
+               _leave(" = {%x} [anon]", key_serial(cell->anonymous_key));
+               return key_get(cell->anonymous_key);
+       } else {
+               /* act as authorised user */
+               _leave(" = {%x} [auth]", key_serial(key));
+               return key;
+       }
+}
+
+/*
+ * dispose of a permits list
+ */
+void afs_zap_permits(struct rcu_head *rcu)
+{
+       struct afs_permits *permits =
+               container_of(rcu, struct afs_permits, rcu);
+       int loop;
+
+       _enter("{%d}", permits->count);
+
+       for (loop = permits->count - 1; loop >= 0; loop--)
+               key_put(permits->permits[loop].key);
+       kfree(permits);
+}
+
+/*
+ * dispose of a permits list in which all the key pointers have been copied
+ */
+static void afs_dispose_of_permits(struct rcu_head *rcu)
+{
+       struct afs_permits *permits =
+               container_of(rcu, struct afs_permits, rcu);
+
+       _enter("{%d}", permits->count);
+
+       kfree(permits);
+}
+
+/*
+ * get the authorising vnode - this is the specified inode itself if it's a
+ * directory or it's the parent directory if the specified inode is a file or
+ * symlink
+ * - the caller must release the ref on the inode
+ */
+static struct afs_vnode *afs_get_auth_inode(struct afs_vnode *vnode,
+                                           struct key *key)
+{
+       struct afs_vnode *auth_vnode;
+       struct inode *auth_inode;
+
+       _enter("");
+
+       if (S_ISDIR(vnode->vfs_inode.i_mode)) {
+               auth_inode = igrab(&vnode->vfs_inode);
+               ASSERT(auth_inode != NULL);
+       } else {
+               auth_inode = afs_iget(vnode->vfs_inode.i_sb, key,
+                                     &vnode->status.parent);
+               if (IS_ERR(auth_inode))
+                       return ERR_PTR(PTR_ERR(auth_inode));
+       }
+
+       auth_vnode = AFS_FS_I(auth_inode);
+       _leave(" = {%x}", auth_vnode->fid.vnode);
+       return auth_vnode;
+}
+
+/*
+ * clear the permit cache on a directory vnode
+ */
+void afs_clear_permits(struct afs_vnode *vnode)
+{
+       struct afs_permits *permits;
+
+       _enter("{%x}", vnode->fid.vnode);
+
+       mutex_lock(&vnode->permits_lock);
+       permits = vnode->permits;
+       rcu_assign_pointer(vnode->permits, NULL);
+       mutex_unlock(&vnode->permits_lock);
+
+       if (permits)
+               call_rcu(&permits->rcu, afs_zap_permits);
+       _leave("");
+}
+
+/*
+ * add the result obtained for a vnode to its or its parent directory's cache
+ * for the key used to access it
+ */
+void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order)
+{
+       struct afs_permits *permits, *xpermits;
+       struct afs_permit *permit;
+       struct afs_vnode *auth_vnode;
+       int count, loop;
+
+       _enter("{%x},%x,%lx", vnode->fid.vnode, key_serial(key), acl_order);
+
+       auth_vnode = afs_get_auth_inode(vnode, key);
+       if (IS_ERR(auth_vnode)) {
+               _leave(" [get error %ld]", PTR_ERR(auth_vnode));
+               return;
+       }
+
+       mutex_lock(&auth_vnode->permits_lock);
+
+       /* guard against a rename being detected whilst we waited for the
+        * lock */
+       if (memcmp(&auth_vnode->fid, &vnode->status.parent,
+                  sizeof(struct afs_fid)) != 0) {
+               _debug("renamed");
+               goto out_unlock;
+       }
+
+       /* have to be careful as the directory's callback may be broken between
+        * us receiving the status we're trying to cache and us getting the
+        * lock to update the cache for the status */
+       if (auth_vnode->acl_order - acl_order > 0) {
+               _debug("ACL changed?");
+               goto out_unlock;
+       }
+
+       /* always update the anonymous mask */
+       _debug("anon access %x", vnode->status.anon_access);
+       auth_vnode->status.anon_access = vnode->status.anon_access;
+       if (key == vnode->volume->cell->anonymous_key)
+               goto out_unlock;
+
+       xpermits = auth_vnode->permits;
+       count = 0;
+       if (xpermits) {
+               /* see if the permit is already in the list
+                * - if it is then we just amend the list
+                */
+               count = xpermits->count;
+               permit = xpermits->permits;
+               for (loop = count; loop > 0; loop--) {
+                       if (permit->key == key) {
+                               permit->access_mask =
+                                       vnode->status.caller_access;
+                               goto out_unlock;
+                       }
+                       permit++;
+               }
+       }
+
+       permits = kmalloc(sizeof(*permits) + sizeof(*permit) * (count + 1),
+                         GFP_NOFS);
+       if (!permits)
+               goto out_unlock;
+
+       memcpy(permits->permits, xpermits->permits,
+              count * sizeof(struct afs_permit));
+
+       _debug("key %x access %x",
+              key_serial(key), vnode->status.caller_access);
+       permits->permits[count].access_mask = vnode->status.caller_access;
+       permits->permits[count].key = key_get(key);
+       permits->count = count + 1;
+
+       rcu_assign_pointer(auth_vnode->permits, permits);
+       if (xpermits)
+               call_rcu(&xpermits->rcu, afs_dispose_of_permits);
+
+out_unlock:
+       mutex_unlock(&auth_vnode->permits_lock);
+       iput(&auth_vnode->vfs_inode);
+       _leave("");
+}
+
+/*
+ * check with the fileserver to see if the directory or parent directory is
+ * permitted to be accessed with this authorisation, and if so, what access it
+ * is granted
+ */
+static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
+                           afs_access_t *_access)
+{
+       struct afs_permits *permits;
+       struct afs_permit *permit;
+       struct afs_vnode *auth_vnode;
+       bool valid;
+       int loop, ret;
+
+       _enter("");
+
+       auth_vnode = afs_get_auth_inode(vnode, key);
+       if (IS_ERR(auth_vnode)) {
+               *_access = 0;
+               _leave(" = %ld", PTR_ERR(auth_vnode));
+               return PTR_ERR(auth_vnode);
+       }
+
+       ASSERT(S_ISDIR(auth_vnode->vfs_inode.i_mode));
+
+       /* check the permits to see if we've got one yet */
+       if (key == auth_vnode->volume->cell->anonymous_key) {
+               _debug("anon");
+               *_access = auth_vnode->status.anon_access;
+               valid = true;
+       } else {
+               valid = false;
+               rcu_read_lock();
+               permits = rcu_dereference(auth_vnode->permits);
+               if (permits) {
+                       permit = permits->permits;
+                       for (loop = permits->count; loop > 0; loop--) {
+                               if (permit->key == key) {
+                                       _debug("found in cache");
+                                       *_access = permit->access_mask;
+                                       valid = true;
+                                       break;
+                               }
+                               permit++;
+                       }
+               }
+               rcu_read_unlock();
+       }
+
+       if (!valid) {
+               /* check the status on the file we're actually interested in
+                * (the post-processing will cache the result on auth_vnode) */
+               _debug("no valid permit");
+
+               set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+               ret = afs_vnode_fetch_status(vnode, auth_vnode, key);
+               if (ret < 0) {
+                       iput(&auth_vnode->vfs_inode);
+                       *_access = 0;
+                       _leave(" = %d", ret);
+                       return ret;
+               }
+       }
+
+       *_access = vnode->status.caller_access;
+       iput(&auth_vnode->vfs_inode);
+       _leave(" = 0 [access %x]", *_access);
+       return 0;
+}
+
+/*
+ * check the permissions on an AFS file
+ * - AFS ACLs are attached to directories only, and a file is controlled by its
+ *   parent directory's ACL
+ */
+int afs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       afs_access_t access;
+       struct key *key;
+       int ret;
+
+       _enter("{%x:%x},%x,", vnode->fid.vid, vnode->fid.vnode, mask);
+
+       key = afs_request_key(vnode->volume->cell);
+       if (IS_ERR(key)) {
+               _leave(" = %ld [key]", PTR_ERR(key));
+               return PTR_ERR(key);
+       }
+
+       /* check the permits to see if we've got one yet */
+       ret = afs_check_permit(vnode, key, &access);
+       if (ret < 0) {
+               key_put(key);
+               _leave(" = %d [check]", ret);
+               return ret;
+       }
+
+       /* interpret the access mask */
+       _debug("REQ %x ACC %x on %s",
+              mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
+
+       if (S_ISDIR(inode->i_mode)) {
+               if (mask & MAY_EXEC) {
+                       if (!(access & AFS_ACE_LOOKUP))
+                               goto permission_denied;
+               } else if (mask & MAY_READ) {
+                       if (!(access & AFS_ACE_READ))
+                               goto permission_denied;
+               } else if (mask & MAY_WRITE) {
+                       if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
+                                       AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */
+                                       AFS_ACE_WRITE))) /* chmod */
+                               goto permission_denied;
+               } else {
+                       BUG();
+               }
+       } else {
+               if (!(access & AFS_ACE_LOOKUP))
+                       goto permission_denied;
+               if (mask & (MAY_EXEC | MAY_READ)) {
+                       if (!(access & AFS_ACE_READ))
+                               goto permission_denied;
+               } else if (mask & MAY_WRITE) {
+                       if (!(access & AFS_ACE_WRITE))
+                               goto permission_denied;
+               }
+       }
+
+       key_put(key);
+       return generic_permission(inode, mask, NULL);
+
+permission_denied:
+       key_put(key);
+       _leave(" = -EACCES");
+       return -EACCES;
+}
 
 
 #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */
 
-struct afs_mount_params {
-       int                     rwpath;
-       struct afs_cell         *default_cell;
-       struct afs_volume       *volume;
-};
-
 static void afs_i_init_once(void *foo, struct kmem_cache *cachep,
                            unsigned long flags);
 
  * - this function has been shamelessly adapted from the ext3 fs which
  *   shamelessly adapted it from the msdos fs
  */
-static int afs_super_parse_options(struct afs_mount_params *params,
-                                  char *options, const char **devname)
+static int afs_parse_options(struct afs_mount_params *params,
+                            char *options, const char **devname)
 {
        struct afs_cell *cell;
        char *key, *value;
                        cell = afs_cell_lookup(value, strlen(value));
                        if (IS_ERR(cell))
                                return PTR_ERR(cell);
-                       afs_put_cell(params->default_cell);
-                       params->default_cell = cell;
+                       afs_put_cell(params->cell);
+                       params->cell = cell;
                } else {
                        printk("kAFS: Unknown mount option: '%s'\n",  key);
                        ret = -EINVAL;
        return ret;
 }
 
+/*
+ * parse a device name to get cell name, volume name, volume type and R/W
+ * selector
+ * - this can be one of the following:
+ *     "%[cell:]volume[.]"             R/W volume
+ *     "#[cell:]volume[.]"             R/O or R/W volume (rwpath=0),
+ *                                      or R/W (rwpath=1) volume
+ *     "%[cell:]volume.readonly"       R/O volume
+ *     "#[cell:]volume.readonly"       R/O volume
+ *     "%[cell:]volume.backup"         Backup volume
+ *     "#[cell:]volume.backup"         Backup volume
+ */
+static int afs_parse_device_name(struct afs_mount_params *params,
+                                const char *name)
+{
+       struct afs_cell *cell;
+       const char *cellname, *suffix;
+       int cellnamesz;
+
+       _enter(",%s", name);
+
+       if (!name) {
+               printk(KERN_ERR "kAFS: no volume name specified\n");
+               return -EINVAL;
+       }
+
+       if ((name[0] != '%' && name[0] != '#') || !name[1]) {
+               printk(KERN_ERR "kAFS: unparsable volume name\n");
+               return -EINVAL;
+       }
+
+       /* determine the type of volume we're looking for */
+       params->type = AFSVL_ROVOL;
+       params->force = false;
+       if (params->rwpath || name[0] == '%') {
+               params->type = AFSVL_RWVOL;
+               params->force = true;
+       }
+       name++;
+
+       /* split the cell name out if there is one */
+       params->volname = strchr(name, ':');
+       if (params->volname) {
+               cellname = name;
+               cellnamesz = params->volname - name;
+               params->volname++;
+       } else {
+               params->volname = name;
+               cellname = NULL;
+               cellnamesz = 0;
+       }
+
+       /* the volume type is further affected by a possible suffix */
+       suffix = strrchr(params->volname, '.');
+       if (suffix) {
+               if (strcmp(suffix, ".readonly") == 0) {
+                       params->type = AFSVL_ROVOL;
+                       params->force = true;
+               } else if (strcmp(suffix, ".backup") == 0) {
+                       params->type = AFSVL_BACKVOL;
+                       params->force = true;
+               } else if (suffix[1] == 0) {
+               } else {
+                       suffix = NULL;
+               }
+       }
+
+       params->volnamesz = suffix ?
+               suffix - params->volname : strlen(params->volname);
+
+       _debug("cell %*.*s [%p]",
+              cellnamesz, cellnamesz, cellname ?: "", params->cell);
+
+       /* lookup the cell record */
+       if (cellname || !params->cell) {
+               cell = afs_cell_lookup(cellname, cellnamesz);
+               if (IS_ERR(cell)) {
+                       printk(KERN_ERR "kAFS: unable to lookup cell '%s'\n",
+                              cellname ?: "");
+                       return PTR_ERR(cell);
+               }
+               afs_put_cell(params->cell);
+               params->cell = cell;
+       }
+
+       _debug("CELL:%s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s",
+              params->cell->name, params->cell,
+              params->volnamesz, params->volnamesz, params->volname,
+              suffix ?: "-", params->type, params->force ? " FORCE" : "");
+
+       return 0;
+}
+
 /*
  * check a superblock to see if it's the one we're looking for
  */
        fid.vid         = as->volume->vid;
        fid.vnode       = 1;
        fid.unique      = 1;
-       inode = afs_iget(sb, &fid);
+       inode = afs_iget(sb, params->key, &fid);
        if (IS_ERR(inode))
                goto error_inode;
 
        struct afs_mount_params params;
        struct super_block *sb;
        struct afs_volume *vol;
+       struct key *key;
        int ret;
 
        _enter(",,%s,%p", dev_name, options);
 
        memset(¶ms, 0, sizeof(params));
 
-       /* parse the options */
+       /* parse the options and device name */
        if (options) {
-               ret = afs_super_parse_options(¶ms, options, &dev_name);
+               ret = afs_parse_options(¶ms, options, &dev_name);
                if (ret < 0)
                        goto error;
-               if (!dev_name) {
-                       printk("kAFS: no volume name specified\n");
-                       ret = -EINVAL;
-                       goto error;
-               }
        }
 
+
+       ret = afs_parse_device_name(¶ms, dev_name);
+       if (ret < 0)
+               goto error;
+
+       /* try and do the mount securely */
+       key = afs_request_key(params.cell);
+       if (IS_ERR(key)) {
+               _leave(" = %ld [key]", PTR_ERR(key));
+               ret = PTR_ERR(key);
+               goto error;
+       }
+       params.key = key;
+
        /* parse the device name */
-       vol = afs_volume_lookup(dev_name, params.default_cell, params.rwpath);
+       vol = afs_volume_lookup(¶ms);
        if (IS_ERR(vol)) {
                ret = PTR_ERR(vol);
                goto error;
        }
-
        params.volume = vol;
 
        /* allocate a deviceless superblock */
 
        simple_set_mnt(mnt, sb);
        afs_put_volume(params.volume);
-       afs_put_cell(params.default_cell);
+       afs_put_cell(params.cell);
        _leave(" = 0 [%p]", sb);
        return 0;
 
 error:
        afs_put_volume(params.volume);
-       afs_put_cell(params.default_cell);
+       afs_put_cell(params.cell);
+       key_put(params.key);
        _leave(" = %d", ret);
        return ret;
 }
                memset(vnode, 0, sizeof(*vnode));
                inode_init_once(&vnode->vfs_inode);
                init_waitqueue_head(&vnode->update_waitq);
+               mutex_init(&vnode->permits_lock);
                spin_lock_init(&vnode->lock);
                INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work);
                mutex_init(&vnode->cb_broken_lock);
 
  * VL.GetEntryByName operation type
  */
 static const struct afs_call_type afs_RXVLGetEntryByName = {
+       .name           = "VL.GetEntryByName",
        .deliver        = afs_deliver_vl_get_entry_by_xxx,
        .abort_to_error = afs_vl_abort_to_error,
        .destructor     = afs_flat_call_destructor,
  * VL.GetEntryById operation type
  */
 static const struct afs_call_type afs_RXVLGetEntryById = {
+       .name           = "VL.GetEntryById",
        .deliver        = afs_deliver_vl_get_entry_by_xxx,
        .abort_to_error = afs_vl_abort_to_error,
        .destructor     = afs_flat_call_destructor,
  * dispatch a get volume entry by name operation
  */
 int afs_vl_get_entry_by_name(struct in_addr *addr,
+                            struct key *key,
                             const char *volname,
                             struct afs_cache_vlocation *entry,
                             const struct afs_wait_mode *wait_mode)
        if (!call)
                return -ENOMEM;
 
+       call->key = key;
        call->reply = entry;
        call->service_id = VL_SERVICE;
        call->port = htons(AFS_VL_PORT);
  * dispatch a get volume entry by ID operation
  */
 int afs_vl_get_entry_by_id(struct in_addr *addr,
+                          struct key *key,
                           afs_volid_t volid,
                           afs_voltype_t voltype,
                           struct afs_cache_vlocation *entry,
        if (!call)
                return -ENOMEM;
 
+       call->key = key;
        call->reply = entry;
        call->service_id = VL_SERVICE;
        call->port = htons(AFS_VL_PORT);
 
  * about the volume in question
  */
 static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl,
+                                          struct key *key,
                                           struct afs_cache_vlocation *vldb)
 {
        struct afs_cell *cell = vl->cell;
                _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr);
 
                /* attempt to access the VL server */
-               ret = afs_vl_get_entry_by_name(&addr, vl->vldb.name, vldb,
+               ret = afs_vl_get_entry_by_name(&addr, key, vl->vldb.name, vldb,
                                               &afs_sync_call);
                switch (ret) {
                case 0:
  * about the volume in question
  */
 static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl,
+                                        struct key *key,
                                         afs_volid_t volid,
                                         afs_voltype_t voltype,
                                         struct afs_cache_vlocation *vldb)
                _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr);
 
                /* attempt to access the VL server */
-               ret = afs_vl_get_entry_by_id(&addr, volid, voltype, vldb,
+               ret = afs_vl_get_entry_by_id(&addr, key, volid, voltype, vldb,
                                             &afs_sync_call);
                switch (ret) {
                case 0:
  * update record if we found it in the cache
  */
 static int afs_vlocation_update_record(struct afs_vlocation *vl,
+                                      struct key *key,
                                       struct afs_cache_vlocation *vldb)
 {
        afs_voltype_t voltype;
        /* contact the server to make sure the volume is still available
         * - TODO: need to handle disconnected operation here
         */
-       ret = afs_vlocation_access_vl_by_id(vl, vid, voltype, vldb);
+       ret = afs_vlocation_access_vl_by_id(vl, key, vid, voltype, vldb);
        switch (ret) {
                /* net error */
        default:
  * fill in a volume location record, consulting the cache and the VL server
  * both
  */
-static int afs_vlocation_fill_in_record(struct afs_vlocation *vl)
+static int afs_vlocation_fill_in_record(struct afs_vlocation *vl,
+                                       struct key *key)
 {
        struct afs_cache_vlocation vldb;
        int ret;
                /* try to update a known volume in the cell VL databases by
                 * ID as the name may have changed */
                _debug("found in cache");
-               ret = afs_vlocation_update_record(vl, &vldb);
+               ret = afs_vlocation_update_record(vl, key, &vldb);
        } else {
                /* try to look up an unknown volume in the cell VL databases by
                 * name */
-               ret = afs_vlocation_access_vl_by_name(vl, &vldb);
+               ret = afs_vlocation_access_vl_by_name(vl, key, &vldb);
                if (ret < 0) {
                        printk("kAFS: failed to locate '%s' in cell '%s'\n",
                               vl->vldb.name, vl->cell->name);
  * - insert/update in the local cache if did get a VL response
  */
 struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell,
+                                          struct key *key,
                                           const char *name,
                                           size_t namesz)
 {
        struct afs_vlocation *vl;
        int ret;
 
-       _enter("{%s},%*.*s,%zu",
-              cell->name, (int) namesz, (int) namesz, name, namesz);
+       _enter("{%s},{%x},%*.*s,%zu",
+              cell->name, key_serial(key),
+              (int) namesz, (int) namesz, name, namesz);
 
        if (namesz > sizeof(vl->vldb.name)) {
                _leave(" = -ENAMETOOLONG");
        up_write(&cell->vl_sem);
 
 fill_in_record:
-       ret = afs_vlocation_fill_in_record(vl);
+       ret = afs_vlocation_fill_in_record(vl, key);
        if (ret < 0)
                goto error_abandon;
        vl->state = AFS_VL_VALID;
        vl->upd_rej_cnt = 0;
        vl->upd_busy_cnt = 0;
 
-       ret = afs_vlocation_update_record(vl, &vldb);
+       ret = afs_vlocation_update_record(vl, NULL, &vldb);
        switch (ret) {
        case 0:
                afs_vlocation_apply_update(vl, &vldb);
 
  *   - there are any outstanding ops that will fetch the status
  * - TODO implement local caching
  */
-int afs_vnode_fetch_status(struct afs_vnode *vnode)
+int afs_vnode_fetch_status(struct afs_vnode *vnode,
+                          struct afs_vnode *auth_vnode, struct key *key)
 {
        struct afs_server *server;
+       unsigned long acl_order;
        int ret;
 
        DECLARE_WAITQUEUE(myself, current);
                return -ENOENT;
        }
 
+       acl_order = 0;
+       if (auth_vnode)
+               acl_order = auth_vnode->acl_order;
+
        spin_lock(&vnode->lock);
 
        if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
                _debug("USING SERVER: %p{%08x}",
                       server, ntohl(server->addr.s_addr));
 
-               ret = afs_fs_fetch_file_status(server, vnode, NULL,
+               ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
                                               &afs_sync_call);
 
        } while (!afs_volume_release_fileserver(vnode, server, ret));
 
        /* adjust the flags */
+       if (ret == 0 && auth_vnode)
+               afs_cache_permit(vnode, key, acl_order);
        afs_vnode_finalise_status_update(vnode, server, ret);
 
        _leave(" = %d", ret);
  * fetch file data from the volume
  * - TODO implement caching and server failover
  */
-int afs_vnode_fetch_data(struct afs_vnode *vnode, off_t offset, size_t length,
-                        struct page *page)
+int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
+                        off_t offset, size_t length, struct page *page)
 {
        struct afs_server *server;
        int ret;
 
-       _enter("%s,{%u,%u,%u}",
+       _enter("%s{%u,%u,%u},%x,,,",
               vnode->volume->vlocation->vldb.name,
               vnode->fid.vid,
               vnode->fid.vnode,
-              vnode->fid.unique);
+              vnode->fid.unique,
+              key_serial(key));
 
        /* this op will fetch the status */
        spin_lock(&vnode->lock);
 
                _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
 
-               ret = afs_fs_fetch_data(server, vnode, offset, length, page,
-                                       NULL, &afs_sync_call);
+               ret = afs_fs_fetch_data(server, key, vnode, offset, length,
+                                       page, NULL, &afs_sync_call);
 
        } while (!afs_volume_release_fileserver(vnode, server, ret));
 
 
  * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
  *           explicitly told otherwise
  */
-struct afs_volume *afs_volume_lookup(const char *name, struct afs_cell *cell,
-                                    int rwpath)
+struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
 {
        struct afs_vlocation *vlocation = NULL;
        struct afs_volume *volume = NULL;
        struct afs_server *server = NULL;
-       afs_voltype_t type;
-       const char *cellname, *volname, *suffix;
        char srvtmask;
-       int force, ret, loop, cellnamesz, volnamesz;
+       int ret, loop;
 
-       _enter("%s,,%d,", name, rwpath);
-
-       if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
-               printk("kAFS: unparsable volume name\n");
-               return ERR_PTR(-EINVAL);
-       }
-
-       /* determine the type of volume we're looking for */
-       force = 0;
-       type = AFSVL_ROVOL;
-
-       if (rwpath || name[0] == '%') {
-               type = AFSVL_RWVOL;
-               force = 1;
-       }
-
-       suffix = strrchr(name, '.');
-       if (suffix) {
-               if (strcmp(suffix, ".readonly") == 0) {
-                       type = AFSVL_ROVOL;
-                       force = 1;
-               } else if (strcmp(suffix, ".backup") == 0) {
-                       type = AFSVL_BACKVOL;
-                       force = 1;
-               } else if (suffix[1] == 0) {
-               } else {
-                       suffix = NULL;
-               }
-       }
-
-       /* split the cell and volume names */
-       name++;
-       volname = strchr(name, ':');
-       if (volname) {
-               cellname = name;
-               cellnamesz = volname - name;
-               volname++;
-       } else {
-               volname = name;
-               cellname = NULL;
-               cellnamesz = 0;
-       }
-
-       volnamesz = suffix ? suffix - volname : strlen(volname);
-
-       _debug("CELL:%*.*s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s",
-              cellnamesz, cellnamesz, cellname ?: "", cell,
-              volnamesz, volnamesz, volname, suffix ?: "-",
-              type,
-              force ? " FORCE" : "");
-
-       /* lookup the cell record */
-       if (cellname || !cell) {
-               cell = afs_cell_lookup(cellname, cellnamesz);
-               if (IS_ERR(cell)) {
-                       ret = PTR_ERR(cell);
-                       printk("kAFS: unable to lookup cell '%s'\n",
-                              cellname ?: "");
-                       goto error;
-               }
-       } else {
-               afs_get_cell(cell);
-       }
+       _enter("{%*.*s,%d}",
+              params->volnamesz, params->volnamesz, params->volname, params->rwpath);
 
        /* lookup the volume location record */
-       vlocation = afs_vlocation_lookup(cell, volname, volnamesz);
+       vlocation = afs_vlocation_lookup(params->cell, params->key,
+                                        params->volname, params->volnamesz);
        if (IS_ERR(vlocation)) {
                ret = PTR_ERR(vlocation);
                vlocation = NULL;
 
        /* make the final decision on the type we want */
        ret = -ENOMEDIUM;
-       if (force && !(vlocation->vldb.vidmask & (1 << type)))
+       if (params->force && !(vlocation->vldb.vidmask & (1 << params->type)))
                goto error;
 
        srvtmask = 0;
        for (loop = 0; loop < vlocation->vldb.nservers; loop++)
                srvtmask |= vlocation->vldb.srvtmask[loop];
 
-       if (force) {
-               if (!(srvtmask & (1 << type)))
+       if (params->force) {
+               if (!(srvtmask & (1 << params->type)))
                        goto error;
        } else if (srvtmask & AFS_VOL_VTM_RO) {
-               type = AFSVL_ROVOL;
+               params->type = AFSVL_ROVOL;
        } else if (srvtmask & AFS_VOL_VTM_RW) {
-               type = AFSVL_RWVOL;
+               params->type = AFSVL_RWVOL;
        } else {
                goto error;
        }
 
-       down_write(&cell->vl_sem);
+       down_write(¶ms->cell->vl_sem);
 
        /* is the volume already active? */
-       if (vlocation->vols[type]) {
+       if (vlocation->vols[params->type]) {
                /* yes - re-use it */
-               volume = vlocation->vols[type];
+               volume = vlocation->vols[params->type];
                afs_get_volume(volume);
                goto success;
        }
                goto error_up;
 
        atomic_set(&volume->usage, 1);
-       volume->type            = type;
-       volume->type_force      = force;
-       volume->cell            = cell;
-       volume->vid             = vlocation->vldb.vid[type];
+       volume->type            = params->type;
+       volume->type_force      = params->force;
+       volume->cell            = params->cell;
+       volume->vid             = vlocation->vldb.vid[params->type];
 
        init_rwsem(&volume->server_sem);
 
        afs_get_vlocation(vlocation);
        volume->vlocation = vlocation;
 
-       vlocation->vols[type] = volume;
+       vlocation->vols[volume->type] = volume;
 
 success:
        _debug("kAFS selected %s volume %08x",
               afs_voltypes[volume->type], volume->vid);
-       up_write(&cell->vl_sem);
+       up_write(¶ms->cell->vl_sem);
        afs_put_vlocation(vlocation);
-       afs_put_cell(cell);
        _leave(" = %p", volume);
        return volume;
 
        /* clean up */
 error_up:
-       up_write(&cell->vl_sem);
+       up_write(¶ms->cell->vl_sem);
 error:
        afs_put_vlocation(vlocation);
-       afs_put_cell(cell);
        _leave(" = %d", ret);
        return ERR_PTR(ret);
 
 error_discard:
-       up_write(&cell->vl_sem);
+       up_write(¶ms->cell->vl_sem);
 
        for (loop = volume->nservers - 1; loop >= 0; loop--)
                afs_put_server(volume->servers[loop]);