[PVFS2-developers] Limit a particular pvfs2 client's access to data(take 2)

Murali Vilayannur vilayann at mcs.anl.gov
Fri Dec 30 13:38:04 EST 2005


Hi Rob,
Attached patch (against cvs) implements the changes that you suggested (I
hope :))
With this patch, we not only allow wildcard-based entries, but also
network/netmask entries such as the following ( I took the liberty of
using an @ symbol to delineate the netmask and network. We can change
that if you hate that :))

<ExportOptions>
ReadOnly tcp://192.168.2.0@24 tcp://192.168.4.* ...
RootSquash tcp://192.168.3.0@20 tcp://192.168.6.* ...
AllSquash tcp://192.168.1.0@14 tcp://192.168.7.* ...
AnonUID 65535
AnonGID 65535
</ExportOptions>

Along the same lines the security options for the trusted ports now also
changes to
<Security>
TrustedNetwork tcp://192.168.2.0@24 ...
TrustedPorts   0-2048
</Security>

The parsing code for a list of character strings has been refactored,
and so is the code for splitting netmasks and IP address matching etc.
Incidentally, the patch also fixed a wierd bug that I used to face
with trusted ports where despite setting SO_REUSEADDR and closing all
sockets on shutdown would cause subsequent bind attempts to fail. I
realize now that setting SO_REUSEADDR after the bind is done successfully,
is of no use and has to be done before ;(
(As an aside, I also came across a very weird gcc 4 assembler generation
(bug? I hope) when it compiles the convert_mask() function. Hence the workaround in that
function.)
Patch also incorporates the suid mount option to honor setuid bit on pvfs2
files (mount -o suid,...).
NOTE: Enforcing or not enforcing suid bit at the server does not make much
sense as an ExportOption I think because it can be subverted at the client-side
kernel module regardless since the operations that make use of suid bit is
local to a node. I could add it if you still think it makes sense.
Do let me know if it satisfies what you had in mind.
Patch does not yet merge the ExportOptions and Security related stuff as I
am not sure what the right approach to that is.
Phil, I have added some new BMI interfaces for this. Do let me know if you
dont like something or want it done some other way.
Thanks,
Murali

On Sun, 11 Dec 2005, Rob Ross wrote:

> Murali Vilayannur wrote:
> > Hey Rob,
> >> If you're going to do TCP addresses, the normal thing to do is
> >> address/netmask pairs.  tcp://192.168.1.0/8 would be more or less how
> >> nmap would do it.  tcp://192.168.1.0/255.255.255.0 would be one way the
> >> NFS exports file would accept it (the other is valid for older NFS
> >> versions too, I think).
> >
> > Okay. I think we should be able to change that.
> > Wouldn't wildcards like tcp://192.168.1.* achieve the same purpose? I must
> > be missing something...
>
> You can define subnets that use just part of the bits in the quad for
> the network.  The '*' notation doesn't cover those cases.
>
> >> function.  That's not so great, but I don't think that we should worry
> >> about it for now.
> >
> > Yeah. if people want all these options, then they pay for them in terms
> > of performance :)
> > If no options are specified it will fall thru quickly with no string
> > comparisons.
>
> Yep!
>
> >> One other thing I'd like to see is a function or two for pulling out
> >> these lists of addresses in the config parsing code, to keep us from
> >> having N copies of the same stuff.
> >
> > okay. will refactor the parsing code into a common function.
>
> Thanks!
>
> >> Last thing: I'm not convinced that we should do anything different for
> >> the Security stuff, other than possibly moving it into the same
> >> ExportOptions section as everything else.  We can try to make that more
> >> generic some other time.
> >
> > Yup. That is what I had in mind (moving it as part of the ExportOptions
> > section and having a tag like TrustedPorts <list of wildcard BMi-tcp url's/netmask combinations>
> > and TrustedNetwork <list of wildcard BMI-tcp url's/netmask combinations>.
>
> Sounds great.  Thanks,
>
> Rob
>
>
-------------- next part --------------
Index: include/pvfs2-types.h
===================================================================
RCS file: /anoncvs/pvfs2/include/pvfs2-types.h,v
retrieving revision 1.119
diff -u -r1.119 pvfs2-types.h
--- include/pvfs2-types.h	16 Dec 2005 06:00:18 -0000	1.119
+++ include/pvfs2-types.h	30 Dec 2005 19:08:59 -0000
@@ -162,13 +162,13 @@
 #define PVFS_U_READ    (1 << 8)
 /* no PVFS_U_VTX (sticky bit) */
 #define PVFS_G_SGID    (1 << 10)
-/* no PVFS_U_SGID */
+#define PVFS_U_SUID    (1 << 11)
 
 /* valid permission mask */
 #define PVFS_PERM_VALID \
 (PVFS_O_EXECUTE | PVFS_O_WRITE | PVFS_O_READ | PVFS_G_EXECUTE | \
  PVFS_G_WRITE | PVFS_G_READ | PVFS_U_EXECUTE | PVFS_U_WRITE | \
- PVFS_U_READ | PVFS_G_SGID)
+ PVFS_U_READ | PVFS_G_SGID | PVFS_U_SUID)
 
 #define PVFS_USER_ALL  (PVFS_U_EXECUTE|PVFS_U_WRITE|PVFS_U_READ)
 #define PVFS_GROUP_ALL (PVFS_G_EXECUTE|PVFS_G_WRITE|PVFS_G_READ)
Index: include/pvfs2-util.h
===================================================================
RCS file: /anoncvs/pvfs2/include/pvfs2-util.h,v
retrieving revision 1.39
diff -u -r1.39 pvfs2-util.h
--- include/pvfs2-util.h	8 Nov 2005 16:41:30 -0000	1.39
+++ include/pvfs2-util.h	30 Dec 2005 19:08:59 -0000
@@ -94,30 +94,34 @@
 inline uint32_t PVFS_util_object_to_sys_attr_mask( 
     uint32_t obj_mask);
 
-static inline int PVFS2_translate_mode(int mode)
+static inline int PVFS2_translate_mode(int mode, int suid)
 {
     int ret = 0, i = 0;
-    static int modes[10] =
+    static int modes[11] =
     {
         S_IXOTH, S_IWOTH, S_IROTH,
         S_IXGRP, S_IWGRP, S_IRGRP,
         S_IXUSR, S_IWUSR, S_IRUSR,
-        S_ISGID
+        S_ISGID, S_ISUID
     };
-    static int pvfs2_modes[10] =
+    static int pvfs2_modes[11] =
     {
         PVFS_O_EXECUTE, PVFS_O_WRITE, PVFS_O_READ,
         PVFS_G_EXECUTE, PVFS_G_WRITE, PVFS_G_READ,
         PVFS_U_EXECUTE, PVFS_U_WRITE, PVFS_U_READ,
-        PVFS_G_SGID
+        PVFS_G_SGID,    PVFS_U_SUID
     };
 
-    for(i = 0; i < 10; i++)
+    for(i = 0; i < 11; i++)
     {
         if (mode & modes[i])
         {
             ret |= pvfs2_modes[i];
         }
+    }
+    if (suid == 0 && (ret & PVFS_U_SUID))
+    {
+        ret &= ~PVFS_U_SUID;
     }
     return ret;
 }
