[AFS]: Add security support.
[linux-2.6.git] / fs / afs / super.c
index 77e6875..497350a 100644 (file)
 
 #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);
 
@@ -150,8 +144,8 @@ static int want_no_value(char *const *_value, const char *option)
  * - 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;
@@ -183,8 +177,8 @@ static int afs_super_parse_options(struct afs_mount_params *params,
                        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;
@@ -199,6 +193,99 @@ error:
 }
 
 /*
+ * 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
  */
 static int afs_test_super(struct super_block *sb, void *data)
@@ -244,7 +331,7 @@ static int afs_fill_super(struct super_block *sb, void *data)
        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;
 
@@ -285,31 +372,40 @@ static int afs_get_sb(struct file_system_type *fs_type,
        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(&params, 0, sizeof(params));
 
-       /* parse the options */
+       /* parse the options and device name */
        if (options) {
-               ret = afs_super_parse_options(&params, options, &dev_name);
+               ret = afs_parse_options(&params, 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(&params, 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(&params);
        if (IS_ERR(vol)) {
                ret = PTR_ERR(vol);
                goto error;
        }
-
        params.volume = vol;
 
        /* allocate a deviceless superblock */
@@ -337,13 +433,14 @@ static int afs_get_sb(struct file_system_type *fs_type,
 
        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;
 }
@@ -375,6 +472,7 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep,
                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);