[AFS]: Add "directory write" support.
[linux-2.6.git] / fs / afs / volume.c
index c82e1bb..dd160ca 100644 (file)
@@ -1,6 +1,6 @@
 /* AFS volume management
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 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
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include "volume.h"
-#include "vnode.h"
-#include "cell.h"
-#include "cache.h"
-#include "cmservice.h"
-#include "fsclient.h"
-#include "vlclient.h"
 #include "internal.h"
 
-#ifdef __KDEBUG
 static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
-#endif
-
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_volume_cache_match(void *target,
-                                                 const void *entry);
-static void afs_volume_cache_update(void *source, void *entry);
-
-struct cachefs_index_def afs_volume_cache_index_def = {
-       .name           = "volume",
-       .data_size      = sizeof(struct afs_cache_vhash),
-       .keys[0]        = { CACHEFS_INDEX_KEYS_BIN, 1 },
-       .keys[1]        = { CACHEFS_INDEX_KEYS_BIN, 1 },
-       .match          = afs_volume_cache_match,
-       .update         = afs_volume_cache_update,
-};
-#endif
 
 /*
  * lookup a volume by name
@@ -65,110 +41,52 @@ struct cachefs_index_def afs_volume_cache_index_def = {
  * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
  *           explicitly told otherwise
  */
-int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
-                     struct afs_volume **_volume)
+struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
 {
        struct afs_vlocation *vlocation = NULL;
        struct afs_volume *volume = NULL;
-       afs_voltype_t type;
-       const char *cellname, *volname, *suffix;
+       struct afs_server *server = NULL;
        char srvtmask;
-       int force, ret, loop, cellnamesz, volnamesz;
-
-       _enter("%s,,%d,", name, rwpath);
-
-       if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
-               printk("kAFS: unparsable volume name\n");
-               return -EINVAL;
-       }
-
-       /* determine the type of volume we're looking for */
-       force = 0;
-       type = AFSVL_ROVOL;
+       int ret, loop;
 
-       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) {
-               ret = afs_cell_lookup(cellname, cellnamesz, &cell);
-               if (ret<0) {
-                       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 */
-       ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation);
-       if (ret < 0)
+       vlocation = afs_vlocation_lookup(params->cell, params->key,
+                                        params->volname, params->volnamesz);
+       if (IS_ERR(vlocation)) {
+               ret = PTR_ERR(vlocation);
+               vlocation = NULL;
                goto error;
+       }
 
        /* 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(&params->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;
        }
@@ -182,23 +100,24 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
                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);
 
        /* look up all the applicable server records */
        for (loop = 0; loop < 8; loop++) {
                if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
-                       ret = afs_server_lookup(
-                               volume->cell,
-                               &vlocation->vldb.servers[loop],
-                               &volume->servers[volume->nservers]);
-                       if (ret < 0)
+                       server = afs_lookup_server(
+                              volume->cell, &vlocation->vldb.servers[loop]);
+                       if (IS_ERR(server)) {
+                               ret = PTR_ERR(server);
                                goto error_discard;
+                       }
 
+                       volume->servers[volume->nservers] = server;
                        volume->nservers++;
                }
        }
@@ -214,26 +133,26 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
        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);
-       *_volume = volume;
-       ret = 0;
+       up_write(&params->cell->vl_sem);
+       afs_put_vlocation(vlocation);
+       _leave(" = %p", volume);
+       return volume;
 
        /* clean up */
 error_up:
-       up_write(&cell->vl_sem);
+       up_write(&params->cell->vl_sem);
 error:
        afs_put_vlocation(vlocation);
-       afs_put_cell(cell);
-
-       _leave(" = %d (%p)", ret, volume);
-       return ret;
+       _leave(" = %d", ret);
+       return ERR_PTR(ret);
 
 error_discard:
-       up_write(&cell->vl_sem);
+       up_write(&params->cell->vl_sem);
 
        for (loop = volume->nservers - 1; loop >= 0; loop--)
                afs_put_server(volume->servers[loop]);
@@ -255,10 +174,9 @@ void afs_put_volume(struct afs_volume *volume)
 
        _enter("%p", volume);
 
-       vlocation = volume->vlocation;
+       ASSERTCMP(atomic_read(&volume->usage), >, 0);
 
-       /* sanity check */
-       BUG_ON(atomic_read(&volume->usage) <= 0);
+       vlocation = volume->vlocation;
 
        /* to prevent a race, the decrement and the dequeue must be effectively
         * atomic */
@@ -292,14 +210,21 @@ void afs_put_volume(struct afs_volume *volume)
  * pick a server to use to try accessing this volume
  * - returns with an elevated usage count on the server chosen
  */