Index: src/apps/admin/pvfs2-cp.c
===================================================================
RCS file: /anoncvs/pvfs2/src/apps/admin/pvfs2-cp.c,v
retrieving revision 1.16
diff -u -r1.16 pvfs2-cp.c
--- src/apps/admin/pvfs2-cp.c	11 Nov 2005 21:30:57 -0000	1.16
+++ src/apps/admin/pvfs2-cp.c	30 Dec 2005 19:09:00 -0000
@@ -684,7 +684,7 @@
 {
     attr->owner = credentials->uid; 
     attr->group = credentials->gid;
-    attr->perms = PVFS2_translate_mode(mode);
+    attr->perms = PVFS2_translate_mode(mode, 0);
     attr->atime = time(NULL);
     attr->mtime = attr->atime;
     attr->ctime = attr->atime;
Index: src/apps/admin/pvfs2-fsck.c
===================================================================
RCS file: /anoncvs/pvfs2/src/apps/admin/pvfs2-fsck.c,v
retrieving revision 1.13
diff -u -r1.13 pvfs2-fsck.c
--- src/apps/admin/pvfs2-fsck.c	11 Nov 2005 21:30:57 -0000	1.13
+++ src/apps/admin/pvfs2-fsck.c	30 Dec 2005 19:09:01 -0000
@@ -989,7 +989,7 @@
     attr.owner = creds->uid;
     attr.owner = creds->uid;
     attr.group = creds->gid;
-    attr.perms = PVFS2_translate_mode(0755);
+    attr.perms = PVFS2_translate_mode(0755, 0);
     attr.atime = time(NULL);
     attr.mtime = attr.atime;
     attr.ctime = attr.atime;
Index: src/apps/admin/pvfs2-mkdir.c
===================================================================
RCS file: /anoncvs/pvfs2/src/apps/admin/pvfs2-mkdir.c,v
retrieving revision 1.8
diff -u -r1.8 pvfs2-mkdir.c
--- src/apps/admin/pvfs2-mkdir.c	11 Nov 2005 21:30:57 -0000	1.8
+++ src/apps/admin/pvfs2-mkdir.c	30 Dec 2005 19:09:01 -0000
@@ -462,7 +462,7 @@
     if(!mode_requested)
     {
         mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU; /* 0777 */
-        opts->mode = PVFS2_translate_mode(mode & ~PVFS_util_get_umask());
+        opts->mode = PVFS2_translate_mode(mode & ~PVFS_util_get_umask(), 0);
     }
     
     /* Allocate memory to hold the filenames */
Index: src/common/misc/server-config.c
===================================================================
RCS file: /anoncvs/pvfs2/src/common/misc/server-config.c,v
retrieving revision 1.76
diff -u -r1.76 server-config.c
--- src/common/misc/server-config.c	11 Nov 2005 21:31:02 -0000	1.76
+++ src/common/misc/server-config.c	30 Dec 2005 19:09:04 -0000
@@ -41,6 +41,8 @@
 static DOTCONF_CB(exit_filesystem_context);
 static DOTCONF_CB(enter_storage_hints_context);
 static DOTCONF_CB(exit_storage_hints_context);
+static DOTCONF_CB(enter_export_options_context);
+static DOTCONF_CB(exit_export_options_context);
 static DOTCONF_CB(enter_mhranges_context);
 static DOTCONF_CB(exit_mhranges_context);
 static DOTCONF_CB(enter_dhranges_context);
@@ -62,6 +64,13 @@
 static DOTCONF_CB(get_range_list);
 static DOTCONF_CB(get_bmi_module_list);
 static DOTCONF_CB(get_flow_module_list);
+
+static DOTCONF_CB(get_root_squash);
+static DOTCONF_CB(get_read_only);
+static DOTCONF_CB(get_all_squash);
+static DOTCONF_CB(get_anon_gid);
+static DOTCONF_CB(get_anon_uid);
+
 static DOTCONF_CB(get_handle_recycle_timeout_seconds);
 static DOTCONF_CB(get_attr_cache_keywords_list);
 static DOTCONF_CB(get_attr_cache_size);
@@ -189,11 +198,11 @@
      * which the connections are going to be accepted and serviced.
      * The format of the TrustedNetwork option is:
      *
-     * TrustedNetwork bmi-network-address bmi-network-mask
+     * TrustedNetwork bmi-network-address at bmi-network-mask
      *
      * As an example:
      *
-     * TrustedNetwork tcp://192.168.4.0 tcp://255.255.255.0
+     * TrustedNetwork tcp://192.168.4.0@24
      */
     {"TrustedNetwork",ARG_LIST, get_trusted_network,NULL,
         CTX_SECURITY,NULL},
@@ -238,6 +247,19 @@
     {"</FileSystem>",ARG_NONE, exit_filesystem_context,NULL,CTX_FILESYSTEM,
         NULL},
 
+    /* Specifies the beginning of a ExportOptions context.
+     * This groups options specific to a filesystem and related to the behavior
+     * of how it gets exported to various clients. Most of these options
+     * will affect things like what uids get translated to and so on..
+     */
+    {"<ExportOptions>",ARG_NONE, enter_export_options_context, NULL, 
+        CTX_FILESYSTEM, NULL},
+
+    /* Specifies the end-tag of the ExportOptions context.
+     */
+    {"</ExportOptions>",ARG_NONE, exit_export_options_context, NULL,
+        CTX_EXPORT, NULL},
+
     /* Specifies the beginning of a StorageHints context.  This groups
      * options specific to a filesystem and related to the behavior of the
      * storage system.  Mostly these options are passed directly to the
@@ -280,7 +302,7 @@
      */
     {"</DataHandleRanges>",ARG_NONE, exit_dhranges_context,NULL,
         CTX_DATAHANDLERANGES,NULL},
-    
+
     /* Provides a context for defining the filesystem's default
      * distribution to use and the parameters to be set for that distribution.
      *
@@ -485,6 +507,49 @@
     {"FlowModules",ARG_LIST, get_flow_module_list,NULL,
         CTX_DEFAULTS|CTX_GLOBAL,"flowproto_multiqueue,"},
 
+    /* Define options that will influence the way a file-system gets exported
+     * to the rest of the world.
+     */
+
+    /* RootSquash option specifies whether the exported file-system needs to squash accesses
+     * by root. This is an optional parameter that needs to be specified as part of the ExportOptions
+     * context and is a list of BMI URL specification of client addresses for which RootSquash
+     * has to be enforced. 
+     * RootSquash tcp://192.168.2.0@24 tcp://10.0.0.* tcp://192.168.* ...
+     */
+    {"RootSquash", ARG_LIST, get_root_squash, NULL,
+        CTX_EXPORT, ""},
+
+    /* ReadOnly option specifies whether the exported file-system needs to disallow write accesses
+     * from clients or anything that modifies the state of the file-system.
+     * This is an optional parameter that needs to be specified as part of the ExportOptions
+     * context and is a list of BMI URL specification of client addresses for which ReadOnly
+     * has to be enforced.
+     * ReadOnly tcp://192.168.2.0@24 tcp://10.0.0.* tcp://192.168.* ...
+     */
+    {"ReadOnly", ARG_LIST,  get_read_only,    NULL,
+        CTX_EXPORT, ""},
+
+    /* AllSquash option specifies whether the exported file-system needs to squash all accesses
+     * to the file-system to a specified uid/gid!
+     * This is an optional parameter that needs to be specified as part of the ExportOptions
+     * context and is a list of BMI URL specification of client addresses for which AllSquash
+     * has to be enforced.
+     * AllSquash tcp://192.168.2.0@24 tcp://10.0.0.* tcp://192.168.* ...
+     */
+    {"AllSquash", ARG_LIST, get_all_squash,   NULL,
+        CTX_EXPORT, ""},
+
+    /* AnonUID and AnonGID are 2 integers that tell the servers to translate the requesting clients'
+     * uid/gid to the specified ones whenever AllSquash is specified!
+     * If these are not specified and AllSquash is specified then the uid used will be
+     * that of nobody and gid that of nobody
+     */
+    {"AnonUID",  ARG_STR,  get_anon_uid,     NULL,
+        CTX_EXPORT, "65534"},
+    {"AnonGID",  ARG_STR,  get_anon_gid,     NULL,
+        CTX_EXPORT, "65534"},
+
     /* The TROVE storage layer has a management component that deals with
      * allocating handle values for new metafiles and datafiles.  The underlying
      * trove module can be given a hint to tell it how long to wait before
@@ -929,6 +994,23 @@
     return NULL;
 }
 
+DOTCONF_CB(enter_export_options_context)
+{
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+    config_s->configuration_context = CTX_EXPORT;
+
+    return PINT_dotconf_set_defaults(
+        cmd->configfile, CTX_EXPORT);
+}
+
+DOTCONF_CB(exit_export_options_context)
+{
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+    config_s->configuration_context = CTX_FILESYSTEM;
+    return NULL;
+}
 
 DOTCONF_CB(enter_mhranges_context)
 {
@@ -1119,6 +1201,286 @@
     return NULL;
 }
 
+static void free_list_of_strings(int list_count, char ***new_list)
+{
+    int i;
+
+    if (new_list && *new_list != NULL)
+    {
+        for (i = 0; i < list_count; i++)
+        {
+            free((*new_list)[i]);
+            (*new_list)[i] = NULL;
+        }
+        free(*new_list);
+        *new_list = NULL;
+    }
+    return;
+}
+
+/*
+ * Given a parsed_list containing list_count number of strings,
+ * create a new array of pointers which holds a duplicate list
+ * of all the strings present in the original parsed list.
+ * Used as a helper function by all the routines/keywords that require a list
+ * of strings as their options/arguments.
+ */
+static int get_list_of_strings(int list_count, char **parsed_list,
+        char ***new_list)
+{
+    int i;
+
+    *new_list = (char **) calloc(list_count, sizeof(char *));
+    if (*new_list == NULL)
+    {
+        errno = ENOMEM;
+        return -1;
+    }
+    for (i = 0; i < list_count; i++)
+    {
+        (*new_list)[i] = strdup(parsed_list[i]);
+        if ((*new_list)[i] == NULL)
+        {
+            break;
+        }
+    }
+    if (i != list_count)
+    {
+        int j;
+        for (j = 0; j < i; j++)
+        {
+            free((*new_list)[j]);
+            (*new_list)[j] = NULL;
+        }
+        free(*new_list);
+        *new_list = NULL;
+        errno = ENOMEM;
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Given a set/list of BMI addresses suffixed with @ and a netmask,
+ * this function will populate a netmasks array that contains
+ * the integer subsqeuent to the @ symbol.
+ * i.e given tcp://192.168.2.0@24 we will have tcp://192.168.2.0, 24 as the netmask
+ */
+static int setup_netmasks(int count, char **bmi_address, int *netmasks)
+{
+    int i;
+    for (i = 0; i < count; i++)
+    {
+        /* 
+         * if we find a @ then we parse the 
+         * chars at the end of it and make sure
+         * that it is less than equal to 32,
+         * else we check if there is a wildcard (*)
+         * present in which case we set it to -1
+         * else we assume that it is equal to 32.
+         */
+        char *special_char = strchr(bmi_address[i], '@'), *ptr = NULL;
+        if (special_char == NULL)
+        {
+            special_char = strchr(bmi_address[i], '*');
+            if (special_char == NULL)
+                netmasks[i] = 32;
+            else
+                netmasks[i] = -1;
+        }
+        else
+        {
+            *special_char = '\0';
+            netmasks[i] = strtol(special_char + 1, &ptr, 10);
+            if (*ptr != '\0' || netmasks[i] < 0
+                    || netmasks[i] > 32)
+                return -1;
+        }
+        gossip_debug(GOSSIP_SERVER_DEBUG, "Parsed %s:%d\n", bmi_address[i], netmasks[i]);
+    }
+    return 0;
+}
+
+DOTCONF_CB(get_root_squash)
+{
+    struct filesystem_configuration_s *fs_conf = NULL;
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+
+    fs_conf = (struct filesystem_configuration_s *)
+        PINT_llist_head(config_s->file_systems);
+    assert(fs_conf);
+
+    if (cmd->arg_count == 0)
+    {
+        fs_conf->exp_flags &= ~TROVE_EXP_ROOT_SQUASH;
+    }
+    else 
+    {
+        fs_conf->exp_flags |= TROVE_EXP_ROOT_SQUASH;
+        fs_conf->root_squash_netmasks = (int *) calloc(cmd->arg_count, sizeof(int));
+        if (fs_conf->root_squash_netmasks == NULL)
+        {
+            fs_conf->root_squash_count = 0;
+            return("Could not allocate memory for root_squash_netmasks\n");
+        }
+        if (get_list_of_strings(cmd->arg_count, cmd->data.list,
+                    &fs_conf->root_squash_hosts) < 0)
+        {
+            free(fs_conf->root_squash_netmasks);
+            fs_conf->root_squash_netmasks = NULL;
+            fs_conf->root_squash_count = 0;
+            return("Could not allocate memory for root_squash_hosts\n");
+        }
+        fs_conf->root_squash_count = cmd->arg_count;
+        /* Setup the netmasks */
+        if (setup_netmasks(fs_conf->root_squash_count, fs_conf->root_squash_hosts, 
+                    fs_conf->root_squash_netmasks) < 0)
+        {
+            free(fs_conf->root_squash_netmasks);
+            fs_conf->root_squash_netmasks = NULL;
+            free_list_of_strings(fs_conf->root_squash_count, &fs_conf->root_squash_hosts);
+            fs_conf->root_squash_count = 0;
+            return("Could not setup netmasks for root_squash_hosts\n");
+        }
+        gossip_debug(GOSSIP_SERVER_DEBUG, "Parsed %d RootSquash wildcard entries\n",
+                cmd->arg_count);
+    }
+    return NULL;
+}
+
+DOTCONF_CB(get_read_only)
+{
+    struct filesystem_configuration_s *fs_conf = NULL;
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+
+    fs_conf = (struct filesystem_configuration_s *)
+        PINT_llist_head(config_s->file_systems);
+    assert(fs_conf);
+    
+    if (cmd->arg_count == 0)
+    {
+        fs_conf->exp_flags &= ~TROVE_EXP_READ_ONLY;
+    }
+    else
+    {
+        fs_conf->exp_flags |= TROVE_EXP_READ_ONLY;
+        fs_conf->ro_netmasks = (int *) calloc(cmd->arg_count, sizeof(int));
+        if (fs_conf->ro_netmasks == NULL)
+        {
+            fs_conf->ro_count = 0;
+            return("Could not allocate memory for ro_netmasks\n");
+        }
+        if (get_list_of_strings(cmd->arg_count, cmd->data.list,
+                    &fs_conf->ro_hosts) < 0)
+        {
+            free(fs_conf->ro_netmasks);
+            fs_conf->ro_netmasks = NULL;
+            fs_conf->ro_count = 0;
+            return("Could not allocate memory for ro_hosts\n");
+        }
+        fs_conf->ro_count  = cmd->arg_count;
+        if (setup_netmasks(fs_conf->ro_count, fs_conf->ro_hosts, fs_conf->ro_netmasks) < 0)
+        {
+            free(fs_conf->ro_netmasks);
+            fs_conf->ro_netmasks = NULL;
+            free_list_of_strings(fs_conf->ro_count, &fs_conf->ro_hosts);
+            fs_conf->ro_count = 0;
+            return("Could not setup netmasks for ro_netmasks\n");
+        }
+        gossip_debug(GOSSIP_SERVER_DEBUG, "Parsed %d ro wildcard entries\n",
+                cmd->arg_count);
+    }
+    return NULL;
+}
+
+DOTCONF_CB(get_all_squash)
+{
+    struct filesystem_configuration_s *fs_conf = NULL;
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+
+    fs_conf = (struct filesystem_configuration_s *)
+        PINT_llist_head(config_s->file_systems);
+    assert(fs_conf);
+
+    if (cmd->arg_count == 0)
+    {
+        fs_conf->exp_flags &= ~TROVE_EXP_ALL_SQUASH;
+    }
+    else 
+    {
+        fs_conf->exp_flags |= TROVE_EXP_ALL_SQUASH;
+        fs_conf->all_squash_netmasks = (int *) calloc(cmd->arg_count, sizeof(int));
+        if (fs_conf->all_squash_netmasks == NULL)
+        {
+            fs_conf->all_squash_count = 0;
+            return("Could not allocate memory for all_squash_netmasks\n");
+        }
+        if (get_list_of_strings(cmd->arg_count, cmd->data.list,
+                    &fs_conf->all_squash_hosts) < 0)
+        {
+            free(fs_conf->all_squash_netmasks);
+            fs_conf->all_squash_netmasks = NULL;
+            fs_conf->all_squash_count = 0;
+            return("Could not allocate memory for all_squash_hosts\n");
+        }
+        fs_conf->all_squash_count = cmd->arg_count;
+        if (setup_netmasks(fs_conf->all_squash_count, fs_conf->all_squash_hosts, 
+                    fs_conf->all_squash_netmasks) < 0)
+        {
+            free(fs_conf->all_squash_netmasks);
+            fs_conf->all_squash_netmasks = NULL;
+            free_list_of_strings(fs_conf->all_squash_count, &fs_conf->all_squash_hosts);
+            fs_conf->all_squash_count = 0;
+            return("Could not setup netmasks for all_squash_hosts\n");
+        }
+        gossip_debug(GOSSIP_SERVER_DEBUG, "Parsed %d AllSquash wildcard entries\n", 
+                cmd->arg_count);
+    }
+    return NULL;
+}
+
+DOTCONF_CB(get_anon_uid)
+{
+    struct filesystem_configuration_s *fs_conf = NULL;
+    unsigned int tmp_var;
+    int ret = -1;
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+
+    fs_conf = (struct filesystem_configuration_s *)
+        PINT_llist_head(config_s->file_systems);
+    assert(fs_conf);
+    ret = sscanf(cmd->data.str, "%u", &tmp_var);
+    if(ret != 1)
+    {
+        return("AnonUID does not have a long long unsigned value.\n");
+    }
+    fs_conf->exp_anon_uid = tmp_var;
+    return NULL;
+}
+
+DOTCONF_CB(get_anon_gid)
+{
+    struct filesystem_configuration_s *fs_conf = NULL;
+    unsigned int tmp_var;
+    int ret = -1;
+    struct server_configuration_s *config_s = 
+        (struct server_configuration_s *)cmd->context;
+
+    fs_conf = (struct filesystem_configuration_s *)
+        PINT_llist_head(config_s->file_systems);
+    assert(fs_conf);
+    ret = sscanf(cmd->data.str, "%u", &tmp_var);
+    if(ret != 1)
+    {
+        return("AnonGID does not have a unsigned value.\n");
+    }
+    fs_conf->exp_anon_gid = tmp_var;
+    return NULL;
+}
 
 DOTCONF_CB(get_bmi_module_list)
 {
@@ -1196,20 +1558,30 @@
     struct server_configuration_s *config_s = 
         (struct server_configuration_s *)cmd->context;
 
-    if (cmd->arg_count != 2)
-    {
-        return("TrustedNetwork must be of the form <Network> <Mask>\n");
-    }
-    config_s->allowed_network = strdup(cmd->data.list[0]);
-    if (config_s->allowed_network == NULL)
+    config_s->allowed_masks = (int *) calloc(cmd->arg_count, sizeof(int));
+    if (config_s->allowed_masks == NULL)
     {
-        return("Could not allocate memory for network\n");
+        config_s->allowed_networks_count = 0;
+        return("Could not allocate memory for allowed netmasks\n");
     }
-    config_s->allowed_network_mask = strdup(cmd->data.list[1]);
-    if (config_s->allowed_network_mask == NULL)
+    if (get_list_of_strings(cmd->arg_count, cmd->data.list,
+                &config_s->allowed_networks) < 0)
     {
-        free(config_s->allowed_network);
-        return("Could not allocate memory for network mask\n");
+        free(config_s->allowed_masks);
+        config_s->allowed_masks = NULL;
+        config_s->allowed_networks_count = 0;
+        return("Could not allocate memory for trusted networks\n");
+    }
+    config_s->allowed_networks_count = cmd->arg_count;
+    /* Setup netmasks */
+    if (setup_netmasks(config_s->allowed_networks_count, config_s->allowed_networks,
+                config_s->allowed_masks) < 0)
+    {
+        free(config_s->allowed_masks);
+        config_s->allowed_masks = NULL;
+        free_list_of_strings(config_s->allowed_networks_count, &config_s->allowed_networks);
+        config_s->allowed_networks_count = 0;
+        return("Parse error in netmask specification\n");
     }
     /* okay, we enable trusted network as well */
     config_s->network_enabled = 1;
@@ -1679,19 +2051,25 @@
             config_s->bmi_modules = NULL;
         }
 #ifdef USE_TRUSTED
-        if (config_s->allowed_network)
+        if (config_s->allowed_networks)
         {
-            free(config_s->allowed_network);
-            config_s->allowed_network = NULL;
+            int i;
+            for (i = 0; i < config_s->allowed_networks_count; i++)
+            {
+                free(config_s->allowed_networks[i]);
+                config_s->allowed_networks[i] = NULL;
+            }
+            free(config_s->allowed_networks);
+            config_s->allowed_networks = NULL;
         }
-        if (config_s->allowed_network_mask)
+        if (config_s->allowed_masks)
         {
-            free(config_s->allowed_network_mask);
-            config_s->allowed_network_mask = NULL;
+            free(config_s->allowed_masks);
+            config_s->allowed_masks = NULL;
         }
-        if (config_s->security)
+        if (config_s->security && config_s->security_dtor)
         {
-            free(config_s->security);
+            config_s->security_dtor(config_s->security);
             config_s->security = NULL;
         }
 #endif
@@ -1911,7 +2289,39 @@
             free(fs->attr_cache_keywords);
             fs->attr_cache_keywords = NULL;
         }
-
+        /* free all ro_hosts specifications */
+        if (fs->ro_hosts)
+        {
+            free_list_of_strings(fs->ro_count, &fs->ro_hosts);
+            fs->ro_count = 0;
+        }
+        if (fs->ro_netmasks)
+        {
+            free(fs->ro_netmasks);
+            fs->ro_netmasks = NULL;
+        }
+        /* free all root_squash_hosts specifications */
+        if (fs->root_squash_hosts)
+        {
+            free_list_of_strings(fs->root_squash_count, &fs->root_squash_hosts);
+            fs->root_squash_count = 0;
+        }
+        if (fs->root_squash_netmasks)
+        {
+            free(fs->root_squash_netmasks);
+            fs->root_squash_netmasks = NULL;
+        }
+        /* free all all_squash_hosts specifications */
+        if (fs->all_squash_hosts)
+        {
+            free_list_of_strings(fs->all_squash_count, &fs->all_squash_hosts);
+            fs->all_squash_count = 0;
+        }
+        if (fs->all_squash_netmasks)
+        {
+            free(fs->all_squash_netmasks);
+            fs->all_squash_netmasks = NULL;
+        }
         free(fs);
         fs = NULL;
     }
@@ -2036,6 +2446,65 @@
             src_fs->attr_cache_max_num_elems;
         dest_fs->trove_sync_meta = src_fs->trove_sync_meta;
         dest_fs->trove_sync_data = src_fs->trove_sync_data;
+
+        /* copy all relevant export options */
+        dest_fs->exp_flags    = src_fs->exp_flags;
+        dest_fs->ro_count     = src_fs->ro_count;
+        dest_fs->root_squash_count = src_fs->root_squash_count;
+        dest_fs->all_squash_count = src_fs->all_squash_count;
+        if (src_fs->ro_count > 0 && src_fs->ro_hosts)
+        {
+            int i;
+            dest_fs->ro_hosts = (char **) calloc(src_fs->ro_count, sizeof(char *));
+            assert(dest_fs->ro_hosts);
+            for (i = 0; i < src_fs->ro_count; i++)
+            {
+                dest_fs->ro_hosts[i] = strdup(src_fs->ro_hosts[i]);
+                assert(dest_fs->ro_hosts[i]);
+            }
+        }
+        if (src_fs->ro_count > 0 && src_fs->ro_netmasks)
+        {
+            dest_fs->ro_netmasks = (int *) calloc(src_fs->ro_count, sizeof(int));
+            assert(dest_fs->ro_netmasks);
+            memcpy(dest_fs->ro_netmasks, src_fs->ro_netmasks, src_fs->ro_count * sizeof(int));
+        }
+        if (src_fs->root_squash_count > 0 && src_fs->root_squash_hosts)
+        {
+            int i;
+            dest_fs->root_squash_hosts = (char **) calloc(src_fs->root_squash_count, sizeof(char *));
+            assert(dest_fs->root_squash_hosts);
+            for (i = 0; i < src_fs->root_squash_count; i++)
+            {
+                dest_fs->root_squash_hosts[i] = strdup(src_fs->root_squash_hosts[i]);
+                assert(dest_fs->root_squash_hosts[i]);
+            }
+        }
+        if (src_fs->root_squash_count > 0 && src_fs->root_squash_netmasks)
+        {
+            dest_fs->root_squash_netmasks = (int *) calloc(src_fs->root_squash_count, sizeof(int));
+            assert(dest_fs->root_squash_netmasks);
+            memcpy(dest_fs->root_squash_netmasks, src_fs->root_squash_netmasks, src_fs->root_squash_count * sizeof(int));
+        }
+        if (src_fs->all_squash_count > 0 && src_fs->all_squash_hosts)
+        {
+            int i;
+            dest_fs->all_squash_hosts = (char **) calloc(src_fs->all_squash_count, sizeof(char *));
+            assert(dest_fs->all_squash_hosts);
+            for (i = 0; i < src_fs->all_squash_count; i++)
+            {
+                dest_fs->all_squash_hosts[i] = strdup(src_fs->all_squash_hosts[i]);
+                assert(dest_fs->all_squash_hosts[i]);
+            }
+        }
+        if (src_fs->all_squash_count > 0 && src_fs->all_squash_netmasks)
+        {
+            dest_fs->all_squash_netmasks = (int *) calloc(src_fs->all_squash_count, sizeof(int));
+            assert(dest_fs->all_squash_netmasks);
+            memcpy(dest_fs->all_squash_netmasks, src_fs->all_squash_netmasks, src_fs->all_squash_count * sizeof(int));
+        }
+        dest_fs->exp_anon_uid = src_fs->exp_anon_uid;
+        dest_fs->exp_anon_gid = src_fs->exp_anon_gid;
     }
 }
 
