root/branches/cu-security-branch/src/common/misc/pvfs2-util.c @ 8394

Revision 8394, 59.0 KB (checked in by nlmills, 3 years ago)

lots of little fixes that have been in the works for a while

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * Changes by Acxiom Corporation to add relative path support to
5 * PVFS_util_resolve(),
6 * Copyright Acxiom Corporation, 2005
7 *
8 * See COPYING in top-level directory.
9 */
10
11#include <string.h>
12#include <errno.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <ctype.h>
16#include <assert.h>
17#include <unistd.h>
18#include <sys/types.h>
19#include <sys/time.h>
20#include <sys/wait.h>
21#include <errno.h>
22#include <signal.h>
23#include <libgen.h>
24
25#define __PINT_REQPROTO_ENCODE_FUNCS_C
26#include "pvfs2-config.h"
27#include "pvfs2-sysint.h"
28#include "pvfs2-util.h"
29#include "pvfs2-debug.h"
30#include "gossip.h"
31#include "pvfs2-attr.h"
32#include "pvfs2-types-debug.h"
33#include "str-utils.h"
34#include "gen-locks.h"
35#include "realpath.h"
36#include "pint-sysint-utils.h"
37#include "pvfs2-internal.h"
38#include "pint-util.h"
39#include "security-util.h"
40
41#ifdef HAVE_MNTENT_H
42
43#include <mntent.h>
44#define PINT_fstab_t FILE
45#define PINT_fstab_entry_t struct mntent
46#define PINT_fstab_open(_fstab, _fname) (_fstab) = setmntent(_fname, "r")
47#define PINT_fstab_close(_tab) endmntent(_tab)
48#define PINT_fstab_next_entry(_tab) getmntent(_tab)
49#define PINT_fstab_entry_destroy(_entry) _entry = NULL
50#define PINT_fstab_entry_hasopt(_entry, _opt) hasmntopt(_entry, _opt)
51
52#define PINT_FSTAB_NAME(_entry) (_entry)->mnt_fsname
53#define PINT_FSTAB_PATH(_entry) (_entry)->mnt_dir
54#define PINT_FSTAB_TYPE(_entry) (_entry)->mnt_type
55#define PINT_FSTAB_OPTS(_entry) (_entry)->mnt_opts
56
57#elif HAVE_FSTAB_H
58
59#include <fstab.h>
60#define PINT_fstab_t FILE
61#define PINT_fstab_entry_t struct fstab
62#define PINT_fstab_open(_fstab, _fname) _fstab = fopen(_fname, "r")
63#define PINT_fstab_close(_tab) fclose(_tab)
64#define PINT_fstab_next_entry(_tab) PINT_util_my_get_next_fsent(_tab)
65#define PINT_fstab_entry_destroy(_entry) PINT_util_fsent_destroy(_entry)
66#define PINT_fstab_entry_hasopt(_entry, _opt) strstr((_entry)->fs_mntops, _opt)
67
68#define PINT_FSTAB_NAME(_entry) (_entry)->fs_spec
69#define PINT_FSTAB_PATH(_entry) (_entry)->fs_file
70#define PINT_FSTAB_TYPE(_entry) (_entry)->fs_vfstype
71#define PINT_FSTAB_OPTS(_entry) (_entry)->fs_mntops
72
73#define DEFINE_MY_GET_NEXT_FSENT
74static struct fstab * PINT_util_my_get_next_fsent(PINT_fstab_t * tab);
75static void PINT_util_fsent_destroy(PINT_fstab_entry_t * entry);
76
77#else
78
79#error OS does not have mntent.h or fstab.h. 
80#error Add your own fstab parser macros to fix.
81
82#endif
83
84#define PVFS2_MAX_INVALID_MNTENTS                     256
85#define PVFS2_MAX_TABFILES                              8
86#define PVFS2_DYNAMIC_TAB_INDEX  (PVFS2_MAX_TABFILES - 1)
87#define PVFS2_DYNAMIC_TAB_NAME              "<DynamicTab>"
88
89static PVFS_util_tab s_stat_tab_array[PVFS2_MAX_TABFILES];
90static int s_stat_tab_count = 0;
91static gen_mutex_t s_stat_tab_mutex = GEN_MUTEX_INITIALIZER;
92
93static int parse_flowproto_string(
94    const char *input,
95    enum PVFS_flowproto_type *flowproto);
96
97static int parse_encoding_string(
98    const char *cp,
99    enum PVFS_encoding_type *et);
100
101static int parse_num_dfiles_string(const char* cp, int* num_dfiles);
102
103static int PINT_util_resolve_absolute(
104    const char* local_path,
105    PVFS_fs_id* out_fs_id,
106    char* out_fs_path,
107    int out_fs_path_max);
108
109struct PVFS_sys_mntent* PVFS_util_gen_mntent(
110    char* config_server,
111    char* fs_name)
112{
113    struct PVFS_sys_mntent* tmp_ent = NULL;
114
115    tmp_ent = (struct PVFS_sys_mntent*)malloc(sizeof(struct
116        PVFS_sys_mntent));
117    if(!tmp_ent)
118    {
119        return(NULL);
120    }
121    memset(tmp_ent, 0, sizeof(struct PVFS_sys_mntent));
122
123    tmp_ent->num_pvfs_config_servers = 1;
124    tmp_ent->pvfs_config_servers = (char**)malloc(sizeof(char*));
125    if(!tmp_ent->pvfs_config_servers)
126    {
127        free(tmp_ent);
128        return(NULL);
129    }
130
131    tmp_ent->pvfs_config_servers[0] = strdup(config_server);
132    if(!tmp_ent->pvfs_config_servers[0])
133    {
134        free(tmp_ent->pvfs_config_servers);
135        free(tmp_ent);
136        return(NULL);
137    }
138
139    tmp_ent->pvfs_fs_name = strdup(fs_name);
140    if(!tmp_ent->pvfs_fs_name)
141    {
142        free(tmp_ent->pvfs_config_servers[0]);
143        free(tmp_ent->pvfs_config_servers);
144        free(tmp_ent);
145        return(NULL);
146    }
147
148    tmp_ent->flowproto = FLOWPROTO_DEFAULT;
149    tmp_ent->encoding = ENCODING_DEFAULT;
150
151    return(tmp_ent);
152}
153
154void PVFS_util_gen_mntent_release(struct PVFS_sys_mntent* mntent)
155{
156    free(mntent->pvfs_config_servers[0]);
157    free(mntent->pvfs_config_servers);
158    free(mntent->pvfs_fs_name);
159    free(mntent);
160    return;
161}
162
163int PVFS_util_gen_credential_defaults(PVFS_credential *cred)
164{
165    return PVFS_util_gen_credential(NULL, PVFS_DEFAULT_CREDENTIAL_TIMEOUT,
166                                    NULL, cred);
167}
168
169
170int PVFS_util_gen_credential(const char *user, unsigned int timeout,
171    const char *keypath, PVFS_credential *cred)
172{
173    struct sigaction newsa, oldsa;
174    pid_t pid;
175    int filedes[2];
176    int ret;
177
178    memset(&newsa, 0, sizeof(newsa));
179    newsa.sa_handler = SIG_DFL;
180    sigaction(SIGCHLD, &newsa, &oldsa);
181
182    ret = pipe(filedes);
183    if (ret == -1)
184    {
185        return -PVFS_errno_to_error(errno);
186    }
187
188    pid = fork();
189    if (pid == 0)
190    {
191        char *args[7];
192        char **ptr = args;
193        char timearg[16];
194        char *envp[] = { NULL };
195
196        close(STDERR_FILENO);
197        close(STDOUT_FILENO);
198        dup(filedes[1]);
199        close(STDIN_FILENO);
200
201        if (user)
202        {
203            *ptr++ = "-u";
204            *ptr++ = (char*)user;
205        }
206        if (timeout != PVFS_DEFAULT_CREDENTIAL_TIMEOUT)
207        {
208           snprintf(timearg, sizeof(timearg), "%u", timeout);
209           *ptr++ = "-t";
210           *ptr++ = timearg;
211        }
212        if (keypath)
213        {
214            *ptr++ = "-k";
215            *ptr++ = (char*)keypath;
216        }
217        *ptr++ = NULL;
218        execve(BINDIR"/pvfs2-gencred", args, envp);
219
220        _exit(100);
221    }
222    else if (pid == -1)
223    {
224        close(filedes[1]);
225        ret = -PVFS_errno_to_error(errno);
226    }
227    else
228    {
229        char buf[sizeof(PVFS_credential)+extra_size_PVFS_credential];
230        ssize_t total = 0;
231        ssize_t cnt;
232
233        /* close write end so we get EOF when child exits */
234        close(filedes[1]);
235
236        do
237        {
238            do cnt = read(filedes[0], buf+total, (sizeof(buf) - total));
239            while (cnt == -1 && errno == EINTR);
240            total += cnt;
241        } while (cnt > 0);
242       
243        if (cnt == -1)
244        {
245            ret = -PVFS_errno_to_error(errno);
246        }
247        else
248        {
249            int rc;
250
251            waitpid(pid, &rc, 0);
252            if (WIFEXITED(rc) && !WEXITSTATUS(rc))
253            {
254                char *ptr = buf;
255                PVFS_credential tmp;
256
257                decode_PVFS_credential(&ptr, &tmp);
258                ret = PINT_copy_credential(&tmp, cred);
259            }
260            else
261            {
262                /* nlmills: TODO: find a more appropriate error code */
263                ret = -PVFS_EINVAL;
264            }
265        }
266    }
267
268    close(filedes[0]);
269    sigaction(SIGCLD, &oldsa, NULL);
270
271    return ret;
272}
273
274int PVFS_util_refresh_credential(PVFS_credential *cred)
275{
276    int ret;
277
278    /* =if the credential is valid for at least an hour */
279    if (PINT_util_get_current_time() <= cred->timeout - 3600)
280    {
281        ret = 0;
282    }
283    else
284    {
285        PINT_cleanup_credential(cred);
286        ret = PVFS_util_gen_credential_defaults(cred);
287    }
288
289    return ret;
290}
291
292int PVFS_util_get_umask(void)
293{
294    static int mask = 0, set = 0;
295
296    if (set == 0)
297    {
298        mask = (int)umask(0);
299        umask(mask);
300        set = 1;
301    }
302    return mask;
303}
304
305int PVFS_util_copy_sys_attr(
306    PVFS_sys_attr *dest_attr, PVFS_sys_attr *src_attr)
307{
308    int ret = -PVFS_EINVAL;
309
310    if (src_attr && dest_attr)
311    {
312        dest_attr->owner = src_attr->owner;
313        dest_attr->group = src_attr->group;
314        dest_attr->perms = src_attr->perms;
315        dest_attr->atime = src_attr->atime;
316        dest_attr->mtime = src_attr->mtime;
317        dest_attr->ctime = src_attr->ctime;
318        dest_attr->dfile_count = src_attr->dfile_count;
319        dest_attr->objtype = src_attr->objtype;
320        dest_attr->mask = src_attr->mask;
321        dest_attr->flags = src_attr->flags;
322
323        if (src_attr->mask & PVFS_ATTR_SYS_SIZE)
324        {
325            dest_attr->size = src_attr->size;
326        }
327
328        if((src_attr->mask & PVFS_ATTR_SYS_LNK_TARGET) &&
329            src_attr->link_target)
330        {
331            dest_attr->link_target = strdup(src_attr->link_target);
332            if (!dest_attr->link_target)
333            {
334                ret = -PVFS_ENOMEM;
335                return ret;
336            }
337        }
338        else if ((src_attr->mask & PVFS_ATTR_SYS_DIR_HINT))
339        {
340            if (src_attr->dist_name)
341            {
342                dest_attr->dist_name = strdup(src_attr->dist_name);
343                if (dest_attr->dist_name == NULL)
344                {
345                    ret = -PVFS_ENOMEM;
346                    return ret;
347                }
348            }
349            if (src_attr->dist_params)
350            {
351                dest_attr->dist_params = strdup(src_attr->dist_params);
352                if (dest_attr->dist_params == NULL)
353                {
354                    free(dest_attr->dist_name);
355                    ret = -PVFS_ENOMEM;
356                    return ret;
357                }
358            }
359        }
360        ret = 0;
361    }
362    return ret;
363}
364
365void PVFS_util_release_sys_attr(PVFS_sys_attr *attr)
366{
367    if (attr)
368    {
369        if ((attr->mask & PVFS_ATTR_SYS_TYPE) &&
370            (attr->objtype == PVFS_TYPE_SYMLINK) && attr->link_target)
371        {
372            free(attr->link_target);
373            attr->link_target = NULL;
374        }
375        else if ((attr->mask & PVFS_ATTR_SYS_DIR_HINT) &&
376            (attr->objtype == PVFS_TYPE_DIRECTORY))
377        {
378            if (attr->dist_name)
379                free(attr->dist_name);
380            if (attr->dist_params)
381                free(attr->dist_params);
382            attr->dist_name = NULL;
383            attr->dist_params = NULL;
384        }
385    }
386}
387
388/* PVFS_util_parse_pvfstab()
389 *
390 * parses either the file pointed to by the PVFS2TAB_FILE env
391 * variable, or /etc/fstab, or /etc/pvfs2tab or ./pvfs2tab to extract
392 * pvfs2 mount entries.
393 *
394 * NOTE: if tabfile argument is given at runtime to specify which
395 * tabfile to use, then that will be the _only_ file searched for
396 * pvfs2 entries.
397 *
398 * example entry:
399 * tcp://localhost:3334/pvfs2-fs /mnt/pvfs2 pvfs2 defaults 0 0
400 *
401 * returns const pointer to internal tab structure on success, NULL on
402 * failure
403 */
404const PVFS_util_tab *PVFS_util_parse_pvfstab(
405    const char *tabfile)
406{
407    PINT_fstab_t *mnt_fp = NULL;
408    int file_count = 5;
409    /* NOTE: mtab should be last for clean error logic below */
410    const char *file_list[5] =
411        { NULL, "/etc/fstab", "/etc/pvfs2tab", "pvfs2tab", "/etc/mtab" };
412    const char *targetfile = NULL;
413    PINT_fstab_entry_t *tmp_ent;
414    int i, j;
415    int ret = -1;
416    int tmp_mntent_count = 0;
417    PVFS_util_tab *current_tab = NULL;
418    char *epenv, *tmp;
419
420    if((epenv = getenv("PVFS2EP")) != NULL)
421    {
422        struct PVFS_sys_mntent *mntent;
423        current_tab = &s_stat_tab_array[0];
424        current_tab->mntent_array = malloc(sizeof(struct PVFS_sys_mntent));
425        mntent = &current_tab->mntent_array[0];
426        strcpy(current_tab->tabfile_name, "PVFSEP");
427        current_tab->mntent_count = 1;
428        mntent->pvfs_config_servers = malloc(sizeof(char *));
429        mntent->pvfs_config_servers[0] = strdup(index(epenv, '=') + 1);
430        mntent->num_pvfs_config_servers = 1;
431        mntent->the_pvfs_config_server = mntent->pvfs_config_servers[0];
432        mntent->pvfs_fs_name = strdup(rindex(mntent->the_pvfs_config_server, '/'));
433        mntent->pvfs_fs_name++;
434        mntent->flowproto = FLOWPROTO_DEFAULT;
435        mntent->encoding = ENCODING_DEFAULT;
436        mntent->mnt_dir = strdup(epenv);
437        tmp = index(mntent->mnt_dir, '=');
438        *tmp = 0;
439        mntent->mnt_opts = strdup("rw");
440        return &s_stat_tab_array[0];
441    }
442
443    if (tabfile != NULL)
444    {
445        /*
446          caller wants us to look in a specific location for the
447          tabfile
448        */
449        file_list[0] = tabfile;
450        file_count = 1;
451    }
452    else
453    {
454        /*
455          search the system and env vars for tab files;
456          first check for environment variable override
457        */
458        file_list[0] = getenv("PVFS2TAB_FILE");
459    }
460
461    gen_mutex_lock(&s_stat_tab_mutex);
462
463    /* start by checking list of files we have already parsed */
464    for (i = 0; i < s_stat_tab_count; i++)
465    {
466        for (j = 0; j < file_count; j++)
467        {
468            if (file_list[j] &&
469                !strcmp(file_list[j], s_stat_tab_array[i].tabfile_name))
470            {
471                /* already done */
472                gen_mutex_unlock(&s_stat_tab_mutex);
473                return (&s_stat_tab_array[i]);
474            }
475        }
476    }
477
478    assert(s_stat_tab_count < PVFS2_DYNAMIC_TAB_INDEX);
479
480    /* scan our prioritized list of tab files in order, stop when we
481     * find one that has at least one pvfs2 entry
482     */
483    for (i = 0; (i < file_count && !targetfile); i++)
484    {
485        if(file_list[i])
486        {
487            PINT_fstab_open(mnt_fp, file_list[i]);
488            if (mnt_fp)
489            {
490                while ((tmp_ent = PINT_fstab_next_entry(mnt_fp)))
491                {
492                    if(!(PINT_FSTAB_NAME(tmp_ent)) ||
493                       !(strncmp(PINT_FSTAB_NAME(tmp_ent), "#", 1)))
494                    {
495                        /* this entry is a comment */
496                        PINT_fstab_entry_destroy(tmp_ent);
497                        continue;
498                    }
499
500                    if (strcmp(PINT_FSTAB_TYPE(tmp_ent), "pvfs2") == 0)
501                    {
502                        targetfile = file_list[i];
503                        tmp_mntent_count++;
504                    }
505
506                    PINT_fstab_entry_destroy(tmp_ent);
507                }
508                PINT_fstab_close(mnt_fp);
509            }
510        }
511    }
512
513    if (!targetfile)
514    {
515        gossip_err("Error: could not find any pvfs2 tabfile entries.\n");
516        gossip_err("Error: tried the following tabfiles:\n");
517        for (i = 0; i < file_count; i++)
518        {
519            gossip_err("       %s\n", file_list[i]);
520        }
521        gen_mutex_unlock(&s_stat_tab_mutex);
522        return (NULL);
523    }
524    gossip_debug(GOSSIP_CLIENT_DEBUG,
525                 "Using pvfs2 tab file: %s\n", targetfile);
526
527    /* allocate array of entries */
528    current_tab = &s_stat_tab_array[s_stat_tab_count];
529    current_tab->mntent_array = (struct PVFS_sys_mntent *)malloc(
530        (tmp_mntent_count * sizeof(struct PVFS_sys_mntent)));
531    if (!current_tab->mntent_array)
532    {
533        gen_mutex_unlock(&s_stat_tab_mutex);
534        return (NULL);
535    }
536    memset(current_tab->mntent_array, 0,
537           (tmp_mntent_count * sizeof(struct PVFS_sys_mntent)));
538    for (i = 0; i < tmp_mntent_count; i++)
539    {
540        current_tab->mntent_array[i].fs_id = PVFS_FS_ID_NULL;
541    }
542    current_tab->mntent_count = tmp_mntent_count;
543
544    /* reopen our chosen fstab file */
545    PINT_fstab_open(mnt_fp, targetfile);
546
547    /* scan through looking for every pvfs2 entry */
548    i = 0;
549    while ((tmp_ent = PINT_fstab_next_entry(mnt_fp)))
550    {
551        if (strcmp(PINT_FSTAB_TYPE(tmp_ent), "pvfs2") == 0)
552        {
553            struct PVFS_sys_mntent *me = &current_tab->mntent_array[i];
554            char *cp;
555            int cur_server;
556
557            /* Enable integrity checks by default */
558            me->integrity_check = 1;
559            /* comma-separated list of ways to contact a config server */
560            me->num_pvfs_config_servers = 1;
561            for (cp=PINT_FSTAB_NAME(tmp_ent); *cp; cp++)
562                if (*cp == ',')
563                    ++me->num_pvfs_config_servers;
564
565            /* allocate room for our copies of the strings */
566            me->pvfs_config_servers = malloc(me->num_pvfs_config_servers
567              * sizeof(*me->pvfs_config_servers));
568            if (!me->pvfs_config_servers)
569                goto error_exit;
570            memset(me->pvfs_config_servers, 0,
571              me->num_pvfs_config_servers * sizeof(*me->pvfs_config_servers));
572            me->mnt_dir = malloc(strlen(PINT_FSTAB_PATH(tmp_ent)) + 1);
573            me->mnt_opts = malloc(strlen(PINT_FSTAB_OPTS(tmp_ent)) + 1);
574
575            /* bail if any mallocs failed */
576            if (!me->mnt_dir || !me->mnt_opts)
577            {
578                goto error_exit;
579            }
580
581            /* parse server list and make sure fsname is same */
582            cp = PINT_FSTAB_NAME(tmp_ent);
583            cur_server = 0;
584            for (;;) {
585                char *tok;
586                int slashcount;
587                char *slash;
588                char *last_slash;
589
590                tok = strsep(&cp, ",");
591                if (!tok) break;
592
593                slash = tok;
594                slashcount = 0;
595                while ((slash = index(slash, '/')))
596                {
597                    slash++;
598                    slashcount++;
599                }
600                if (slashcount != 3)
601                {
602                    /* if we are looking at the mtab, then just silently
603                     * treat this error as if we didn't find an entry at
604                     * all; they may have mounted using an odd syntax on a
605                     * 2.4 kernel system
606                     */
607                    if(!strcmp(targetfile, "/etc/mtab"))
608                    {
609                        gossip_err("Error: could not find any pvfs2 tabfile entries.\n");
610                        gossip_err("Error: tried the following tabfiles:\n");
611                        for (j = 0; j < file_count; j++)
612                        {
613                            gossip_err("       %s\n", file_list[j]);
614                        }
615                        goto error_exit;
616                    }
617                    else
618                    {
619                        gossip_err("Error: invalid tab file entry: %s\n",
620                                    PINT_FSTAB_NAME(tmp_ent));
621                        gossip_err("Error: offending tab file: %s\n",
622                                    targetfile);
623                        goto error_exit;
624                    }
625                }
626
627                /* find a reference point in the string */
628                last_slash = rindex(tok, '/');
629                *last_slash = '\0';
630
631                /* config server and fs name are a special case, take one
632                 * string and split it in half on "/" delimiter
633                 */
634                me->pvfs_config_servers[cur_server] = strdup(tok);
635                if (!me->pvfs_config_servers[cur_server])
636                    goto error_exit;
637
638                ++last_slash;
639
640                if (cur_server == 0) {
641                    me->pvfs_fs_name = strdup(last_slash);
642                    if (!me->pvfs_fs_name)
643                        goto error_exit;
644                } else {
645                    if (strcmp(last_slash, me->pvfs_fs_name) != 0) {
646                        gossip_lerr(
647                          "Error: different fs names in server addresses: %s\n",
648                          PINT_FSTAB_NAME(tmp_ent));
649                        goto error_exit;
650                    }
651                }
652                ++cur_server;
653            }
654
655            /* make our own copy of parameters of interest */
656            /* mnt_dir and mnt_opts are verbatim copies */
657            strcpy(current_tab->mntent_array[i].mnt_dir,
658                   PINT_FSTAB_PATH(tmp_ent));
659            strcpy(current_tab->mntent_array[i].mnt_opts,
660                   PINT_FSTAB_OPTS(tmp_ent));
661
662            /* find out if a particular flow protocol was specified */
663            if ((PINT_fstab_entry_hasopt(tmp_ent, "flowproto")))
664            {
665                ret = parse_flowproto_string(
666                    PINT_FSTAB_OPTS(tmp_ent),
667                    &(current_tab->
668                      mntent_array[i].flowproto));
669                if (ret < 0)
670                {
671                    goto error_exit;
672                }
673            }
674            else
675            {
676                current_tab->mntent_array[i].flowproto =
677                    FLOWPROTO_DEFAULT;
678            }
679
680            /* pick an encoding to use with the server */
681            current_tab->mntent_array[i].encoding =
682                ENCODING_DEFAULT;
683            cp = PINT_fstab_entry_hasopt(tmp_ent, "encoding");
684            if (cp)
685            {
686                ret = parse_encoding_string(
687                    cp, &current_tab->mntent_array[i].encoding);
688                if (ret < 0)
689                {
690                    goto error_exit;
691                }
692            }
693
694            /* find out if a particular flow protocol was specified */
695            current_tab->mntent_array[i].default_num_dfiles = 0;
696            cp = PINT_fstab_entry_hasopt(tmp_ent, "num_dfiles");
697            if (cp)
698            {
699                ret = parse_num_dfiles_string(
700                    cp,
701                    &(current_tab->mntent_array[i].default_num_dfiles));
702
703                if (ret < 0)
704                {
705                    goto error_exit;
706                }
707            }
708
709            /* Loop counter increment */
710            i++;
711
712            PINT_fstab_entry_destroy(tmp_ent);
713        }
714    }
715    s_stat_tab_count++;
716    strcpy(s_stat_tab_array[s_stat_tab_count-1].tabfile_name, targetfile);
717    PINT_fstab_close(mnt_fp);
718    gen_mutex_unlock(&s_stat_tab_mutex);
719    return (&s_stat_tab_array[s_stat_tab_count - 1]);
720
721  error_exit:
722    for (; i > -1; i--)
723    {
724        struct PVFS_sys_mntent *me = &current_tab->mntent_array[i];
725
726        if (me->pvfs_config_servers)
727        {
728            int j;
729            for (j=0; j<me->num_pvfs_config_servers; j++)
730                if (me->pvfs_config_servers[j])
731                    free(me->pvfs_config_servers[j]);
732            free(me->pvfs_config_servers);
733            me->pvfs_config_servers = NULL;
734            me->num_pvfs_config_servers = 0;
735        }
736
737        if (me->mnt_dir)
738        {
739            free(me->mnt_dir);
740            me->mnt_dir = NULL;
741        }
742
743        if (me->mnt_opts)
744        {
745            free(me->mnt_opts);
746            me->mnt_opts = NULL;
747        }
748
749        if (me->pvfs_fs_name)
750        {
751            free(me->pvfs_fs_name);
752            me->pvfs_fs_name = NULL;
753        }
754    }
755    PINT_fstab_close(mnt_fp);
756    gen_mutex_unlock(&s_stat_tab_mutex);
757    return (NULL);
758}
759
760/* PVFS_util_get_default_fsid()
761 *
762 * fills in the fs identifier for the first active file system that
763 * the library knows about.  Useful for test programs or admin tools
764 * that need default file system to access if the user has not
765 * specified one
766 *
767 * returns 0 on success, -PVFS_error on failure
768 */
769int PVFS_util_get_default_fsid(PVFS_fs_id* out_fs_id)
770{
771    int i = 0, j = 0;
772
773    gen_mutex_lock(&s_stat_tab_mutex);
774
775    for(i = 0; i < s_stat_tab_count; i++)
776    {
777        for(j = 0; j < s_stat_tab_array[i].mntent_count; j++)
778        {
779            *out_fs_id = s_stat_tab_array[i].mntent_array[j].fs_id;
780            if(*out_fs_id != PVFS_FS_ID_NULL)
781            {
782                gen_mutex_unlock(&s_stat_tab_mutex);
783                return(0);
784            }
785        }
786    }
787
788    /* check the dynamic tab area if we haven't found an fs yet */
789    for(j = 0; j < s_stat_tab_array[
790            PVFS2_DYNAMIC_TAB_INDEX].mntent_count; j++)
791    {
792        *out_fs_id = s_stat_tab_array[
793            PVFS2_DYNAMIC_TAB_INDEX].mntent_array[j].fs_id;
794        if(*out_fs_id != PVFS_FS_ID_NULL)
795        {
796            gen_mutex_unlock(&s_stat_tab_mutex);
797            return(0);
798        }
799    }
800
801    gen_mutex_unlock(&s_stat_tab_mutex);
802    return(-PVFS_ENOENT);
803}
804
805/*
806 * PVFS_util_add_dynamic_mntent()
807 *
808 * dynamically add mount information to our internally managed mount
809 * tables (used for quick fs resolution using PVFS_util_resolve).
810 * dynamic mnt entries can only be added to a particular dynamic
811 * region of our book keeping, so they're the exception, not the rule.
812 *
813 * returns 0 on success, -PVFS_error on failure, and 1 if the mount
814 * entry already exists as a parsed entry (not dynamic)
815 */
816int PVFS_util_add_dynamic_mntent(struct PVFS_sys_mntent *mntent)
817{
818    int i = 0, j = 0, new_index = 0;
819    int ret = -PVFS_EINVAL;
820    struct PVFS_sys_mntent *current_mnt = NULL;
821    struct PVFS_sys_mntent *tmp_mnt_array = NULL;
822
823    if (mntent)
824    {
825        gen_mutex_lock(&s_stat_tab_mutex);
826
827        /*
828          we exhaustively scan to be sure this mnt entry doesn't exist
829          anywhere in our book keeping; first scan the parsed regions
830        */
831        for(i = 0; i < s_stat_tab_count; i++)
832        {
833            for(j = 0; j < s_stat_tab_array[i].mntent_count; j++)
834            {
835                current_mnt = &(s_stat_tab_array[i].mntent_array[j]);
836
837                if (current_mnt->fs_id == mntent->fs_id)
838                {
839                    /*
840                      no need to add the dynamic mount information
841                      because the file system already exists as a
842                      parsed mount entry
843                    */
844                    gen_mutex_unlock(&s_stat_tab_mutex);
845                    return 1;
846                }
847            }
848        }
849
850#if 0
851        /* check the dynamic region if we haven't found a match yet */
852        for(j = 0; j < s_stat_tab_array[
853                PVFS2_DYNAMIC_TAB_INDEX].mntent_count; j++)
854        {
855            current_mnt = &(s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].
856                            mntent_array[j]);
857
858            if ((current_mnt->fs_id == mntent->fs_id) &&
859                (strcmp(current_mnt->pvfs_config_servers[0],
860                        mntent->pvfs_config_servers[0]) != 0))
861            {
862                gossip_err("Error: FS with id %d is already mounted using"
863                           " a different config server.\n", (int)mntent->fs_id);
864                gossip_err("Error: This could indicate that a duplicate fsid"
865                           " is being used.\n");
866                gossip_err("Error: Please check your server configuration.\n");
867                gen_mutex_unlock(&s_stat_tab_mutex);
868                return -PVFS_ENXIO;
869            }
870        }
871#endif
872
873        /* copy the mntent to our table in the dynamic tab area */
874        new_index = s_stat_tab_array[
875            PVFS2_DYNAMIC_TAB_INDEX].mntent_count;
876
877        if (new_index == 0)
878        {
879            /* allocate and initialize the dynamic tab object */
880            s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_array =
881                (struct PVFS_sys_mntent *)malloc(
882                    sizeof(struct PVFS_sys_mntent));
883            if (!s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_array)
884            {
885                return -PVFS_ENOMEM;
886            }
887            strncpy(s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].tabfile_name,
888                    PVFS2_DYNAMIC_TAB_NAME, PVFS_NAME_MAX);
889        }
890        else
891        {
892            /* we need to re-alloc this guy to add a new array entry */
893            tmp_mnt_array = (struct PVFS_sys_mntent *)malloc(
894                ((new_index + 1) * sizeof(struct PVFS_sys_mntent)));
895            if (!tmp_mnt_array)
896            {
897                return -PVFS_ENOMEM;
898            }
899
900            /*
901              copy all mntent entries into the new array, freeing the
902              original entries
903            */
904            for(i = 0; i < new_index; i++)
905            {
906                current_mnt = &s_stat_tab_array[
907                    PVFS2_DYNAMIC_TAB_INDEX].mntent_array[i];
908                PVFS_util_copy_mntent(&tmp_mnt_array[i], current_mnt);
909                PVFS_util_free_mntent(current_mnt);
910            }
911
912            /* finally, swap the mntent arrays */
913            free(s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_array);
914            s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_array =
915                tmp_mnt_array;
916        }
917
918        gossip_debug(GOSSIP_CLIENT_DEBUG, "* Adding new dynamic mount "
919                     "point %s [%d,%d]\n", mntent->mnt_dir,
920                     PVFS2_DYNAMIC_TAB_INDEX, new_index);
921
922        current_mnt = &s_stat_tab_array[
923            PVFS2_DYNAMIC_TAB_INDEX].mntent_array[new_index];
924
925        ret = PVFS_util_copy_mntent(current_mnt, mntent);
926
927        s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_count++;
928
929        gen_mutex_unlock(&s_stat_tab_mutex);
930    }
931    return ret;
932}
933
934/*
935 * PVFS_util_remove_internal_mntent()
936 *
937 * dynamically remove mount information from our internally managed
938 * mount tables.
939 *
940 * returns 0 on success, -PVFS_error on failure
941 */
942int PVFS_util_remove_internal_mntent(
943    struct PVFS_sys_mntent *mntent)
944{
945    int i = 0, j = 0, new_count = 0, found = 0, found_index = 0;
946    int ret = -PVFS_EINVAL;
947    struct PVFS_sys_mntent *current_mnt = NULL;
948    struct PVFS_sys_mntent *tmp_mnt_array = NULL;
949
950    if (mntent)
951    {
952        gen_mutex_lock(&s_stat_tab_mutex);
953
954        /*
955          we exhaustively scan to be sure this mnt entry *does* exist
956          somewhere in our book keeping
957        */
958        for(i = 0; i < s_stat_tab_count; i++)
959        {
960            for(j = 0; j < s_stat_tab_array[i].mntent_count; j++)
961            {
962                current_mnt = &(s_stat_tab_array[i].mntent_array[j]);
963                if ((current_mnt->fs_id == mntent->fs_id)
964                    && (strcmp(current_mnt->mnt_dir, mntent->mnt_dir) == 0))
965                {
966                    found_index = i;
967                    found = 1;
968                    goto mntent_found;
969                }
970            }
971        }
972
973        /* check the dynamic region if we haven't found a match yet */
974        for(j = 0; j < s_stat_tab_array[
975                PVFS2_DYNAMIC_TAB_INDEX].mntent_count; j++)
976        {
977            current_mnt = &(s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].
978                            mntent_array[j]);
979
980            if (current_mnt->fs_id == mntent->fs_id)
981            {
982                found_index = PVFS2_DYNAMIC_TAB_INDEX;
983                found = 1;
984                goto mntent_found;
985            }
986        }
987
988      mntent_found:
989        if (!found)
990        {
991            return -PVFS_EINVAL;
992        }
993
994        gossip_debug(GOSSIP_CLIENT_DEBUG, "* Removing mount "
995                     "point %s [%d,%d]\n", current_mnt->mnt_dir,
996                     found_index, j);
997
998        /* remove the mntent from our table in the found tab area */
999        if ((s_stat_tab_array[found_index].mntent_count - 1) > 0)
1000        {
1001            /*
1002              this is 1 minus the old count since there will be 1 less
1003              mnt entries after this call
1004            */
1005            new_count = s_stat_tab_array[found_index].mntent_count - 1;
1006
1007            /* we need to re-alloc this guy to remove the array entry */
1008            tmp_mnt_array = (struct PVFS_sys_mntent *)malloc(
1009                (new_count * sizeof(struct PVFS_sys_mntent)));
1010            if (!tmp_mnt_array)
1011            {
1012                return -PVFS_ENOMEM;
1013            }
1014
1015            /*
1016              copy all mntent entries into the new array, freeing the
1017              original entries -- and skipping the one that we're
1018              trying to remove
1019            */
1020            for(i = 0, new_count = 0;
1021                i < s_stat_tab_array[found_index].mntent_count; i++)
1022            {
1023                current_mnt = &s_stat_tab_array[found_index].mntent_array[i];
1024
1025                if ((current_mnt->fs_id == mntent->fs_id)
1026                    && (strcmp(current_mnt->mnt_dir, mntent->mnt_dir) == 0))
1027                {
1028                    PVFS_util_free_mntent(current_mnt);
1029                    continue;
1030                }
1031                PVFS_util_copy_mntent(
1032                    &tmp_mnt_array[new_count++], current_mnt);
1033                PVFS_util_free_mntent(current_mnt);
1034            }
1035
1036            /* finally, swap the mntent arrays */
1037            free(s_stat_tab_array[found_index].mntent_array);
1038            s_stat_tab_array[found_index].mntent_array = tmp_mnt_array;
1039
1040            s_stat_tab_array[found_index].mntent_count--;
1041            ret = 0;
1042        }
1043        else
1044        {
1045            /*
1046              special case: we're removing the last mnt entry in the
1047              array here.  since this is the case, we also free the
1048              array since we know it's now empty.
1049            */
1050            PVFS_util_free_mntent(
1051                &s_stat_tab_array[found_index].mntent_array[0]);
1052            free(s_stat_tab_array[found_index].mntent_array);
1053            s_stat_tab_array[found_index].mntent_array = NULL;
1054            s_stat_tab_array[found_index].mntent_count = 0;
1055            ret = 0;
1056        }
1057        gen_mutex_unlock(&s_stat_tab_mutex);
1058    }
1059    return ret;
1060}
1061
1062/*
1063 * PVFS_util_get_mntent_copy()
1064 *
1065 * Given a pointer to a valid mount entry, out_mntent, copy the contents of
1066 * the mount entry  for fs_id into out_mntent.
1067 *
1068 * returns 0 on success, -PVFS_error on failure
1069 */
1070int PVFS_util_get_mntent_copy(PVFS_fs_id fs_id,
1071                              struct PVFS_sys_mntent* out_mntent)
1072{
1073    int i = 0;
1074
1075    /* Search for mntent by fsid */
1076    gen_mutex_lock(&s_stat_tab_mutex);
1077
1078    for(i = 0; i < s_stat_tab_count; i++)
1079    {
1080        int j;
1081        for(j = 0; j < s_stat_tab_array[i].mntent_count; j++)
1082        {
1083            struct PVFS_sys_mntent* mnt_iter;
1084            mnt_iter = &(s_stat_tab_array[i].mntent_array[j]);
1085
1086            if (mnt_iter->fs_id == fs_id)
1087            {
1088                PVFS_util_copy_mntent(out_mntent, mnt_iter);
1089                gen_mutex_unlock(&s_stat_tab_mutex);
1090                return 0;
1091            }
1092        }
1093    }
1094
1095    /* check the dynamic region if we haven't found a match yet */
1096    for (i = 0; i < s_stat_tab_array[
1097             PVFS2_DYNAMIC_TAB_INDEX].mntent_count; i++)
1098    {
1099        struct PVFS_sys_mntent *mnt_iter;
1100        mnt_iter = &(s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].
1101                     mntent_array[i]);
1102
1103        if (mnt_iter->fs_id == fs_id)
1104        {
1105            PVFS_util_copy_mntent(out_mntent, mnt_iter);
1106            gen_mutex_unlock(&s_stat_tab_mutex);
1107            return 0;
1108        }
1109    }
1110
1111    gen_mutex_unlock(&s_stat_tab_mutex);
1112
1113    return -PVFS_EINVAL;
1114}
1115
1116/* PVFS_util_resolve()
1117 *
1118 * given a local path of a file that resides on a pvfs2 volume,
1119 * determine what the fsid and fs relative path is. 
1120 *
1121 * returns 0 on succees, -PVFS_error on failure
1122 */
1123int PVFS_util_resolve(
1124    const char* local_path,
1125    PVFS_fs_id* out_fs_id,
1126    char* out_fs_path,
1127    int out_fs_path_max)
1128{
1129    int ret = -1;
1130    char* tmp_path = NULL;
1131    char* parent_path = NULL;
1132    int base_len = 0;
1133
1134    if(strlen(local_path) > (PVFS_NAME_MAX-1))
1135    {
1136        gossip_err("Error: PVFS_util_resolve() input path too long.\n");
1137        return(-PVFS_ENAMETOOLONG);
1138    }
1139
1140    /* the most common case first; just try to resolve the path that we
1141     * were given
1142     */
1143    ret = PINT_util_resolve_absolute(local_path, out_fs_id, out_fs_path,
1144        out_fs_path_max);
1145    if(ret == 0)
1146    {
1147        /* done */
1148        return(0);
1149    }
1150    if(ret == -PVFS_ENOENT)
1151    {
1152        /* if the path wasn't found, try canonicalizing the path in case it
1153         * refers to a relative path on a mounted volume or contains symlinks
1154         */
1155        tmp_path = (char*)malloc(PVFS_NAME_MAX*sizeof(char));
1156        if(!tmp_path)
1157        {
1158            return(-PVFS_ENOMEM);
1159        }
1160        memset(tmp_path, 0, PVFS_NAME_MAX*sizeof(char));
1161        ret = PINT_realpath(local_path, tmp_path, (PVFS_NAME_MAX-1));
1162        if(ret == -PVFS_EINVAL)
1163        {
1164            /* one more try; canonicalize the parent in case this function
1165             * is called before object creation; the basename
1166             * doesn't yet exist but we still need to find the PVFS volume
1167             */
1168            parent_path = (char*)malloc(PVFS_NAME_MAX*sizeof(char));
1169            if(!parent_path)
1170            {
1171                free(tmp_path);
1172                return(-PVFS_ENOMEM);
1173            }
1174            /* find size of basename so we can reserve space for it */
1175            /* note: basename() and dirname() modifiy args, thus the strcpy */
1176            strcpy(parent_path, local_path);
1177            base_len = strlen(basename(parent_path));
1178            strcpy(parent_path, local_path);
1179            ret = PINT_realpath(dirname(parent_path), tmp_path,
1180                (PVFS_NAME_MAX-base_len-2));
1181            if(ret < 0)
1182            {
1183                free(tmp_path);
1184                free(parent_path);
1185                /* last chance failed; this is not a valid pvfs2 path */
1186                return(-PVFS_ENOENT);
1187            }
1188            /* glue the basename back on */
1189            strcpy(parent_path, local_path);
1190            strcat(tmp_path, "/");
1191            strcat(tmp_path, basename(parent_path));
1192            free(parent_path);
1193        }
1194        else if(ret < 0)
1195        {
1196            /* first canonicalize failed; this is not a valid pvfs2 path */
1197            free(tmp_path);
1198            return(-PVFS_ENOENT);
1199        }
1200
1201        ret = PINT_util_resolve_absolute(tmp_path, out_fs_id, out_fs_path,
1202            out_fs_path_max);
1203        free(tmp_path);
1204
1205        /* fall through and preserve "ret" to be returned */
1206    }
1207
1208    return(ret);
1209}
1210
1211
1212/* PVFS_util_init_defaults()
1213 *
1214 * performs the standard set of initialization steps for the system
1215 * interface, mostly just a wrapper function
1216 *
1217 * returns 0 on success, -PVFS_error on failure
1218 */
1219int PVFS_util_init_defaults(void)
1220{
1221    int ret = -1, i = 0, j = 0, found_one = 0;
1222    int failed_indices[PVFS2_MAX_INVALID_MNTENTS] = {0};
1223
1224    /* use standard system tab files */
1225    const PVFS_util_tab* tab = PVFS_util_parse_pvfstab(NULL);
1226    if (!tab)
1227    {
1228        gossip_err(
1229            "Error: failed to find any pvfs2 file systems in the "
1230            "standard system tab files.\n");
1231        return(-PVFS_ENOENT);
1232    }
1233
1234    /* initialize pvfs system interface */
1235    ret = PVFS_sys_initialize(GOSSIP_NO_DEBUG);
1236    if (ret < 0)
1237    {
1238        return(ret);
1239    }
1240
1241    /* add in any file systems we found in the fstab */
1242    for(i = 0; i < tab->mntent_count; i++)
1243    {
1244        ret = PVFS_sys_fs_add(&tab->mntent_array[i]);
1245        if (ret == 0)
1246        {
1247            found_one = 1;
1248        }
1249        else
1250        {
1251            failed_indices[j++] = i;
1252
1253            if (j > (PVFS2_MAX_INVALID_MNTENTS - 1))
1254            {
1255                gossip_err("*** Failed to initialize %d file systems "
1256                           "from tab file %s.\n ** If this is a valid "
1257                           "tabfile, please remove invalid entries.\n",
1258                           PVFS2_MAX_INVALID_MNTENTS,
1259                           tab->tabfile_name);
1260                gossip_err("Continuing execution without remaining "
1261                           "mount entries\n");
1262               
1263                break;
1264            }
1265        }
1266    }
1267
1268    /* remove any mount entries that couldn't be added here */
1269    for(i = 0; i < PVFS2_MAX_INVALID_MNTENTS; i++)
1270    {
1271        if (failed_indices[i])
1272        {
1273            PVFS_util_remove_internal_mntent(
1274                &tab->mntent_array[failed_indices[i]]);
1275        }
1276        else
1277        {
1278            break;
1279        }
1280    }
1281
1282    if (found_one)
1283    {
1284        return 0;
1285    }
1286
1287    gossip_err("ERROR: could not initialize any file systems "
1288               "in %s.\n", tab->tabfile_name);
1289
1290    PVFS_sys_finalize();
1291    return -PVFS_ENODEV;
1292}
1293
1294/*********************/
1295/* normal size units */
1296/*********************/
1297#define KILOBYTE                1024
1298#define MEGABYTE   (1024 * KILOBYTE)
1299#define GIGABYTE   (1024 * MEGABYTE)
1300#define TERABYTE   (1024llu * GIGABYTE)
1301#define PETABYTE   (1024llu * TERABYTE)
1302#define EXABYTE    (1024llu * PETABYTE)
1303#define ZETTABYTE  (1024llu * EXABYTE)
1304#define YOTTABYTE  (1024llu * ZETTABYTE)
1305
1306/*****************/
1307/* si size units */
1308/*****************/
1309#define SI_KILOBYTE                   1000
1310#define SI_MEGABYTE   (1000 * SI_KILOBYTE)
1311#define SI_GIGABYTE   (1000 * SI_MEGABYTE)
1312#define SI_TERABYTE  (1000llu * SI_GIGABYTE)
1313#define SI_PETABYTE  (1000llu * SI_TERABYTE)
1314#define SI_EXABYTE   (1000llu * SI_PETABYTE)
1315#define SI_ZETTABYTE (1000llu * SI_EXABYTE)
1316#define SI_YOTTABYTE (1000llu * SI_ZETTABYTE)
1317
1318#if SIZEOF_LONG_INT == 8
1319#define NUM_SIZES                  5
1320#else
1321#define NUM_SIZES                  4
1322#endif
1323
1324static PVFS_size PINT_s_size_table[NUM_SIZES] =
1325{
1326    /*YOTTABYTE, ZETTABYTE, EXABYTE, */
1327#if SIZEOF_LONG_INT == 8
1328    PETABYTE,
1329    TERABYTE,
1330#endif
1331    GIGABYTE, MEGABYTE, KILOBYTE
1332};
1333
1334static PVFS_size PINT_s_si_size_table[NUM_SIZES] =
1335{
1336    /*SI_YOTTABYTE, SI_ZETTABYTE, SI_EXABYTE, */
1337#if SIZEOF_LONG_INT == 8
1338    SI_PETABYTE, SI_TERABYTE,
1339#endif
1340    SI_GIGABYTE, SI_MEGABYTE, SI_KILOBYTE
1341};
1342
1343static const char *PINT_s_str_size_table[NUM_SIZES] =
1344{
1345    /*"Y", "Z", "E", */
1346#if SIZEOF_LONG_INT == 8
1347    "P","T",
1348#endif
1349    "G", "M", "K"
1350};
1351
1352/*
1353 * PVFS_util_make_size_human_readable
1354 *
1355 * converts a size value to a human readable string format
1356 *
1357 * size         - numeric size of file
1358 * out_str      - nicely formatted string, like "3.4M"
1359 *                  (caller must allocate this string)
1360 * max_out_len  - maximum lenght of out_str
1361 * use_si_units - use units of 1000, not 1024
1362 */
1363void PVFS_util_make_size_human_readable(
1364    PVFS_size size,
1365    char *out_str,
1366    int max_out_len,
1367    int use_si_units)
1368{
1369    int i = 0;
1370    double tmp = 0.0f;
1371    PVFS_size *size_table =
1372        (use_si_units? PINT_s_si_size_table : PINT_s_size_table);
1373
1374    if (out_str)
1375    {
1376        for (i = 0; i < NUM_SIZES; i++)
1377        {
1378            tmp = (double)size;
1379            if ((PVFS_size) (tmp / size_table[i]) > 0)
1380            {
1381                tmp = (tmp / size_table[i]);
1382                break;
1383            }
1384        }
1385        if (i == NUM_SIZES)
1386        {
1387            snprintf(out_str, 16, "%lld", lld(size));
1388        }
1389        else
1390        {
1391            snprintf(out_str, max_out_len, "%.1f%s",
1392                     tmp, PINT_s_str_size_table[i]);
1393        }
1394    }
1395}
1396
1397/* parse_flowproto_string()
1398 *
1399 * looks in the mount options string for a flowprotocol specifier and
1400 * sets the flowproto type accordingly
1401 *
1402 * returns 0 on success, -PVFS_error on failure
1403 */
1404static int parse_flowproto_string(
1405    const char *input,
1406    enum PVFS_flowproto_type *flowproto)
1407{
1408    int ret = 0;
1409    char *start = NULL;
1410    char flow[256];
1411    char *comma = NULL;
1412
1413    start = strstr(input, "flowproto");
1414    /* we must find a match if this function is being called... */
1415    assert(start);
1416
1417    /* scan out the option */
1418    ret = sscanf(start, "flowproto = %255s ,", flow);
1419    if (ret != 1)
1420    {
1421        gossip_err("Error: malformed flowproto option in tab file.\n");
1422        return (-PVFS_EINVAL);
1423    }
1424
1425    /* chop it off at any trailing comma */
1426    comma = index(flow, ',');
1427    if (comma)
1428    {
1429        comma[0] = '\0';
1430    }
1431
1432    if (!strcmp(flow, "dump_offsets"))
1433    {
1434        *flowproto = FLOWPROTO_DUMP_OFFSETS;
1435    }
1436    else if (!strcmp(flow, "bmi_cache"))
1437    {
1438        *flowproto = FLOWPROTO_BMI_CACHE;
1439    }
1440    else if (!strcmp(flow, "multiqueue"))
1441    {
1442        *flowproto = FLOWPROTO_MULTIQUEUE;
1443    }
1444    else
1445    {
1446        gossip_err("Error: unrecognized flowproto option: %s\n", flow);
1447        return (-PVFS_EINVAL);
1448    }
1449    return 0;
1450}
1451
1452void PVFS_util_free_mntent(
1453    struct PVFS_sys_mntent *mntent)
1454{
1455    if (mntent)
1456    {
1457        if (mntent->pvfs_config_servers)
1458        {
1459            int j;
1460            for (j=0; j<mntent->num_pvfs_config_servers; j++)
1461                if (mntent->pvfs_config_servers[j])
1462                    free(mntent->pvfs_config_servers[j]);
1463            free(mntent->pvfs_config_servers);
1464            mntent->pvfs_config_servers = NULL;
1465            mntent->num_pvfs_config_servers = 0;
1466        }
1467        if (mntent->pvfs_fs_name)
1468        {
1469            free(mntent->pvfs_fs_name);
1470            mntent->pvfs_fs_name = NULL;
1471        }
1472        if (mntent->mnt_dir)
1473        {
1474            free(mntent->mnt_dir);
1475            mntent->mnt_dir = NULL;
1476        }
1477        if (mntent->mnt_opts)
1478        {
1479            free(mntent->mnt_opts);
1480            mntent->mnt_opts = NULL;
1481        }
1482
1483        mntent->flowproto = 0;
1484        mntent->encoding = 0;
1485        mntent->fs_id = PVFS_FS_ID_NULL;
1486    }   
1487}
1488
1489int PVFS_util_copy_mntent(
1490    struct PVFS_sys_mntent *dest_mntent,
1491    struct PVFS_sys_mntent *src_mntent)
1492{
1493    int ret = -PVFS_EINVAL, i = 0;
1494
1495    if (dest_mntent && src_mntent)
1496    {
1497        memset(dest_mntent, 0, sizeof(struct PVFS_sys_mntent));
1498
1499        dest_mntent->num_pvfs_config_servers =
1500            src_mntent->num_pvfs_config_servers;
1501
1502        dest_mntent->pvfs_config_servers =
1503            malloc(dest_mntent->num_pvfs_config_servers *
1504                   sizeof(*dest_mntent->pvfs_config_servers));
1505        if (!dest_mntent)
1506        {
1507            return -PVFS_ENOMEM;
1508        }
1509
1510        memset(dest_mntent->pvfs_config_servers, 0,
1511               dest_mntent->num_pvfs_config_servers *
1512               sizeof(*dest_mntent->pvfs_config_servers));
1513
1514        for(i = 0; i < dest_mntent->num_pvfs_config_servers; i++)
1515        {
1516            dest_mntent->pvfs_config_servers[i] =
1517                strdup(src_mntent->pvfs_config_servers[i]);
1518            if (!dest_mntent->pvfs_config_servers[i])
1519            {
1520                ret = -PVFS_ENOMEM;
1521                goto error_exit;
1522            }
1523        }
1524
1525        /* nlmills: TODO: this copy will leak memory. fix that */
1526        dest_mntent->the_pvfs_config_server =
1527            strdup(src_mntent->the_pvfs_config_server);
1528        if (!dest_mntent->the_pvfs_config_server)
1529        {
1530            ret = -PVFS_ENOMEM;
1531            goto error_exit;
1532        }
1533
1534        dest_mntent->pvfs_fs_name = strdup(src_mntent->pvfs_fs_name);
1535        if (!dest_mntent->pvfs_fs_name)
1536        {
1537            ret = -PVFS_ENOMEM;
1538            goto error_exit;
1539        }
1540
1541        if (src_mntent->mnt_dir)
1542        {
1543            dest_mntent->mnt_dir = strdup(src_mntent->mnt_dir);
1544            if (!dest_mntent->mnt_dir)
1545            {
1546                ret = -PVFS_ENOMEM;
1547                goto error_exit;
1548            }
1549        }
1550
1551        if (src_mntent->mnt_opts)
1552        {
1553            dest_mntent->mnt_opts = strdup(src_mntent->mnt_opts);
1554            if (!dest_mntent->mnt_opts)
1555            {
1556                ret = -PVFS_ENOMEM;
1557                goto error_exit;
1558            }
1559        }
1560
1561        dest_mntent->flowproto = src_mntent->flowproto;
1562        dest_mntent->encoding = src_mntent->encoding;
1563        dest_mntent->fs_id = src_mntent->fs_id;
1564        dest_mntent->default_num_dfiles = src_mntent->default_num_dfiles;
1565    }
1566    return 0;
1567
1568  error_exit:
1569
1570    for(i = 0; i < dest_mntent->num_pvfs_config_servers; i++)
1571    {
1572        if (dest_mntent->pvfs_config_servers[i])
1573        {
1574            free(dest_mntent->pvfs_config_servers[i]);
1575            dest_mntent->pvfs_config_servers[i] = NULL;
1576        }
1577    }
1578
1579    if (dest_mntent->pvfs_config_servers)
1580    {
1581        free(dest_mntent->pvfs_config_servers);
1582        dest_mntent->pvfs_config_servers = NULL;
1583    }
1584
1585    if (dest_mntent->pvfs_fs_name)
1586    {
1587        free(dest_mntent->pvfs_fs_name);
1588        dest_mntent->pvfs_fs_name = NULL;
1589    }
1590
1591    if (dest_mntent->mnt_dir)
1592    {
1593        free(dest_mntent->mnt_dir);
1594        dest_mntent->mnt_dir = NULL;
1595    }
1596
1597    if (dest_mntent->mnt_opts)
1598    {
1599        free(dest_mntent->mnt_opts);
1600        dest_mntent->mnt_opts = NULL;
1601    }
1602    return ret;
1603}
1604
1605/*
1606 * Pull out the wire encoding specified as a mount option in the tab
1607 * file.
1608 *
1609 * Input string is not modified; result goes into et.
1610 *
1611 * Returns 0 if all okay.
1612 */
1613static int parse_encoding_string(
1614    const char *cp,
1615    enum PVFS_encoding_type *et)
1616{
1617    int i = 0;
1618    const char *cq = NULL;
1619
1620    struct
1621    {
1622        const char *name;
1623        enum PVFS_encoding_type val;
1624    } enc_str[] =
1625        { { "default", ENCODING_DEFAULT },
1626          { "defaults", ENCODING_DEFAULT },
1627          { "direct", ENCODING_DIRECT },
1628          { "le_bfield", ENCODING_LE_BFIELD },
1629          { "xdr", ENCODING_XDR } };
1630
1631    gossip_debug(GOSSIP_CLIENT_DEBUG, "%s: input is %s\n",
1632                 __func__, cp);
1633    cp += strlen("encoding");
1634    for (; isspace(*cp); cp++);        /* optional spaces */
1635    if (*cp != '=')
1636    {
1637        gossip_err("Error: %s: malformed encoding option in tab file.\n",
1638                   __func__);
1639        return -PVFS_EINVAL;
1640    }
1641    for (++cp; isspace(*cp); cp++);        /* optional spaces */
1642    for (cq = cp; *cq && *cq != ','; cq++);/* find option end */
1643
1644    *et = -1;
1645    for (i = 0; i < sizeof(enc_str) / sizeof(enc_str[0]); i++)
1646    {
1647        int n = strlen(enc_str[i].name);
1648        if (cq - cp > n)
1649            n = cq - cp;
1650        if (!strncmp(enc_str[i].name, cp, n))
1651        {
1652            *et = enc_str[i].val;
1653            break;
1654        }
1655    }
1656    if (*et == -1)
1657    {
1658        gossip_err("Error: %s: unknown encoding type in tab file.\n",
1659                   __func__);
1660        return -PVFS_EINVAL;
1661    }
1662    return 0;
1663}
1664
1665/* PINT_release_pvfstab()
1666 *
1667 * frees up any resources associated with previously parsed tabfiles
1668 *
1669 * no return value
1670 */
1671void PINT_release_pvfstab(void)
1672{
1673    int i, j;
1674
1675    gen_mutex_lock(&s_stat_tab_mutex);
1676    for(i=0; i<s_stat_tab_count; i++)
1677    {
1678        for (j = 0; j < s_stat_tab_array[i].mntent_count; j++)
1679        {
1680            if (s_stat_tab_array[i].mntent_array[j].fs_id !=
1681                PVFS_FS_ID_NULL)
1682            {
1683                PVFS_util_free_mntent(
1684                    &s_stat_tab_array[i].mntent_array[j]);
1685            }
1686        }
1687        free(s_stat_tab_array[i].mntent_array);
1688    }
1689    s_stat_tab_count = 0;
1690
1691    for (j = 0; j < s_stat_tab_array[
1692             PVFS2_DYNAMIC_TAB_INDEX].mntent_count; j++)
1693    {
1694        if (s_stat_tab_array[
1695                PVFS2_DYNAMIC_TAB_INDEX].mntent_array[j].fs_id !=
1696            PVFS_FS_ID_NULL)
1697        {
1698            PVFS_util_free_mntent(
1699                &s_stat_tab_array[
1700                    PVFS2_DYNAMIC_TAB_INDEX].mntent_array[j]);
1701        }
1702    }
1703    if (s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_array)
1704    {
1705        free(s_stat_tab_array[PVFS2_DYNAMIC_TAB_INDEX].mntent_array);
1706    }
1707
1708    gen_mutex_unlock(&s_stat_tab_mutex);
1709}
1710
1711uint32_t PVFS_util_sys_to_object_attr_mask(
1712    uint32_t sys_attrmask)
1713{
1714
1715    /*
1716      adjust parameters as necessary; what's happening here
1717      is that we're converting sys_attr masks to obj_attr masks
1718      before passing the getattr request to the server.
1719    */
1720    uint32_t attrmask = 0;
1721    if (sys_attrmask & PVFS_ATTR_SYS_SIZE)
1722    {
1723        /* need datafile handles and distribution in order to get
1724         * datafile handles and know what function to call to get
1725         * the file size.
1726         */
1727        attrmask |= (PVFS_ATTR_META_ALL | PVFS_ATTR_DATA_SIZE);
1728    }
1729
1730    if (sys_attrmask & PVFS_ATTR_SYS_DFILE_COUNT)
1731    {
1732        attrmask |= PVFS_ATTR_META_DFILES;
1733    }
1734
1735    if (sys_attrmask & PVFS_ATTR_SYS_DIRENT_COUNT)
1736    {
1737        attrmask |= PVFS_ATTR_DIR_DIRENT_COUNT;
1738    }
1739
1740    if (sys_attrmask & PVFS_ATTR_SYS_DIR_HINT)
1741    {
1742        attrmask |= PVFS_ATTR_DIR_HINT;
1743    }
1744
1745    if (sys_attrmask & PVFS_ATTR_SYS_LNK_TARGET)
1746    {
1747        attrmask |= PVFS_ATTR_SYMLNK_TARGET;
1748    }
1749
1750    /* we need the distribution in order to calculate block size */
1751    if (sys_attrmask & PVFS_ATTR_SYS_BLKSIZE)
1752    {
1753        attrmask |= PVFS_ATTR_META_DIST;
1754    }
1755
1756    if (sys_attrmask & PVFS_ATTR_SYS_CAPABILITY)
1757    {
1758        attrmask |= PVFS_ATTR_CAPABILITY;
1759    }
1760
1761    if(sys_attrmask & PVFS_ATTR_SYS_UID)
1762        attrmask |= PVFS_ATTR_COMMON_UID;
1763    if(sys_attrmask & PVFS_ATTR_SYS_GID)
1764        attrmask |= PVFS_ATTR_COMMON_GID;
1765    if(sys_attrmask & PVFS_ATTR_SYS_PERM)
1766        attrmask |= PVFS_ATTR_COMMON_PERM;
1767    if(sys_attrmask & PVFS_ATTR_SYS_ATIME)
1768        attrmask |= PVFS_ATTR_COMMON_ATIME;
1769    if(sys_attrmask & PVFS_ATTR_SYS_CTIME)
1770        attrmask |= PVFS_ATTR_COMMON_CTIME;
1771    if(sys_attrmask & PVFS_ATTR_SYS_MTIME)
1772        attrmask |= PVFS_ATTR_COMMON_MTIME;
1773    if(sys_attrmask & PVFS_ATTR_SYS_TYPE)
1774        attrmask |= PVFS_ATTR_COMMON_TYPE;
1775    if(sys_attrmask & PVFS_ATTR_SYS_ATIME_SET)
1776        attrmask |= PVFS_ATTR_COMMON_ATIME_SET;
1777    if(sys_attrmask & PVFS_ATTR_SYS_MTIME_SET)
1778        attrmask |= PVFS_ATTR_COMMON_MTIME_SET;
1779
1780    gossip_debug(GOSSIP_GETATTR_DEBUG,
1781                 "attrmask being passed to server: ");
1782    PINT_attrmask_print(GOSSIP_GETATTR_DEBUG, attrmask);
1783
1784    return attrmask;
1785}
1786
1787uint32_t PVFS_util_object_to_sys_attr_mask(
1788    uint32_t obj_mask)
1789{
1790    int sys_mask = 0;
1791
1792    if (obj_mask & PVFS_ATTR_COMMON_UID)
1793    {
1794        sys_mask |= PVFS_ATTR_SYS_UID;
1795    }
1796    if (obj_mask & PVFS_ATTR_COMMON_GID)
1797    {
1798        sys_mask |= PVFS_ATTR_SYS_GID;
1799    }
1800    if (obj_mask & PVFS_ATTR_COMMON_PERM)
1801    {
1802        sys_mask |= PVFS_ATTR_SYS_PERM;
1803    }
1804    if (obj_mask & PVFS_ATTR_COMMON_ATIME)
1805    {
1806        sys_mask |= PVFS_ATTR_SYS_ATIME;
1807    }
1808    if (obj_mask & PVFS_ATTR_COMMON_CTIME)
1809    {
1810        sys_mask |= PVFS_ATTR_SYS_CTIME;
1811    }
1812    if (obj_mask & PVFS_ATTR_COMMON_MTIME)
1813    {
1814        sys_mask |= PVFS_ATTR_SYS_MTIME;
1815    }
1816    if (obj_mask & PVFS_ATTR_COMMON_TYPE)
1817    {
1818        sys_mask |= PVFS_ATTR_SYS_TYPE;
1819    }
1820    if (obj_mask & PVFS_ATTR_DATA_SIZE)
1821    {
1822        sys_mask |= PVFS_ATTR_DATA_SIZE;
1823    }
1824    if (obj_mask & PVFS_ATTR_SYMLNK_TARGET)
1825    {
1826        sys_mask |= PVFS_ATTR_SYS_LNK_TARGET;
1827    }
1828    if (obj_mask & PVFS_ATTR_DIR_DIRENT_COUNT)
1829    {
1830        sys_mask |= PVFS_ATTR_SYS_DIRENT_COUNT;
1831    }
1832    if (obj_mask & PVFS_ATTR_META_DFILES)
1833    {
1834        sys_mask |= PVFS_ATTR_SYS_DFILE_COUNT;
1835    }
1836    if (obj_mask & PVFS_ATTR_META_DIST)
1837    {
1838        sys_mask |= PVFS_ATTR_SYS_BLKSIZE;
1839    }
1840    if (obj_mask & PVFS_ATTR_DIR_HINT)
1841    {
1842        sys_mask |= PVFS_ATTR_SYS_DIR_HINT;
1843    }
1844    if (obj_mask & PVFS_ATTR_CAPABILITY)
1845    {
1846        sys_mask |= PVFS_ATTR_SYS_CAPABILITY;
1847    }
1848
1849    /* NOTE: the PVFS_ATTR_META_UNSTUFFED is intentionally not exposed
1850     * outside of the system interface
1851     */
1852    return sys_mask;
1853}
1854
1855/*
1856 * Pull out the wire encoding specified as a mount option in the tab
1857 * file.
1858 *
1859 * Input string is not modified; result goes into et.
1860 *
1861 * Returns 0 if all okay.
1862 */
1863static int parse_num_dfiles_string(const char* cp, int* num_dfiles)
1864{
1865    int parsed_value = 0;
1866    char* end_ptr = NULL;
1867
1868    gossip_debug(GOSSIP_CLIENT_DEBUG, "%s: input is %s\n",
1869                 __func__, cp);
1870   
1871    cp += strlen("num_dfiles");
1872
1873    /* Skip optional spacing */
1874    for (; isspace(*cp); cp++);
1875   
1876    if (*cp != '=')
1877    {
1878        gossip_err("Error: %s: malformed num_dfiles option in tab file.\n",
1879                   __func__);
1880        return -PVFS_EINVAL;
1881    }
1882   
1883    /* Skip optional spacing */
1884    for (++cp; isspace(*cp); cp++);
1885
1886    parsed_value = strtol(cp, &end_ptr, 10);
1887
1888    /* If a numerica value was found, continue
1889       else, report an error */
1890    if (end_ptr != cp)
1891    {
1892        *num_dfiles = parsed_value;
1893    }
1894    else
1895    {
1896        gossip_err("Error: %s: malformed num_dfiles option in tab file.\n",
1897                   __func__);
1898        return -PVFS_EINVAL;
1899    }
1900
1901    return 0;
1902}
1903
1904/* PINT_util_resolve_absolute()
1905 *
1906 * given a local path of a file that may reside on a pvfs2 volume,
1907 * determine what the fsid and fs relative path is. Makes no attempt
1908 * to canonicalize the path.
1909 *
1910 * returns 0 on succees, -PVFS_error on failure
1911 */
1912static int PINT_util_resolve_absolute(
1913    const char* local_path,
1914    PVFS_fs_id* out_fs_id,
1915    char* out_fs_path,
1916    int out_fs_path_max)
1917{
1918    int i = 0, j = 0;
1919    int ret = -PVFS_EINVAL;
1920
1921    gen_mutex_lock(&s_stat_tab_mutex);
1922
1923    for(i=0; i < s_stat_tab_count; i++)
1924    {
1925        for(j=0; j<s_stat_tab_array[i].mntent_count; j++)
1926        {
1927            ret = PINT_remove_dir_prefix(
1928                local_path,
1929                s_stat_tab_array[i].mntent_array[j].mnt_dir,
1930                out_fs_path, out_fs_path_max);
1931            if(ret == 0)
1932            {
1933                *out_fs_id = s_stat_tab_array[i].mntent_array[j].fs_id;
1934                if(*out_fs_id == PVFS_FS_ID_NULL)
1935                {
1936                    gossip_err("Error: %s resides on a PVFS2 file system "
1937                    "that has not yet been initialized.\n", local_path);
1938
1939                    gen_mutex_unlock(&s_stat_tab_mutex);
1940                    return(-PVFS_ENXIO);
1941                }
1942                gen_mutex_unlock(&s_stat_tab_mutex);
1943                return(0);
1944            }
1945        }
1946    }
1947
1948    /* check the dynamic tab area if we haven't resolved anything yet */
1949    for(j = 0; j < s_stat_tab_array[
1950            PVFS2_DYNAMIC_TAB_INDEX].mntent_count; j++)
1951    {
1952        ret = PINT_remove_dir_prefix(
1953            local_path, s_stat_tab_array[
1954                PVFS2_DYNAMIC_TAB_INDEX].mntent_array[j].mnt_dir,
1955            out_fs_path, out_fs_path_max);
1956        if (ret == 0)
1957        {
1958            *out_fs_id = s_stat_tab_array[
1959                PVFS2_DYNAMIC_TAB_INDEX].mntent_array[j].fs_id;
1960            if(*out_fs_id == PVFS_FS_ID_NULL)
1961            {
1962                gossip_err("Error: %s resides on a PVFS2 file system "
1963                           "that has not yet been initialized.\n",
1964                           local_path);
1965
1966                gen_mutex_unlock(&s_stat_tab_mutex);
1967                return(-PVFS_ENXIO);
1968            }
1969            gen_mutex_unlock(&s_stat_tab_mutex);
1970            return(0);
1971        }
1972    }
1973
1974    gen_mutex_unlock(&s_stat_tab_mutex);
1975    return(-PVFS_ENOENT);
1976}
1977
1978#ifdef DEFINE_MY_GET_NEXT_FSENT
1979
1980static struct fstab * PINT_util_my_get_next_fsent(PINT_fstab_t * tab)
1981{
1982    char linestr[500];
1983    int linelen = 0;
1984    char * strtok_ctx;
1985    char * nexttok;
1986    PINT_fstab_entry_t * fsentry;
1987    if(!fgets(linestr, 500, tab))
1988    {
1989        return NULL;
1990    }
1991
1992    fsentry = malloc(sizeof(PINT_fstab_entry_t));
1993    if(!fsentry)
1994    {
1995        return NULL;
1996    }
1997    memset(fsentry, 0, sizeof(PINT_fstab_entry_t));
1998
1999    linelen = strlen(linestr);
2000    if(linestr[linelen - 1] == '\n')
2001    {
2002        linestr[linelen - 1] = 0;
2003    }
2004
2005    /* get the path string */
2006    nexttok = strtok_r(linestr, " ", &strtok_ctx);
2007    if(!nexttok)
2008    {
2009        goto exit;
2010    }
2011    fsentry->fs_spec = strdup(nexttok);
2012
2013   
2014    /* get the mount point */
2015
2016    nexttok = strtok_r(NULL, " ", &strtok_ctx);
2017    if(!nexttok)
2018    {
2019        goto exit;
2020    }
2021    fsentry->fs_file = strdup(nexttok);
2022
2023    /* get the fs type */
2024    nexttok = strtok_r(NULL, " ", &strtok_ctx);
2025    if(!nexttok)
2026    {
2027        goto exit;
2028    }
2029    fsentry->fs_vfstype = strdup(nexttok);
2030
2031    /* get the mount opts */
2032    nexttok = strtok_r(NULL, " ", &strtok_ctx);
2033    if(!nexttok)
2034    {
2035        goto exit;
2036    }
2037    fsentry->fs_mntops = strdup(nexttok);
2038
2039 exit:
2040    return fsentry;
2041}
2042
2043static void PINT_util_fsent_destroy(PINT_fstab_entry_t * entry)
2044{
2045    if(entry)
2046    {
2047        if(entry->fs_spec)
2048        {
2049            free(entry->fs_spec);
2050        }
2051
2052        if(entry->fs_file)
2053        {
2054            free(entry->fs_file);
2055        }
2056       
2057        if(entry->fs_vfstype)
2058        {
2059            free(entry->fs_vfstype);
2060        }
2061
2062        if(entry->fs_mntops)
2063        {
2064            free(entry->fs_mntops);
2065        }
2066
2067        if(entry->fs_type)
2068        {
2069            free(entry->fs_type);
2070        }
2071   
2072        free(entry);
2073    }
2074}
2075#endif /* DEFINE_MY_GET_NEXT_FSENT */
2076
2077int32_t PVFS_util_translate_mode(int mode, int suid)
2078{
2079    int ret = 0, i = 0;
2080#define NUM_MODES 11
2081    static int modes[NUM_MODES] =
2082    {
2083        S_IXOTH, S_IWOTH, S_IROTH,
2084        S_IXGRP, S_IWGRP, S_IRGRP,
2085        S_IXUSR, S_IWUSR, S_IRUSR,
2086        S_ISGID, S_ISUID
2087    };
2088    static int pvfs2_modes[NUM_MODES] =
2089    {
2090        PVFS_O_EXECUTE, PVFS_O_WRITE, PVFS_O_READ,
2091        PVFS_G_EXECUTE, PVFS_G_WRITE, PVFS_G_READ,
2092        PVFS_U_EXECUTE, PVFS_U_WRITE, PVFS_U_READ,
2093        PVFS_G_SGID,    PVFS_U_SUID
2094    };
2095
2096    for(i = 0; i < NUM_MODES; i++)
2097    {
2098        if (mode & modes[i])
2099        {
2100            ret |= pvfs2_modes[i];
2101        }
2102    }
2103    if (suid == 0 && (ret & PVFS_U_SUID))
2104    {
2105         ret &= ~PVFS_U_SUID;
2106    }
2107    return ret;
2108#undef NUM_MODES
2109}
2110
2111/*
2112 * Local variables:
2113 *  mode: c
2114 *  c-indent-level: 4
2115 *  c-basic-offset: 4
2116 * End:
2117 *
2118 * vim: ts=8 sts=4 sw=4 expandtab
2119 */
Note: See TracBrowser for help on using the browser.