-int afs_volume_pick_fileserver(struct afs_volume *volume,
-                              struct afs_server **_server)
+struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
 {
+       struct afs_volume *volume = vnode->volume;
        struct afs_server *server;
        int ret, state, loop;
 
        _enter("%s", volume->vlocation->vldb.name);
 
+       /* stick with the server we're already using if we can */
+       if (vnode->server && vnode->server->fs_state == 0) {
+               afs_get_server(vnode->server);
+               _leave(" = %p [current]", vnode->server);
+               return vnode->server;
+       }
+
        down_read(&volume->server_sem);
 
        /* handle the no-server case */
@@ -307,7 +232,7 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
                ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
                up_read(&volume->server_sem);
                _leave(" = %d [no servers]", ret);
-               return ret;
+               return ERR_PTR(ret);
        }
 
        /* basically, just search the list for the first live server and use
@@ -317,15 +242,16 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
                server = volume->servers[loop];
                state = server->fs_state;
 
+               _debug("consider %d [%d]", loop, state);
+
                switch (state) {
                        /* found an apparently healthy server */
                case 0:
                        afs_get_server(server);
                        up_read(&volume->server_sem);
-                       *_server = server;
-                       _leave(" = 0 (picked %08x)",
-                              ntohl(server->addr.s_addr));
-                       return 0;
+                       _leave(" = %p (picked %08x)",
+                              server, ntohl(server->addr.s_addr));
+                       return server;
 
                case -ENETUNREACH:
                        if (ret == 0)
@@ -361,7 +287,7 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
         */
        up_read(&volume->server_sem);
        _leave(" = %d", ret);
-       return ret;
+       return ERR_PTR(ret);
 }
 
 /*
@@ -369,11 +295,13 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
  * - releases the ref on the server struct that was acquired by picking
  * - records result of using a particular server to access a volume
  * - return 0 to try again, 1 if okay or to issue error
+ * - the caller must release the server struct if result was 0
  */
-int afs_volume_release_fileserver(struct afs_volume *volume,
+int afs_volume_release_fileserver(struct afs_vnode *vnode,
                                  struct afs_server *server,
                                  int result)
 {
+       struct afs_volume *volume = vnode->volume;
        unsigned loop;
 
        _enter("%s,%08x,%d",
@@ -384,14 +312,16 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
                /* success */
        case 0:
                server->fs_act_jif = jiffies;
-               break;
+               server->fs_state = 0;
+               _leave("");
+               return 1;
 
                /* the fileserver denied all knowledge of the volume */
        case -ENOMEDIUM:
                server->fs_act_jif = jiffies;
                down_write(&volume->server_sem);
 
-               /* first, find where the server is in the active list (if it
+               /* firstly, find where the server is in the active list (if it
                 * is) */
                for (loop = 0; loop < volume->nservers; loop++)
                        if (volume->servers[loop] == server)
@@ -429,6 +359,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
        case -ENETUNREACH:
        case -EHOSTUNREACH:
        case -ECONNREFUSED:
+       case -ETIME:
        case -ETIMEDOUT:
        case -EREMOTEIO:
                /* mark the server as dead
@@ -448,14 +379,12 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
                server->fs_act_jif = jiffies;
        case -ENOMEM:
        case -ENONET:
-               break;
+               /* tell the caller to accept the result */
+               afs_put_server(server);
+               _leave(" [local failure]");
+               return 1;
        }
 
-       /* tell the caller to accept the result */
-       afs_put_server(server);
-       _leave("");
-       return 1;
-
        /* tell the caller to loop around and try the next server */
 try_next_server_upw:
        up_write(&volume->server_sem);
@@ -464,40 +393,3 @@ try_next_server:
        _leave(" [try next server]");
        return 0;
 }
-
-/*
- * match a volume hash record stored in the cache
- */
-#ifdef AFS_CACHING_SUPPORT
-static cachefs_match_val_t afs_volume_cache_match(void *target,
-                                                 const void *entry)
-{
-       const struct afs_cache_vhash *vhash = entry;
-       struct afs_volume *volume = target;
-
-       _enter("{%u},{%u}", volume->type, vhash->vtype);
-
-       if (volume->type == vhash->vtype) {
-               _leave(" = SUCCESS");
-               return CACHEFS_MATCH_SUCCESS;
-       }
-
-       _leave(" = FAILED");
-       return CACHEFS_MATCH_FAILED;
-}
-#endif
-
-/*
- * update a volume hash record stored in the cache
- */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_volume_cache_update(void *source, void *entry)
-{
-       struct afs_cache_vhash *vhash = entry;
-       struct afs_volume *volume = source;
-
-       _enter("");
-
-       vhash->vtype = volume->type;
-}
-#endif