@@ -2191,24 +2660,26 @@
 }
 
 /*
- * Function: PINT_config_get_allowed_network
+ * Function: PINT_config_get_allowed_networks
  *
  * Params:   struct server_configuration_s *server_config
  *           int  *enabled
- *           char **allowed_network (OUT)
- *           char **allowed_netmask (OUT)
+ *           int  *allowed_network_count (OUT)
+ *           char **allowed_networks (OUT)
+ *           int *allowed_netmasks (OUT)
  *
- * Returns:  Fills up *allowed_network and *allowed_netmask
+ * Returns:  Fills up *allowed_network_count, *allowed_networks and *allowed_netmasks
  *           and returns 0 on success and -1 on failure
  *
- * Synopsis: Retrieve the allowed network address and netmask.
+ * Synopsis: Retrieve the list of allowed network addresses and netmasks
  */
 
-int PINT_config_get_allowed_network(
+int PINT_config_get_allowed_networks(
     struct server_configuration_s *config_s,
     int  *enabled,
-    char **allowed_network,
-    char **allowed_mask)
+    int  *allowed_networks_count,
+    char ***allowed_networks,
+    int  **allowed_masks)
 {
     int ret = -1;
 
@@ -2217,8 +2688,9 @@
         *enabled = config_s->network_enabled;
         if (*enabled == 1)
         {
-            *allowed_network = config_s->allowed_network;
-            *allowed_mask    = config_s->allowed_network_mask;
+            *allowed_networks_count = config_s->allowed_networks_count;
+            *allowed_networks = config_s->allowed_networks;
+            *allowed_masks    = config_s->allowed_masks;
         }
         ret = 0;
     }
Index: src/common/misc/server-config.h
===================================================================
RCS file: /anoncvs/pvfs2/src/common/misc/server-config.h,v
retrieving revision 1.48
diff -u -r1.48 server-config.h
--- src/common/misc/server-config.h	10 Nov 2005 01:27:02 -0000	1.48
+++ src/common/misc/server-config.h	30 Dec 2005 19:09:04 -0000
@@ -25,7 +25,8 @@
     CTX_DATAHANDLERANGES = (1 << 6),
     CTX_STORAGEHINTS     = (1 << 7),
     CTX_DISTRIBUTION     = (1 << 8),
-    CTX_SECURITY         = (1 << 9)
+    CTX_SECURITY         = (1 << 9),
+    CTX_EXPORT           = (1 << 10),
 };
 
 typedef struct phys_server_desc
@@ -82,6 +83,23 @@
     int trove_sync_meta;
     int trove_sync_data;
 
+    /* Export flags bitwise OR of flags specified */
+    int exp_flags;
+
+    int    ro_count;
+    char **ro_hosts;
+    int   *ro_netmasks;
+
+    int    root_squash_count;
+    char **root_squash_hosts;
+    int   *root_squash_netmasks;
+
+    int    all_squash_count;
+    char **all_squash_hosts;
+    int   *all_squash_netmasks;
+
+    PVFS_uid exp_anon_uid;
+    PVFS_gid exp_anon_gid;
 } filesystem_configuration_s;
 
 typedef struct distribution_param_configuration_s
@@ -127,9 +145,11 @@
     int           ports_enabled;    /* Should we enable trusted port connections at all? */
     unsigned long allowed_ports[2]; /* {Min, Max} value of ports from which connections will be allowed */
     int          network_enabled;   /* Should we enable trusted network connections at all? */
-    char  *allowed_network;         /* BMI address of the trusted network */
-    char  *allowed_network_mask;    /* BMI address of the trusted network mask */
+    int   allowed_networks_count;   /* Number of trusted networks parsed */
+    char  **allowed_networks;       /* BMI addresses of the trusted networks */
+    int   *allowed_masks;            /* Netmasks for each of the specified trusted network */
     void  *security;                /* BMI module specific information */
+    void  (*security_dtor)(void *); /* Destructor to free BMI module specific information */
 #endif
     int  configuration_context;
     PINT_llist *host_aliases;       /* ptrs are type host_alias_s       */
@@ -154,11 +174,12 @@
     int  *enabled,
     unsigned long *allowed_ports);
 
-int PINT_config_get_allowed_network(
+int PINT_config_get_allowed_networks(
     struct server_configuration_s *config,
     int  *enabled,
-    char **allowed_network,
-    char **allowed_mask);
+    int   *allowed_networks_count,
+    char  ***allowed_networks,
+    int   **allowed_masks);
 
 #endif
 
@@ -212,6 +233,8 @@
 int PINT_config_trim_filesystems_except(
     struct server_configuration_s *config_s,
     PVFS_fs_id fs_id);
+
+struct server_configuration_s *PINT_get_server_config(void);
 
 #ifdef __PVFS2_TROVE_SUPPORT__
 int PINT_config_pvfs2_mkspace(
Index: src/io/bmi/bmi-method-support.h
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi-method-support.h,v
retrieving revision 1.23
diff -u -r1.23 bmi-method-support.h
--- src/io/bmi/bmi-method-support.h	2 Aug 2005 17:56:12 -0000	1.23
+++ src/io/bmi/bmi-method-support.h	30 Dec 2005 19:09:04 -0000
@@ -156,6 +156,7 @@
     void (*BMI_meth_close_context)(bmi_context_id);
     int (*BMI_meth_cancel)(bmi_op_id_t, bmi_context_id);
     const char* (*BMI_meth_rev_lookup_unexpected)(method_addr_p);
+    int (*BMI_meth_query_addr_range)(method_addr_p, const char *, int);
 };
 
 
Index: src/io/bmi/bmi.c
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi.c,v
retrieving revision 1.68
diff -u -r1.68 bmi.c
--- src/io/bmi/bmi.c	14 Dec 2005 21:50:20 -0000	1.68
+++ src/io/bmi/bmi.c	30 Dec 2005 19:09:05 -0000
@@ -14,6 +14,7 @@
 #include <string.h>
 #include <assert.h>
 #include <sys/time.h>
+#include <stdio.h>
 
 #include "bmi.h"
 #include "bmi-method-support.h"
@@ -1282,6 +1283,83 @@
 	break;
     }
     return (0);
+}
+
+/** Given a string representation of a host/network address and a BMI
+ * address handle, return whether the BMI address handle is part of the wildcard
+ * address range specified by the string.
+ * \return 1 on success, -errno on failure and 0 if it is not part of
+ * the specified range
+ */
+int BMI_query_addr_range (PVFS_BMI_addr_t addr, const char *id_string, int netmask)
+{
+    int ret = -1;
+    int i = 0, failed = 1;
+    int provided_method_length = 0;
+    char *ptr, *provided_method_name = NULL;
+    ref_st_p tmp_ref = NULL;
+
+    if((strlen(id_string)+1) > BMI_MAX_ADDR_LEN)
+    {
+	return(bmi_errno_to_pvfs(-ENAMETOOLONG));
+    }
+    /* lookup the provided address */
+    gen_mutex_lock(&ref_mutex);
+    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
+    if (!tmp_ref)
+    {
+	gen_mutex_unlock(&ref_mutex);
+	return (bmi_errno_to_pvfs(-EPROTO));
+    }
+    gen_mutex_unlock(&ref_mutex);
+
+    ptr = strchr(id_string, ':');
+    if (ptr == NULL)
+    {
+        return (bmi_errno_to_pvfs(-EINVAL));
+    }
+    ret = -EPROTO;
+    provided_method_length = (unsigned long) ptr - (unsigned long) id_string;
+    provided_method_name = (char *) calloc(provided_method_length + 1, sizeof(char));
+    if (provided_method_name == NULL)
+    {
+        return bmi_errno_to_pvfs(-ENOMEM);
+    }
+    strncpy(provided_method_name, id_string, provided_method_length);
+
+    /* Now we will run through each method looking for one that
+     * matches the specified wildcard address. 
+     */
+    i = 0;
+    gen_mutex_lock(&active_method_count_mutex);
+    while (i < active_method_count)
+    {
+        char *active_method_name = (char *) active_method_table[i]->method_name + 4;
+        /* provided name matches this interface */
+        if (!strncmp(active_method_name, provided_method_name, provided_method_length))
+        {
+            int (*meth_fnptr)(method_addr_p, const char *, int);
+            failed = 0;
+            if ((meth_fnptr = active_method_table[i]->BMI_meth_query_addr_range) == NULL)
+            {
+                ret = -ENOSYS;
+                gossip_lerr("Error: method doesn't implement querying address range/wildcards yet!\n");
+                failed = 1;
+                break;
+            }
+            /* pass it into the specific bmi layer */
+            ret = meth_fnptr(tmp_ref->method_addr, id_string, netmask);
+            if (ret < 0)
+                failed = 1;
+            break;
+        }
+	i++;
+    }
+    gen_mutex_unlock(&active_method_count_mutex);
+    free(provided_method_name);
+    if (failed)
+        return bmi_errno_to_pvfs(ret);
+    return ret;
 }
 
 /** Resolves the string representation of a host address into a BMI
Index: src/io/bmi/bmi.h
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi.h,v
retrieving revision 1.27
diff -u -r1.27 bmi.h
--- src/io/bmi/bmi.h	2 Aug 2005 17:56:12 -0000	1.27
+++ src/io/bmi/bmi.h	30 Dec 2005 19:09:05 -0000
@@ -126,6 +126,8 @@
 
 const char* BMI_addr_rev_lookup_unexpected(PVFS_BMI_addr_t addr);
 
+int BMI_query_addr_range (PVFS_BMI_addr_t addr, const char *id_string, int netmask);
+
 int BMI_post_send_list(bmi_op_id_t * id,
 		       PVFS_BMI_addr_t dest,
 		       const void *const *buffer_list,
Index: src/io/bmi/bmi_gm/bmi-gm.c
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi_gm/bmi-gm.c,v
retrieving revision 1.78
diff -u -r1.78 bmi-gm.c
--- src/io/bmi/bmi_gm/bmi-gm.c	22 Dec 2005 15:42:47 -0000	1.78
+++ src/io/bmi/bmi_gm/bmi-gm.c	30 Dec 2005 19:09:08 -0000
@@ -169,6 +169,7 @@
     BMI_gm_open_context,
     BMI_gm_close_context,
     BMI_gm_cancel,
+    NULL,
     NULL
 };
 
Index: src/io/bmi/bmi_ib/ib.c
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi_ib/ib.c,v
retrieving revision 1.20
diff -u -r1.20 ib.c
--- src/io/bmi/bmi_ib/ib.c	23 Dec 2005 20:47:52 -0000	1.20
+++ src/io/bmi/bmi_ib/ib.c	30 Dec 2005 19:09:09 -0000
@@ -1597,5 +1597,6 @@
     .BMI_meth_close_context = BMI_ib_close_context,
     .BMI_meth_cancel = BMI_ib_cancel,
     .BMI_meth_rev_lookup_unexpected = BMI_ib_rev_lookup,
+	 .BMI_meth_query_addr_range = NULL,
 };
 
Index: src/io/bmi/bmi_tcp/bmi-tcp-addressing.h
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi_tcp/bmi-tcp-addressing.h,v
retrieving revision 1.14
diff -u -r1.14 bmi-tcp-addressing.h
--- src/io/bmi/bmi_tcp/bmi-tcp-addressing.h	25 Oct 2005 18:00:58 -0000	1.14
+++ src/io/bmi/bmi_tcp/bmi-tcp-addressing.h	30 Dec 2005 19:09:09 -0000
@@ -34,8 +34,9 @@
     int                 port_enforce;
     unsigned long       ports[2];
     int                 network_enforce;
-    struct in_addr      network;
-    struct in_addr      netmask;
+    int                 network_count;
+    struct in_addr      *network;
+    struct in_addr      *netmask;
 };
 
 #endif
Index: src/io/bmi/bmi_tcp/bmi-tcp.c
===================================================================
RCS file: /anoncvs/pvfs2/src/io/bmi/bmi_tcp/bmi-tcp.c,v
retrieving revision 1.99
diff -u -r1.99 bmi-tcp.c
--- src/io/bmi/bmi_tcp/bmi-tcp.c	21 Dec 2005 00:23:07 -0000	1.99
+++ src/io/bmi/bmi_tcp/bmi-tcp.c	30 Dec 2005 19:09:12 -0000
@@ -40,6 +40,7 @@
 #include "pint-event.h"
 #ifdef USE_TRUSTED
 #include "server-config.h"
+#include "bmi-tcp-addressing.h"
 #endif
 #include "gen-locks.h"
 
@@ -122,6 +123,7 @@
 		     bmi_context_id context_id);
 method_addr_p BMI_tcp_method_addr_lookup(const char *id_string);
 const char* BMI_tcp_addr_rev_lookup_unexpected(method_addr_p map);
+int BMI_tcp_query_addr_range(method_addr_p, const char *, int);
 int BMI_tcp_post_send_list(bmi_op_id_t * id,
 			   method_addr_p dest,
 			   const void *const *buffer_list,
@@ -305,7 +307,8 @@
     BMI_tcp_open_context,
     BMI_tcp_close_context,
     BMI_tcp_cancel,
-    BMI_tcp_addr_rev_lookup_unexpected
+    BMI_tcp_addr_rev_lookup_unexpected,
+    BMI_tcp_query_addr_range,
 };
 
 /* module parameters */
@@ -632,6 +635,75 @@
     return (0);
 }
 
+#ifdef USE_TRUSTED
+
+static struct tcp_allowed_connection_s *
+alloc_trusted_connection_info(int network_count)
+{
+    struct tcp_allowed_connection_s *tcp_allowed_connection_info = NULL;
+
+    tcp_allowed_connection_info = (struct tcp_allowed_connection_s *)
+            calloc(1, sizeof(struct tcp_allowed_connection_s));
+    if (tcp_allowed_connection_info)
+    {
+        tcp_allowed_connection_info->network =
+            (struct in_addr *) calloc(network_count, sizeof(struct in_addr));
+        if (tcp_allowed_connection_info->network == NULL)
+        {
+            free(tcp_allowed_connection_info);
+            tcp_allowed_connection_info = NULL;
+        }
+        else
+        {
+            tcp_allowed_connection_info->netmask =
+                (struct in_addr *) calloc(network_count, sizeof(struct in_addr));
+            if (tcp_allowed_connection_info->netmask == NULL)
+            {
+                free(tcp_allowed_connection_info->network);
+                free(tcp_allowed_connection_info);
+                tcp_allowed_connection_info = NULL;
+            }
+            else {
+                tcp_allowed_connection_info->network_count = network_count;
+            }
+        }
+    }
+    return tcp_allowed_connection_info;
+}
+
+static void 
+dealloc_trusted_connection_info(void* ptcp_allowed_connection_info)
+{
+    struct tcp_allowed_connection_s *tcp_allowed_connection_info =
+        (struct tcp_allowed_connection_s *) ptcp_allowed_connection_info;
+    if (tcp_allowed_connection_info)
+    {
+        free(tcp_allowed_connection_info->network);
+        tcp_allowed_connection_info->network = NULL;
+        free(tcp_allowed_connection_info->netmask);
+        tcp_allowed_connection_info->netmask = NULL;
+        free(tcp_allowed_connection_info);
+    }
+    return;
+}
+
+#endif
+
+/*
+ * This function will convert a mask_bits value to an in_addr
+ * representation. i.e for example if
+ * mask_bits was 24 then it would be 255.255.255.0
+ * if mask_bits was 22 then it would be 255.255.252.0
+ * etc
+ */
+static void convert_mask(int mask_bits, struct in_addr *mask)
+{
+   uint32_t addr = -1;
+   addr = addr & ~~(-1 << (mask_bits ? (32 - mask_bits) : 32));
+   mask->s_addr = htonl(addr);
+   return;
+}
+
 /* BMI_tcp_set_info()
  * 
  * Pass in optional parameters.
@@ -678,10 +750,13 @@
         }
         else 
         {
-            char *bmi_network = NULL, *bmi_netmask = NULL;
-            struct server_configuration_s *svc_config = (struct server_configuration_s *) inout_parameter;
-            tcp_allowed_connection = 
-                (struct tcp_allowed_connection_s *) calloc(1, sizeof(struct tcp_allowed_connection_s));
+            int    bmi_networks_count = 0;
+            char **bmi_networks = NULL;
+            int   *bmi_netmasks = NULL;
+            struct server_configuration_s *svc_config = NULL;
+
+            svc_config = (struct server_configuration_s *) inout_parameter;
+            tcp_allowed_connection = alloc_trusted_connection_info(svc_config->allowed_networks_count);
             if (tcp_allowed_connection == NULL)
             {
                 ret = -ENOMEM;
@@ -692,6 +767,7 @@
 #endif
             /* Stash this in the server_configuration_s structure. freed later on */
             svc_config->security = tcp_allowed_connection;
+            svc_config->security_dtor = &dealloc_trusted_connection_info;
             ret = 0;
             /* Fill up the list of allowed ports */
             PINT_config_get_allowed_ports(svc_config, 
@@ -713,40 +789,35 @@
                 }
             }
             ret = 0;
-            /* Retrieve the BMI network address and port */
-            PINT_config_get_allowed_network(svc_config,
+            /* Retrieve the list of BMI network addresses and masks  */
+            PINT_config_get_allowed_networks(svc_config,
                     &tcp_allowed_connection->network_enforce,
-                    &bmi_network, &bmi_netmask);
+                    &bmi_networks_count,
+                    &bmi_networks,
+                    &bmi_netmasks);
 
             /* if it was enabled, make sure that we know how to deal with it */
             if (tcp_allowed_connection->network_enforce == 1)
             {
-                char *tcp_string = NULL;
-                /* Convert the network string into an in_addr_t structure */
-                tcp_string = string_key("tcp", bmi_network);
-                if (!tcp_string)
+                int i;
+
+                for (i = 0; i < bmi_networks_count; i++)
                 {
-                    /* the string doesn't even have our info */
-                    gossip_lerr("Error: malformed tcp network address\n");
-                    ret = bmi_tcp_errno_to_pvfs(-EINVAL);
-                }
-                else {
-                    /* convert this into an in_addr_t */
-                    inet_aton(tcp_string, &tcp_allowed_connection->network);
-                    free(tcp_string);
-                    /* Do the same for the netmask as well */
-                    tcp_string = string_key("tcp", bmi_netmask);
+                    char *tcp_string = NULL;
+                    /* Convert the network string into an in_addr_t structure */
+                    tcp_string = string_key("tcp", bmi_networks[i]);
                     if (!tcp_string)
                     {
                         /* the string doesn't even have our info */
-                        gossip_lerr("Error: malformed tcp netmask\n");
+                        gossip_lerr("Error: malformed tcp network address\n");
                         ret = bmi_tcp_errno_to_pvfs(-EINVAL);
                     }
                     else {
                         /* convert this into an in_addr_t */
-                        inet_aton(tcp_string, &tcp_allowed_connection->netmask);
+                        inet_aton(tcp_string, &tcp_allowed_connection->network[i]);
                         free(tcp_string);
                     }
+                    convert_mask(bmi_netmasks[i], &tcp_allowed_connection->netmask[i]);
                 }
                 /* don't enforce anything if there were any errors */
                 if (ret != 0)
@@ -1477,6 +1548,152 @@
     return(0);
 }
 
+/*
+ * For now, we only support wildcard strings that are IP addresses
+ * and not *hostnames*!
+ */
+static int check_valid_wildcard(const char *wildcard_string, unsigned long *octets)
+{
+    int i, len = strlen(wildcard_string), last_dot = -1, octet_count = 0;
+    char str[16];
+    for (i = 0; i < len; i++)
+    {
+        char c = wildcard_string[i];
+        memset(str, 0, 16);
+        if ((c < '0' || c > '9') && c != '*' && c != '.')
+            return -EINVAL;
+        if (c == '*') {
+            if (octet_count >= 4)
+                return -EINVAL;
+            octets[octet_count++] = 256;
+        }
+        else if (c == '.')
+        {
+            char *endptr = NULL;
+            if (octet_count >= 4)
+                return -EINVAL;
+            strncpy(str, &wildcard_string[last_dot + 1], (i - last_dot - 1));
+            octets[octet_count++] = strtol(str, &endptr, 10);
+            if (*endptr != '\0' || octets[octet_count-1] >= 256)
+                return -EINVAL;
+            last_dot = i;
+        }
+    }
+    for (i = octet_count; i < 4; i++)
+    {
+         octets[i] = 256;
+    }
+    return 0;
+}
+
+/*
+ * return 1 if the addr specified is part of the wildcard specification of octet
+ * return 0 otherwise.
+ */
+static int check_octets(struct in_addr addr, unsigned long *octets)
+{
+#define B1_MASK  0xff000000
+#define B1_SHIFT 24
+#define B2_MASK  0x00ff0000
+#define B2_SHIFT 16
+#define B3_MASK  0x0000ff00
+#define B3_SHIFT 8
+#define B4_MASK  0x000000ff
+    uint32_t host_addr = ntohl(addr.s_addr);
+    /* * stands for all clients */
+    if (octets[0] == 256)
+    {
+        return 1;
+    }
+    if (((host_addr & B1_MASK) >> B1_SHIFT) != octets[0])
+    {
+        return 0;
+    }
+    if (octets[1] == 256)
+    {
+        return 1;
+    }
+    if (((host_addr & B2_MASK) >> B2_SHIFT) != octets[1])
+    {
+        return 0;
+    }
+    if (octets[2] == 256)
+    {
+        return 1;
+    }
+    if (((host_addr & B3_MASK) >> B3_SHIFT) != octets[2])
+    {
+        return 0;
+    }
+    if (octets[3] == 256)
+    {
+        return 1;
+    }
+    if ((host_addr & B4_MASK) != octets[3])
+    {
+        return 0;
+    }
+    return 1;
+#undef B1_MASK
+#undef B1_SHIFT 
+#undef B2_MASK 
+#undef B2_SHIFT
+#undef B3_MASK
+#undef B3_SHIFT
+#undef B4_MASK
+}
+/* BMI_tcp_query_addr_range()
+ * Check if a given address is within the network specified by the wildcard string!
+ * or if it is part of the subnet mask specified
+ */
+int BMI_tcp_query_addr_range(method_addr_p map, const char *wildcard_string, int netmask)
+{
+    struct tcp_addr *tcp_addr_data = map->method_data;
+    struct sockaddr_in map_addr;
+    socklen_t map_addr_len = sizeof(map_addr);
+    char *tcp_wildcard = (char *) wildcard_string + 6 /* strlen("tcp://") */;
+
+    memset(&map_addr, 0, sizeof(map_addr));
+    getsockname(tcp_addr_data->socket, (struct sockaddr *) &map_addr, &map_addr_len);
+    /* Wildcard specification */
+    if (netmask == -1)
+    {
+        unsigned long octets[4];
+        if (check_valid_wildcard(tcp_wildcard, octets) < 0)
+        {
+            gossip_lerr("Invalid wildcard specification: %s\n", tcp_wildcard);
+            return -EINVAL;
+        }
+        gossip_debug(GOSSIP_BMI_DEBUG_TCP, "Map Address is : %s, Wildcard Octets: %lu.%lu.%lu.%lu\n", inet_ntoa(map_addr.sin_addr),
+                octets[0], octets[1], octets[2], octets[3]);
+        if (check_octets(map_addr.sin_addr, octets) == 1)
+        {
+            return 1;
+        }
+    }
+    /* Netmask specification */
+    else {
+        struct sockaddr_in mask_addr, network_addr;
+        memset(&mask_addr, 0, sizeof(mask_addr));
+        memset(&network_addr, 0, sizeof(network_addr));
+        /* Convert the netmask address */
+        convert_mask(netmask, &mask_addr.sin_addr);
+        /* Invalid network address */
+        if (inet_aton(tcp_wildcard, &network_addr.sin_addr) == 0)
+        {
+            gossip_lerr("Invalid network specification: %s\n", tcp_wildcard);
+            return -EINVAL;
+        }
+        /* Matches the subnet mask! */
+        if ((map_addr.sin_addr.s_addr & mask_addr.sin_addr.s_addr)
+                == network_addr.sin_addr.s_addr)
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
 /* BMI_tcp_addr_rev_lookup_unexpected()
  *
  * looks up an address that was initialized unexpectedly and returns a string
@@ -3068,9 +3285,14 @@
     static unsigned short my_requested_port = 1023;
     unsigned short my_local_port = 0;
     struct sockaddr_in my_local_sockaddr;
-    int len = sizeof(struct sockaddr_in);
+    socklen_t len = sizeof(struct sockaddr_in);
     memset(&my_local_sockaddr, 0, sizeof(struct sockaddr_in));
 
+    /* setup for a fast restart to avoid bind addr in use errors */
+    if (BMI_sockio_set_sockopt(tcp_addr_data->socket, SO_REUSEADDR, 1) < 0)
+    {
+        gossip_lerr("Could not set SO_REUSEADDR on local socket (port %hd)\n", my_local_port);
+    }
     if (BMI_sockio_bind_sock(tcp_addr_data->socket, my_requested_port) < 0)
     {
         gossip_lerr("Could not bind to local port %hd: %s\n", 
@@ -3086,11 +3308,6 @@
         my_local_port = ntohs(my_local_sockaddr.sin_port);
     }
     gossip_debug(GOSSIP_BMI_DEBUG_TCP, "Bound locally to port: %hd\n", my_local_port);
-    /* setup for a fast restart to avoid bind addr in use errors */
-    if (BMI_sockio_set_sockopt(tcp_addr_data->socket, SO_REUSEADDR, 1) < 0)
-    {
-        gossip_lerr("Could not set SO_REUSEADDR on local socket (port %hd)\n", my_local_port);
-    }
     return 0;
 }
 
@@ -3114,7 +3331,7 @@
 {
     char *peer_hostname = inet_ntoa(peer_sockaddr->sin_addr);
     unsigned short peer_port = ntohs(peer_sockaddr->sin_port);
-    int   what_failed   = -1;
+    int   i, what_failed   = -1;
 
     /* Don't refuse connects if there were any
      * parse errors or if it is not enabled in the config file
@@ -3132,12 +3349,20 @@
         {
             goto port_check;
         }
-        /* check the masks */
-        if ((peer_sockaddr->sin_addr.s_addr & gtcp_allowed_connection->netmask.s_addr)
-                != gtcp_allowed_connection->network.s_addr)
+        for (i = 0; i < gtcp_allowed_connection->network_count; i++)
         {
-            what_failed = 0;
+            /* check with all the masks */
+            if ((peer_sockaddr->sin_addr.s_addr & gtcp_allowed_connection->netmask[i].s_addr) 
+                    != gtcp_allowed_connection->network[i].s_addr)
+            {
+                continue;
+            }
+            else {
+                goto port_check;
+            }
         }
+        /* not from a trusted network */
+        what_failed = 0;
     }
 port_check:
     /* make sure that the client port numbers are within specified limits */
Index: src/io/trove/trove.h
===================================================================
RCS file: /anoncvs/pvfs2/src/io/trove/trove.h,v
retrieving revision 1.28
diff -u -r1.28 trove.h
--- src/io/trove/trove.h	1 Aug 2005 22:49:50 -0000	1.28
+++ src/io/trove/trove.h	30 Dec 2005 19:09:12 -0000
@@ -56,6 +56,13 @@
     TROVE_ONLYOVERWRITE = 16, /* keyval_write and keyval_write_list */
 };
 
+enum
+{
+    TROVE_EXP_ROOT_SQUASH = 1,
+    TROVE_EXP_READ_ONLY   = 2,
+    TROVE_EXP_ALL_SQUASH  = 4,
+};
+
 /* get/setinfo option flags */
 enum
 {
Index: src/kernel/linux-2.6/acl.c
===================================================================
RCS file: /anoncvs/pvfs2/src/kernel/linux-2.6/acl.c,v
retrieving revision 1.8
diff -u -r1.8 acl.c
--- src/kernel/linux-2.6/acl.c	28 Nov 2005 22:15:30 -0000	1.8
+++ src/kernel/linux-2.6/acl.c	30 Dec 2005 19:09:12 -0000
@@ -547,9 +547,9 @@
     return ret;
 #else
     /* We sort of duplicate the code below from generic_permission. */
-    int mode = inode->i_mode;
     int error;
-
+    int mode;
+    mode = inode->i_mode;
     pvfs2_print("pvfs2_permission: mask = %x mode = %x current->fsuid = %x, inode->i_uid = %x\n",
             mask, mode, current->fsuid, inode->i_uid);
 
Index: src/kernel/linux-2.6/pvfs2-kernel.h
===================================================================
RCS file: /anoncvs/pvfs2/src/kernel/linux-2.6/pvfs2-kernel.h,v
retrieving revision 1.108
diff -u -r1.108 pvfs2-kernel.h
--- src/kernel/linux-2.6/pvfs2-kernel.h	20 Dec 2005 15:33:57 -0000	1.108
+++ src/kernel/linux-2.6/pvfs2-kernel.h	30 Dec 2005 19:09:13 -0000
@@ -355,6 +355,11 @@
      * requires the file system to honor acl's 
      */
     int acl;
+    /** suid option (if set) is inspired by the nfs mount option
+    * that requires the file system to honor the setuid bit of a 
+    * file if set. NOTE: this is disabled by default.
+    */
+    int suid;
 } pvfs2_mount_options_t;
 
 /** per superblock private pvfs2 info */
@@ -904,6 +909,9 @@
 #define get_acl_flag(inode)                               \
 (PVFS2_SB(inode->i_sb)->mnt_options.acl)
 
+#define get_suid_flag(inode)                              \
+(PVFS2_SB(inode->i_sb)->mnt_options.suid)
+
 #ifdef USE_MMAP_RA_CACHE
 #define clear_inode_mmap_ra_cache(inode)                  \
 do {                                                      \
@@ -985,7 +993,7 @@
     sys_attr.ctime =                              \
       pvfs2_convert_time_field((void *)&cur_time);\
     sys_attr.size = 0;                            \
-    sys_attr.perms = PVFS2_translate_mode(mode);  \
+    sys_attr.perms = PVFS2_translate_mode(mode,0);  \
     sys_attr.objtype = type;                      \
     sys_attr.mask = PVFS_ATTR_SYS_ALL_SETABLE;    \
 } while(0)
@@ -1016,7 +1024,7 @@
     sys_attr.ctime =                              \
       pvfs2_convert_time_field((void *)&cur_time);\
     sys_attr.size = 0;                            \
-    sys_attr.perms = PVFS2_translate_mode(mode);  \
+    sys_attr.perms = PVFS2_translate_mode(mode,0);  \
     sys_attr.objtype = type;                      \
     sys_attr.mask = PVFS_ATTR_SYS_ALL_SETABLE;    \
 } while(0)
Index: src/kernel/linux-2.6/pvfs2-utils.c
===================================================================
RCS file: /anoncvs/pvfs2/src/kernel/linux-2.6/pvfs2-utils.c,v
retrieving revision 1.113
diff -u -r1.113 pvfs2-utils.c
--- src/kernel/linux-2.6/pvfs2-utils.c	2 Dec 2005 22:15:46 -0000	1.113
+++ src/kernel/linux-2.6/pvfs2-utils.c	30 Dec 2005 19:09:14 -0000
@@ -166,6 +166,9 @@
 
         if (attrs->perms & PVFS_G_SGID)
             perm_mode |= S_ISGID;
+        /* Should we honor the suid bit of the file? */
+        if (get_suid_flag(inode) == 1 && (attrs->perms & PVFS_U_SUID))
+            perm_mode |= S_ISUID;
 
         inode->i_mode |= perm_mode;
         /* NOTE: this will change once we move from the iget() model to the
@@ -234,9 +237,10 @@
 
 static inline void convert_attribute_mode_to_pvfs_sys_attr(
     int mode,
-    PVFS_sys_attr *attrs)
+    PVFS_sys_attr *attrs,
+    int suid)
 {
-    attrs->perms = PVFS2_translate_mode(mode);
+    attrs->perms = PVFS2_translate_mode(mode, suid);
     attrs->mask |= PVFS_ATTR_SYS_PERM;
 
     pvfs2_print("mode is %d | translated perms is %d\n", mode,
@@ -297,26 +301,26 @@
         if (iattr && (iattr->ia_valid & ATTR_MODE))
         {
             pvfs2_print("[1] converting attr mode %d\n", iattr->ia_mode);
-            if((iattr->ia_mode & (S_ISUID|S_ISVTX)) != 0)
+            if((iattr->ia_mode & S_ISVTX) != 0)
             {
-                pvfs2_print("User attempted to set setuid or sticky bit; "
+                pvfs2_print("User attempted to set sticky bit; "
                     "returning EINVAL.\n");
                 return(-EINVAL);
             }
             convert_attribute_mode_to_pvfs_sys_attr(
-                iattr->ia_mode, attrs);
+                iattr->ia_mode, attrs, get_suid_flag(inode));
         }
         else
         {
             pvfs2_print("[2] converting attr mode %d\n", inode->i_mode); 
-            if((inode->i_mode & (S_ISUID|S_ISVTX)) != 0)
+            if((inode->i_mode & S_ISVTX) != 0)
             {
-                pvfs2_print("User attempted to set setuid or sticky bit; "
+                pvfs2_print("User attempted to set sticky bit; "
                     "returning EINVAL.\n");
                 return(-EINVAL);
             }
             convert_attribute_mode_to_pvfs_sys_attr(
-                inode->i_mode, attrs);
+                inode->i_mode, attrs, get_suid_flag(inode));
         }
 
         /* we carefully selected which bits to set in attrs->mask above, so
Index: src/kernel/linux-2.6/super.c
===================================================================
RCS file: /anoncvs/pvfs2/src/kernel/linux-2.6/super.c,v
retrieving revision 1.66
diff -u -r1.66 super.c
--- src/kernel/linux-2.6/super.c	2 Dec 2005 21:56:15 -0000	1.66
+++ src/kernel/linux-2.6/super.c	30 Dec 2005 19:09:15 -0000
@@ -31,8 +31,8 @@
     pvfs2_sb_info_t *pvfs2_sb = NULL;
     int i = 0, j = 0, num_keywords = 0, got_device = 0;
 
-    static char *keywords[] = {"intr", "acl"};
-    static int num_possible_keywords = 2;
+    static char *keywords[] = {"intr", "acl", "suid"};
+    static int num_possible_keywords = 3;
     static char options[PVFS2_MAX_NUM_OPTIONS][PVFS2_MAX_MOUNT_OPT_LEN];
 
     if (!silent)
@@ -117,6 +117,16 @@
                                         "acl specified\n");
                         }
                         pvfs2_sb->mnt_options.acl = 1;
+                        break;
+                    }
+                    else if (strncmp(options[i], "suid", 4) == 0)
+                    {
+                        if (!silent)
+                        {
+                            pvfs2_print("pvfs2: mount option "
+                                        "suid specified\n");
+                        }
+                        pvfs2_sb->mnt_options.suid = 1;
                         break;
                     }
                 }
Index: src/server/prelude.sm
===================================================================
RCS file: /anoncvs/pvfs2/src/server/prelude.sm,v
retrieving revision 1.57
diff -u -r1.57 prelude.sm
--- src/server/prelude.sm	11 Nov 2005 21:31:09 -0000	1.57
+++ src/server/prelude.sm	30 Dec 2005 19:09:16 -0000
@@ -156,6 +156,275 @@
     return ret;
 }
 
+static void get_fs_intent(struct PVFS_server_req *req, PVFS_fs_id *fsid, int *read_only)
+{
+    if (req == NULL)
+    {
+        *fsid = PVFS_FS_ID_NULL;
+        *read_only = -1;
+        return;
+    }
+    switch (req->op)
+    {
+        case PVFS_SERV_CREATE:
+            *fsid = req->u.create.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_REMOVE:
+            *fsid = req->u.remove.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_IO:
+            *fsid = req->u.io.fs_id;
+            *read_only = (req->u.io.io_type == PVFS_IO_READ) ? 1 : 0;
+            break;
+        case PVFS_SERV_GETATTR:
+            *fsid = req->u.getattr.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_SETATTR:
+            *fsid = req->u.setattr.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_LOOKUP_PATH:
+            *fsid = req->u.lookup_path.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_CRDIRENT:
+            *fsid = req->u.crdirent.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_RMDIRENT:
+            *fsid = req->u.rmdirent.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_CHDIRENT:
+            *fsid = req->u.chdirent.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_TRUNCATE:
+            *fsid = req->u.truncate.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_MKDIR:
+            *fsid = req->u.mkdir.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_READDIR:
+            *fsid = req->u.readdir.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_FLUSH:
+            *fsid = req->u.flush.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_MGMT_SETPARAM:
+            *fsid = req->u.mgmt_setparam.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_STATFS:
+            *fsid = req->u.statfs.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_MGMT_ITERATE_HANDLES:
+            *fsid = req->u.mgmt_iterate_handles.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_MGMT_DSPACE_INFO_LIST:
+            *fsid = req->u.mgmt_dspace_info_list.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_MGMT_REMOVE_OBJECT:
+            *fsid = req->u.mgmt_remove_object.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_MGMT_REMOVE_DIRENT:
+            *fsid = req->u.mgmt_remove_dirent.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_MGMT_GET_DIRDATA_HANDLE:
+            *fsid = req->u.mgmt_get_dirdata_handle.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_GETEATTR:
+            *fsid = req->u.geteattr.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_SETEATTR:
+            *fsid = req->u.seteattr.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_DELEATTR:
+            *fsid = req->u.deleattr.fs_id;
+            *read_only = 0;
+            break;
+        case PVFS_SERV_LISTEATTR:
+            *fsid = req->u.listeattr.fs_id;
+            *read_only = 1;
+            break;
+        case PVFS_SERV_PROTO_ERROR:
+        case PVFS_SERV_JOB_TIMER:
+        case PVFS_SERV_MGMT_EVENT_MON:
+        case PVFS_SERV_MGMT_PERF_MON:
+        case PVFS_SERV_PERF_UPDATE:
+        case PVFS_SERV_MGMT_NOOP:
+        case PVFS_SERV_WRITE_COMPLETION:
+        case PVFS_SERV_GETCONFIG:
+        default:
+            *fsid = PVFS_FS_ID_NULL;
+            *read_only = -1;
+            break;
+    }
+    return;
+}
+
+static void get_anon_ids(struct filesystem_configuration_s *fsconfig,
+    PVFS_uid *uid, PVFS_gid *gid)
+{
+    *uid = fsconfig->exp_anon_uid;
+    *gid = fsconfig->exp_anon_gid;
+    return;
+}
+
+static int iterate_all_squash_wildcards(struct filesystem_configuration_s *fsconfig,
+    PVFS_BMI_addr_t client_addr)
+{
+    int i;
+
+    for (i = 0; i < fsconfig->all_squash_count; i++)
+    {
+        gossip_debug(GOSSIP_SERVER_DEBUG, "BMI_query_addr_range %Ld, %s\n",
+            client_addr, fsconfig->all_squash_hosts[i]);
+        if (BMI_query_addr_range(client_addr, fsconfig->all_squash_hosts[i],
+                fsconfig->all_squash_netmasks[i]) == 1)
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int iterate_root_squash_wildcards(struct filesystem_configuration_s *fsconfig,
+    PVFS_BMI_addr_t client_addr)
+{
+    int i;
+
+    for (i = 0; i < fsconfig->root_squash_count; i++)
+    {
+        gossip_debug(GOSSIP_SERVER_DEBUG, "BMI_query_addr_range %Ld, %s\n",
+            client_addr, fsconfig->root_squash_hosts[i]);
+        if (BMI_query_addr_range(client_addr, fsconfig->root_squash_hosts[i], 
+                fsconfig->root_squash_netmasks[i]) == 1)
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* Translate_ids will return 1 if it did some uid/gid squashing, 0 otherwise */
+static int translate_ids(PVFS_fs_id fsid, PVFS_uid uid, PVFS_gid gid, 
+    PVFS_uid *translated_uid, PVFS_gid *translated_gid, PVFS_BMI_addr_t client_addr)
+{
+    int exp_flags = 0;
+    struct server_configuration_s *serv_config = NULL;
+    struct filesystem_configuration_s * fsconfig = NULL;
+
+    serv_config = PINT_get_server_config();
+    fsconfig = PINT_config_find_fs_id(serv_config, fsid);
+
+    if (fsconfig == NULL)
+    {
+        return 0;
+    }
+    exp_flags = fsconfig->exp_flags;
+    /* If all squash was set */
+    if (exp_flags & TROVE_EXP_ALL_SQUASH)
+    {
+        if (iterate_all_squash_wildcards(fsconfig, client_addr) == 1)
+        {
+            get_anon_ids(fsconfig, translated_uid, translated_gid);
+            gossip_debug(GOSSIP_SERVER_DEBUG,
+                "Translated ids from <%u:%u> to <%u:%u>\n",
+                uid, gid, *translated_uid, *translated_gid);
+            return 1;
+        }
+    }
+    /* if only root squash was set translate uids for root alone*/
+    if (exp_flags & TROVE_EXP_ROOT_SQUASH)
+    {
+        if (uid == 0 || gid == 0)
+        {
+            if (iterate_root_squash_wildcards(fsconfig, client_addr) == 1)
+            {
+                get_anon_ids(fsconfig, translated_uid, translated_gid);
+                gossip_debug(GOSSIP_SERVER_DEBUG,
+                    "Translated ids from <%u:%u> to <%u:%u>\n",
+                    uid, gid, *translated_uid, *translated_gid);
+                return 1;
+            }
+        }
+    }
+    /* no such translation required! */
+    *translated_uid = uid;
+    *translated_gid = gid;
+    return 0;
+}
+
+static int iterate_ro_wildcards(struct filesystem_configuration_s *fsconfig, PVFS_BMI_addr_t client_addr)
+{
+    int i;
+
+    for (i = 0; i < fsconfig->ro_count; i++)
+    {
+        gossip_debug(GOSSIP_SERVER_DEBUG, "BMI_query_addr_range %Ld, %s\n",
+            client_addr, fsconfig->ro_hosts[i]);
+        /* Does the client address match the wildcard specification and/or the netmask specification? */
+        if (BMI_query_addr_range(client_addr, fsconfig->ro_hosts[i],
+                fsconfig->ro_netmasks[i]) == 1)
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int permit_operation(PVFS_fs_id fsid, int read_only, PVFS_BMI_addr_t client_addr)
+{ 
+    int exp_flags = 0; 
+    struct server_configuration_s *serv_config = NULL;
+    struct filesystem_configuration_s * fsconfig = NULL;
+
+    if (read_only == 1)
+    {
+        return 0;
+    }
+    serv_config = PINT_get_server_config();
+    fsconfig = PINT_config_find_fs_id(serv_config, fsid);
+
+    if (fsconfig == NULL)
+    {
+        return 0;
+    }
+    exp_flags = fsconfig->exp_flags;
+
+    /* cheap test to see if ReadOnly was even specified in the exportoptions */
+    if (!(exp_flags & TROVE_EXP_READ_ONLY))
+    {
+        return 0;
+    }
+    /* Drat. Iterate thru the list of wildcards specified in server_configuration and see
+     * the client address matches. if yes, then we deny permission
+     */
+    if (iterate_ro_wildcards(fsconfig, client_addr) == 1)
+    {
+        gossip_debug(GOSSIP_SERVER_DEBUG, 
+            "Disallowing read-write operation on a read-only exported file-system\n");
+        return -EROFS;
+    }
+    return 0;
+}
+
 /* prelude_perm_check()
  *
  * this really just marks the spot where we would want to do
@@ -167,6 +436,10 @@
 {
     PVFS_object_attr *obj_attr = NULL;
     PVFS_ds_attributes *ds_attr = NULL;
+    PVFS_uid translated_uid = s_op->req->credentials.uid;
+    PVFS_gid translated_gid = s_op->req->credentials.gid;
+    PVFS_fs_id  fsid;
+    int  rdonly = -1;
 
     /* moved gossip server debug output to end of state, so we can report
      * resulting status value.
@@ -181,6 +454,8 @@
     PVFS_ds_attr_to_object_attr(ds_attr, obj_attr);
     s_op->attr.mask = PVFS_ATTR_COMMON_ALL;
 
+    get_fs_intent(s_op->req, &fsid, &rdonly);
+
     /* the next thing we need to do is interpret the error code from
      * reading the attributes.  Normally it is an error if that step
      * failed, but we have to look for the special case in which we
@@ -192,7 +467,33 @@
     {
         js_p->error_code = 0;
     }
-
+    if (fsid != PVFS_FS_ID_NULL)
+    {
+        /*
+         * if we are exporting a volume readonly, disallow any operation that modifies
+         * the state of the file-system.
+         */
+        if (permit_operation(fsid, rdonly, s_op->addr) < 0)
+        {
+            js_p->error_code = -PVFS_EROFS;
+            return 1;
+        }
+        else {
+            /* Translate the uid and gid's in case we need to do some squashing based on the export and the client address */
+            if (translate_ids(fsid, s_op->req->credentials.uid, s_op->req->credentials.gid,
+                &translated_uid, &translated_gid, s_op->addr) == 1)
+            {
+                s_op->req->credentials.uid = translated_uid;
+                s_op->req->credentials.gid = translated_gid;
+                /* in the case of a setattr, translate the ids as well right here */
+                if (s_op->req->op == PVFS_SERV_SETATTR)
+                {
+                    s_op->req->u.setattr.attr.owner = translated_uid;
+                    s_op->req->u.setattr.attr.group = translated_gid;
+                }
+            }
+        }
+    }
     /* anything else we treat as a real error */
     if (js_p->error_code)
     {
@@ -200,6 +501,7 @@
         return(1);
     }
 
+
     gossip_debug(
         GOSSIP_PERMISSIONS_DEBUG, "PVFS operation \"%s\" got "
         "attr mask %d\n\t(attr_uid_valid? %s, attr_owner = "
@@ -207,32 +509,32 @@
         "%d, credentials.gid = %d)\n",
         PINT_map_server_op_to_string(s_op->req->op), s_op->attr.mask,
         ((s_op->attr.mask & PVFS_ATTR_COMMON_UID) ? "yes" : "no"),
-        s_op->attr.owner, s_op->req->credentials.uid,
+        s_op->attr.owner, translated_uid,
         ((s_op->attr.mask & PVFS_ATTR_COMMON_GID) ? "yes" : "no"),
-        s_op->attr.group, s_op->req->credentials.gid);
+        s_op->attr.group, translated_gid);
     
     switch(PINT_server_req_table[s_op->req->op].perm)
     {
         case PINT_SERVER_CHECK_WRITE:
             js_p->error_code = PINT_check_mode(
-                &(s_op->attr), s_op->req->credentials.uid,
-                s_op->req->credentials.gid, PINT_ACCESS_WRITABLE);
+                &(s_op->attr), translated_uid,
+                translated_gid, PINT_ACCESS_WRITABLE);
             break;
         case PINT_SERVER_CHECK_READ:
             js_p->error_code = PINT_check_mode(
-                &(s_op->attr), s_op->req->credentials.uid,
-                s_op->req->credentials.gid, PINT_ACCESS_READABLE);
+                &(s_op->attr), translated_uid,
+                translated_gid, PINT_ACCESS_READABLE);
             break;
         case PINT_SERVER_CHECK_CRDIRENT:
             /* must also check executable after writable */
             js_p->error_code = PINT_check_mode(
-                &(s_op->attr), s_op->req->credentials.uid,
-                s_op->req->credentials.gid, PINT_ACCESS_WRITABLE);
+                &(s_op->attr), translated_uid,
+                translated_gid, PINT_ACCESS_WRITABLE);
             if(js_p->error_code == 0)
             {
                 js_p->error_code = PINT_check_mode(
-                    &(s_op->attr), s_op->req->credentials.uid,
-                    s_op->req->credentials.gid, PINT_ACCESS_EXECUTABLE);
+                    &(s_op->attr), translated_uid,
+                    translated_gid, PINT_ACCESS_EXECUTABLE);
             }
             break;
         case PINT_SERVER_CHECK_ATTR:
@@ -263,11 +565,11 @@
                 */
                 if (((s_op->attr.mask & PVFS_ATTR_COMMON_UID) &&
                      ((s_op->attr.owner == 0) ||
-                      (s_op->attr.owner == s_op->req->credentials.uid))) ||
+                      (s_op->attr.owner == translated_uid))) ||
                     (((s_op->attr.mask & PVFS_ATTR_COMMON_GID) &&
                       ((s_op->attr.group == 0) ||
-                       (s_op->attr.group == s_op->req->credentials.gid)))) ||
-                    (s_op->req->credentials.uid == 0))
+                       (s_op->attr.group == translated_gid)))) ||
+                    (translated_uid == 0))
                 {
                     js_p->error_code = 0;
                 }
Index: src/server/pvfs2-server.c
===================================================================
RCS file: /anoncvs/pvfs2/src/server/pvfs2-server.c,v
retrieving revision 1.198
diff -u -r1.198 pvfs2-server.c
--- src/server/pvfs2-server.c	20 Dec 2005 00:08:30 -0000	1.198
+++ src/server/pvfs2-server.c	30 Dec 2005 19:09:17 -0000
@@ -385,6 +385,11 @@
         &pvfs2_small_io_sm}
 };
 
+struct server_configuration_s *PINT_get_server_config(void)
+{
+    return &server_config;
+}
+
 int main(int argc, char **argv)
 {
     int ret = -1, siglevel = 0;
@@ -1113,6 +1118,14 @@
                          "for %s: %s\n", cur_fs->file_system_name,
                          ((cur_fs->trove_sync_data == TROVE_SYNC) ?
                           "yes" : "no"));
+
+            gossip_debug(GOSSIP_SERVER_DEBUG, "Export options for "
+                         "%s:\n RootSquash %s\n AllSquash %s\n ReadOnly %s\n"
+                         " AnonUID %u\n AnonGID %u\n", cur_fs->file_system_name,
+                         (cur_fs->exp_flags & TROVE_EXP_ROOT_SQUASH) ? "yes" : "no",
+                         (cur_fs->exp_flags & TROVE_EXP_ALL_SQUASH)  ? "yes" : "no",
+                         (cur_fs->exp_flags & TROVE_EXP_READ_ONLY)   ? "yes" : "no",
+                         cur_fs->exp_anon_uid, cur_fs->exp_anon_gid);
 
             /* format and pass sync mode to the flow implementation */
             snprintf(buf, 16, "%d,%d", cur_fs->coll_id,


More information about the PVFS2-developers mailing list