diff options
Diffstat (limited to 'xlators/nfs')
| -rw-r--r-- | xlators/nfs/server/src/Makefile.am | 4 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs.c | 8 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs3-helpers.c | 2825 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs3-helpers.h | 342 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs3.c | 4836 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs3.h | 202 | 
6 files changed, 8215 insertions, 2 deletions
diff --git a/xlators/nfs/server/src/Makefile.am b/xlators/nfs/server/src/Makefile.am index 9661dd95d20..c51dad1e804 100644 --- a/xlators/nfs/server/src/Makefile.am +++ b/xlators/nfs/server/src/Makefile.am @@ -2,10 +2,10 @@ xlator_LTLIBRARIES = server.la  xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/nfs  rpclibdir = $(top_srcdir)/xlators/nfs/lib/src/  server_la_LDFLAGS = -module -avoidversion -server_la_SOURCES = nfs.c nfs-common.c nfs-fops.c nfs-inodes.c nfs-generics.c mount3.c nfs3-fh.c +server_la_SOURCES = nfs.c nfs-common.c nfs-fops.c nfs-inodes.c nfs-generics.c mount3.c nfs3-fh.c nfs3.c nfs3-helpers.c  server_la_LIBADD = $(top_builddir)/xlators/nfs/lib/src/librpcsvc.la $(top_builddir)/libglusterfs/src/libglusterfs.la -noinst_HEADERS = nfs.h nfs-common.h nfs-fops.h nfs-inodes.h nfs-generics.h mount3.h nfs3-fh.h +noinst_HEADERS = nfs.h nfs-common.h nfs-fops.h nfs-inodes.h nfs-generics.h mount3.h nfs3-fh.h nfs3.h nfs3-helpers.h  AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\  	-I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS)\ diff --git a/xlators/nfs/server/src/nfs.c b/xlators/nfs/server/src/nfs.c index 74937903612..488ece419cb 100644 --- a/xlators/nfs/server/src/nfs.c +++ b/xlators/nfs/server/src/nfs.c @@ -37,6 +37,7 @@  #include "nfs-fops.h"  #include "inode.h"  #include "mount3.h" +#include "nfs3.h"  /* Every NFS version must call this function with the init function   * for its particular version. @@ -152,6 +153,13 @@ nfs_add_all_initiators (struct nfs_state *nfs)                  goto ret;          } +        ret = nfs_add_initer (&nfs->versions, nfs3svc_init); +        if (ret == -1) { +                gf_log (GF_NFS, GF_LOG_ERROR, "Failed to add protocol" +                        " initializer"); +                goto ret; +        } +          ret = 0;  ret:          return ret; diff --git a/xlators/nfs/server/src/nfs3-helpers.c b/xlators/nfs/server/src/nfs3-helpers.c new file mode 100644 index 00000000000..5b2fdaca4ca --- /dev/null +++ b/xlators/nfs/server/src/nfs3-helpers.c @@ -0,0 +1,2825 @@ +/* +  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include "xlator.h" +#include "nfs3.h" +#include "nfs3-fh.h" +#include "msg-nfs3.h" +#include "rbthash.h" +#include "nfs-fops.h" +#include "nfs-inodes.h" +#include "nfs-generics.h" +#include "nfs3-helpers.h" +#include "iatt.h" +#include <string.h> + +#define nfs3_call_resume(cst)                                   \ +        do {                                                    \ +                if (((cst)) && (cst)->resume_fn)                \ +                        (cst)->resume_fn (cst);                 \ +        } while (0)                                             \ + +#define nfs3_call_resume_estale(csta)                           \ +        do {                                                    \ +                (csta)->resolve_ret = -1;                       \ +                (csta)->resolve_errno = ESTALE;                 \ +                nfs3_call_resume (csta);                        \ +        } while (0)                                             \ + +struct nfs3stat_strerror { +        nfsstat3        stat; +        char            strerror[100]; +}; + + +struct nfs3stat_strerror nfs3stat_strerror_table[] = { +        { NFS3_OK,              "Call completed successfully."          }, +        { NFS3ERR_PERM,         "Not owner"                             }, +        { NFS3ERR_NOENT,        "No such file or directory"             }, +        { NFS3ERR_IO,           "I/O error"                             }, +        { NFS3ERR_NXIO,         "I/O error"                             }, +        { NFS3ERR_ACCES,        "Permission denied"                     }, +        { NFS3ERR_EXIST,        "File exists"                           }, +        { NFS3ERR_XDEV,         "Attempt to do a cross-device hard link"}, +        { NFS3ERR_NODEV,        "No such device"                        }, +        { NFS3ERR_NOTDIR,       "Not a directory"                       }, +        { NFS3ERR_ISDIR,        "Not a directory"                       }, +        { NFS3ERR_INVAL,        "Invalid argument for operation"        }, +        { NFS3ERR_FBIG,         "File too large"                        }, +        { NFS3ERR_NOSPC,        "No space left on device"               }, +        { NFS3ERR_ROFS,         "Read-only file system"                 }, +        { NFS3ERR_MLINK,        "Too many hard links"                   }, +        { NFS3ERR_NAMETOOLONG,  "Filename in operation was too long"    }, +        { NFS3ERR_NOTEMPTY,     "Directory not empty"                   }, +        { NFS3ERR_DQUOT,        "Resource (quota) hard limit exceeded"  }, +        { NFS3ERR_STALE,        "Invalid file handle"                   }, +        { NFS3ERR_REMOTE,       "Too many levels of remote in path"     }, +        { NFS3ERR_BADHANDLE,    "Illegal NFS file handle"               }, +        { NFS3ERR_NOT_SYNC,     "Update synchronization mismatch detected" }, +        { NFS3ERR_BAD_COOKIE,   "READDIR or READDIRPLUS cookie is stale"}, +        { NFS3ERR_NOTSUPP,      "Operation is not supported"            }, +        { NFS3ERR_TOOSMALL,     "Buffer or request is too small"        }, +        { NFS3ERR_SERVERFAULT,  "Error occurred on the server or IO Error" }, +        { NFS3ERR_BADTYPE,      "Type not supported by the server"      }, +        { NFS3ERR_JUKEBOX,      "Cannot complete server initiated request" }, +        { -1,                   "IO Error"                              }, + +}; + + +void +nfs3_map_xlid_to_statdev (struct iatt *ia, uint16_t xlid) +{ +        if (!ia) +                return; + +        ia->ia_dev = xlid; +} + + +struct nfs3_fh +nfs3_extract_nfs3_fh (nfs_fh3 fh) +{ +        struct nfs3_fh          gfh; + +        memcpy (&gfh, fh.data.data_val, fh.data.data_len); +        return gfh; +} + + +struct nfs3_fh +nfs3_extract_lookup_fh (lookup3args *args) +{ +        return nfs3_extract_nfs3_fh(args->what.dir); +} + + +char * +nfs3_extract_lookup_name (lookup3args *args) +{ +        return args->what.name; +} + + +nfsstat3 +nfs3_errno_to_nfsstat3 (int errnum) +{ +        nfsstat3        stat = NFS3_OK; + +        switch (errnum) { + +        case 0: +                stat = NFS3_OK; +                break; + +        case EPERM: +                stat = NFS3ERR_PERM; +                break; + +        case ENOENT: +                stat = NFS3ERR_NOENT; +                break; + +        case EACCES: +                stat = NFS3ERR_ACCES; +                break; + +        case EEXIST: +                stat = NFS3ERR_EXIST; +                break; + +        case EXDEV: +                stat = NFS3ERR_XDEV; +                break; + +        case ENODEV: +                stat = NFS3ERR_NODEV; +                break; + +        case EIO: +                stat = NFS3ERR_IO; +                break; + +        case ENXIO: +                stat = NFS3ERR_NXIO; +                break; + +        case ENOTDIR: +                stat = NFS3ERR_NOTDIR; +                break; + +        case EISDIR: +                stat = NFS3ERR_ISDIR; +                break; + +        case EINVAL: +                stat = NFS3ERR_INVAL; +                break; + +        case ENOSPC: +                stat = NFS3ERR_NOSPC; +                break; + +        case EROFS: +                stat = NFS3ERR_ROFS; +                break; + +        case EFBIG: +                stat = NFS3ERR_FBIG; +                break; + +        case EMLINK: +                stat = NFS3ERR_MLINK; +                break; + +        case ENAMETOOLONG: +                stat = NFS3ERR_NAMETOOLONG; +                break; + +        case ENOTEMPTY: +                stat = NFS3ERR_NOTEMPTY; +                break; + +        case EFAULT: +                stat = NFS3ERR_SERVERFAULT; +                break; + +        case ENOSYS: +                stat = NFS3ERR_NOTSUPP; +                break; + +        case EBADF: +                stat = NFS3ERR_BADTYPE; +                break; + +        case ESTALE: +                stat = NFS3ERR_STALE; + +        default: +                stat = NFS3ERR_SERVERFAULT; +                break; +        } + +        return stat; +} + + +void +nfs3_fill_lookup3res_error (lookup3res *res, nfsstat3 stat, +                            struct iatt *dirstat) +{ + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (!dirstat) { +                res->lookup3res_u.resfail.dir_attributes.attributes_follow = FALSE; +        } + +} + +fattr3 +nfs3_stat_to_fattr3 (struct iatt *buf) +{ +        fattr3          fa = {0, }; + +        if (IA_ISDIR (buf->ia_type)) +                fa.type = NF3DIR; +        else if (IA_ISREG (buf->ia_type)) +                fa.type = NF3REG; +        else if (IA_ISCHR (buf->ia_type)) +                fa.type = NF3CHR; +        else if (IA_ISBLK (buf->ia_type)) +                fa.type = NF3BLK; +        else if (IA_ISFIFO (buf->ia_type)) +                fa.type = NF3FIFO; +        else if (IA_ISLNK (buf->ia_type)) +                fa.type = NF3LNK; +        else if (IA_ISSOCK (buf->ia_type)) +                fa.type = NF3SOCK; + +        if (IA_PROT_RUSR (buf->ia_prot)) +                fa.mode |= NFS3MODE_ROWNER; +        if (IA_PROT_WUSR (buf->ia_prot)) +                fa.mode |= NFS3MODE_WOWNER; +        if (IA_PROT_XUSR (buf->ia_prot)) +                fa.mode |= NFS3MODE_XOWNER; + +        if (IA_PROT_RGRP (buf->ia_prot)) +                fa.mode |= NFS3MODE_RGROUP; +        if (IA_PROT_WGRP (buf->ia_prot)) +                fa.mode |= NFS3MODE_WGROUP; +        if (IA_PROT_XGRP (buf->ia_prot)) +                fa.mode |= NFS3MODE_XGROUP; + +        if (IA_PROT_ROTH (buf->ia_prot)) +                fa.mode |= NFS3MODE_ROTHER; +        if (IA_PROT_WOTH (buf->ia_prot)) +                fa.mode |= NFS3MODE_WOTHER; +        if (IA_PROT_XOTH (buf->ia_prot)) +                fa.mode |= NFS3MODE_XOTHER; + +        if (IA_PROT_SUID (buf->ia_prot)) +                fa.mode |= NFS3MODE_SETXUID; +        if (IA_PROT_SGID (buf->ia_prot)) +                fa.mode |= NFS3MODE_SETXGID; +        if (IA_PROT_STCKY (buf->ia_prot)) +                fa.mode |= NFS3MODE_SAVESWAPTXT; + +        fa.nlink = buf->ia_nlink; +        fa.uid = buf->ia_uid; +        fa.gid = buf->ia_gid; +        fa.size = buf->ia_size; +        fa.used = (buf->ia_blocks * 512); + +        if ((IA_ISCHR (buf->ia_type) || IA_ISBLK (buf->ia_type))) { +                fa.rdev.specdata1 = ia_major (buf->ia_rdev); +                fa.rdev.specdata2 = ia_minor (buf->ia_rdev); +        } else { +                fa.rdev.specdata1 = 0; +                fa.rdev.specdata2 = 0; +        } + +        fa.fsid = buf->ia_dev; +        fa.fileid = buf->ia_ino; +        /* FIXME: Handle time resolutions for sub-second granularity */ +        if (buf->ia_atime == 9669) { +                fa.mtime.seconds = 0; +                fa.mtime.nseconds = 0; +                fa.atime.seconds = 0; +                fa.atime.nseconds = 0; +        } else { +                fa.mtime.seconds = buf->ia_mtime; +                fa.mtime.nseconds = 0; +                fa.atime.seconds = buf->ia_atime; +                fa.atime.seconds = 0; +                fa.atime.nseconds = 0; +        } + +        fa.atime.seconds = buf->ia_atime; +        fa.atime.nseconds = 0; + +        fa.ctime.seconds = buf->ia_ctime; +        fa.ctime.nseconds = 0; + +        return fa; +} + + +post_op_attr +nfs3_stat_to_post_op_attr (struct iatt *buf) +{ +        post_op_attr    attr = {0, }; +        if (!buf) +                return attr; + +        /* Some performance translators return zero-filled stats when they +         * do not have up-to-date attributes. Need to handle this by not +         * returning these zeroed out attrs. +         */ +        attr.attributes_follow = FALSE; +        if (nfs_zero_filled_stat (buf)) +                goto out; + +        attr.post_op_attr_u.attributes = nfs3_stat_to_fattr3 (buf); +        attr.attributes_follow = TRUE; + +out: +        return attr; +} + + +pre_op_attr +nfs3_stat_to_pre_op_attr (struct iatt *pre) +{ +        pre_op_attr     poa = {0, }; + +        /* Some performance translators return zero-filled stats when they +         * do not have up-to-date attributes. Need to handle this by not +         * returning these zeroed out attrs. +         */ +        poa.attributes_follow = FALSE; +        if (nfs_zero_filled_stat (pre)) +                goto out; + +        poa.attributes_follow = TRUE; +        poa.pre_op_attr_u.attributes.size = pre->ia_size; +        if (pre->ia_atime == 9669) +                poa.pre_op_attr_u.attributes.mtime.seconds = 0; +        else +                poa.pre_op_attr_u.attributes.mtime.seconds = pre->ia_mtime; +        poa.pre_op_attr_u.attributes.ctime.seconds = pre->ia_ctime; + +out: +        return poa; +} + +void +nfs3_fill_lookup3res_success (lookup3res *res, nfsstat3 stat, +                              struct nfs3_fh *fh, struct iatt *buf, +                              struct iatt *postparent) +{ +        post_op_attr    obj, dir; +        uint32_t        fhlen = 0; + +        res->status = stat; +        if (fh) { +                res->lookup3res_u.resok.object.data.data_val = (void *)fh; +                fhlen = nfs3_fh_compute_size (fh); +                res->lookup3res_u.resok.object.data.data_len = fhlen; +        } + +        obj.attributes_follow = FALSE; +        dir.attributes_follow = FALSE; +        if (buf) { +                nfs3_map_xlid_to_statdev (buf, fh->xlatorid); +                obj = nfs3_stat_to_post_op_attr (buf); +        } + +        if (postparent) { +                nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); +                dir = nfs3_stat_to_post_op_attr (postparent); +        } + +        res->lookup3res_u.resok.obj_attributes = obj; +        res->lookup3res_u.resok.dir_attributes = dir; +} + + +void +nfs3_fill_lookup3res (lookup3res *res, nfsstat3 stat, struct nfs3_fh *newfh, +                      struct iatt *buf, struct iatt *postparent) +{ + +        memset (res, 0, sizeof (*res)); +        if (stat != NFS3_OK) +                nfs3_fill_lookup3res_error (res, stat, postparent); +        else +                nfs3_fill_lookup3res_success (res, stat, newfh, buf, +                                              postparent); +} + +struct nfs3_fh +nfs3_extract_getattr_fh (getattr3args *args) +{ +        return nfs3_extract_nfs3_fh(args->object); +} + + +void +nfs3_fill_getattr3res (getattr3res *res, nfsstat3 stat, struct iatt *buf, +                       uint16_t xlid) +{ + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (buf, xlid); +        res->getattr3res_u.resok.obj_attributes = nfs3_stat_to_fattr3 (buf); + +} + + +struct nfs3_fh +nfs3_extract_fsinfo_fh (fsinfo3args *args) +{ +        return nfs3_extract_nfs3_fh (args->fsroot); +} + + +void +nfs3_fill_fsinfo3res (struct nfs3_state *nfs3, fsinfo3res *res, +                      nfsstat3 status, struct iatt *fsroot, uint16_t xlid) +{ +        fsinfo3resok    resok = {{0}, }; +        nfstime3        tdelta = GF_NFS3_TIMEDELTA_SECS; + +        memset (res, 0, sizeof (*res)); +        res->status = status; +        if (status != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (fsroot, xlid); +        resok.obj_attributes = nfs3_stat_to_post_op_attr (fsroot); +        resok.rtmax = nfs3->readsize; +        resok.rtpref = nfs3->readsize; +        resok.rtmult = GF_NFS3_RTMULT; +        resok.wtmax = nfs3->writesize; +        resok.wtpref = nfs3->writesize; +        resok.wtmult = GF_NFS3_WTMULT; +        resok.dtpref = nfs3->readdirsize; +        resok.maxfilesize = GF_NFS3_MAXFILE; +        resok.time_delta = tdelta; +        resok.properties = GF_NFS3_FS_PROP; + +        res->fsinfo3res_u.resok = resok; + +} + + +void +nfs3_prep_lookup3args (lookup3args *args, struct nfs3_fh *fh, char *name) +{ +        memset (args, 0, sizeof (*args)); +        args->what.dir.data.data_val = (void *)fh; +        args->what.name = name; +} + + +void +nfs3_prep_getattr3args (getattr3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->object.data.data_val = (void *)fh; +} + + +void +nfs3_prep_fsinfo3args (fsinfo3args *args, struct nfs3_fh *root) +{ +        memset (args, 0, sizeof (*args)); +        args->fsroot.data.data_val = (void *)root; +} + + +char * +nfsstat3_strerror(int stat) +{ +        int i; +        for(i = 0; nfs3stat_strerror_table[i].stat != -1; i++) { +                if (nfs3stat_strerror_table[i].stat == stat) +                        return nfs3stat_strerror_table[i].strerror; +        } + +        return nfs3stat_strerror_table[i].strerror; +} + + + +void +nfs3_prep_access3args (access3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->object.data.data_val = (void *)fh; +} + + +uint32_t +nfs3_owner_accessbits (ia_prot_t prot, ia_type_t type, uint32_t request) +{ +        uint32_t accresult = 0; + +        if (IA_PROT_RUSR (prot) && (request & ACCESS3_READ)) +                accresult |= ACCESS3_READ; + +        if (request & ACCESS3_LOOKUP) +                if ((IA_ISDIR (type)) && (IA_PROT_XUSR (prot))) +                        accresult |= ACCESS3_LOOKUP; + +        if ((IA_PROT_WUSR (prot) && (request & ACCESS3_MODIFY))) +                accresult |= ACCESS3_MODIFY; + +        if ((IA_PROT_WUSR (prot) && (request & ACCESS3_EXTEND))) +                accresult |= ACCESS3_EXTEND; + +        /* ACCESS3_DELETE is ignored for now since that requires +         * knowing the permissions on the parent directory. +         */ + +        if (request & ACCESS3_EXECUTE) +                if (IA_PROT_XUSR (prot) && (!IA_ISDIR (type))) +                        accresult |= ACCESS3_EXECUTE; + +        return accresult; +} + + +uint32_t +nfs3_group_accessbits (ia_prot_t prot, ia_type_t type, uint32_t request) +{ +        uint32_t accresult = 0; + +        if (IA_PROT_RGRP (prot) && (request & ACCESS3_READ)) +                accresult |= ACCESS3_READ; + +        if (request & ACCESS3_LOOKUP) +                if ((IA_ISDIR (type)) && IA_PROT_RGRP (prot)) +                        accresult |= ACCESS3_LOOKUP; + +        if (IA_PROT_WGRP (prot) && (request & ACCESS3_MODIFY)) +                accresult |= ACCESS3_MODIFY; + +        if (IA_PROT_WGRP (prot) && (request & ACCESS3_EXTEND)) +                accresult |= ACCESS3_EXTEND; + +        /* ACCESS3_DELETE is ignored for now since that requires +         * knowing the permissions on the parent directory. +         */ + +        if (request & ACCESS3_EXECUTE) +                if (IA_PROT_XGRP (prot) && (!IA_ISDIR (type))) +                        accresult |= ACCESS3_EXECUTE; + +        return accresult; +} + + +uint32_t +nfs3_other_accessbits (ia_prot_t prot, ia_type_t type, uint32_t request) +{ +        uint32_t accresult = 0; + +        if (IA_PROT_ROTH (prot) && (request & ACCESS3_READ)) +                accresult |= ACCESS3_READ; + +        if (request & ACCESS3_LOOKUP) +                if (IA_ISDIR (type) && IA_PROT_ROTH (prot)) +                        accresult |= ACCESS3_LOOKUP; + +        if (IA_PROT_WOTH (prot) && (request & ACCESS3_MODIFY)) +                accresult |= ACCESS3_MODIFY; + +        if (IA_PROT_WOTH (prot) && (request & ACCESS3_EXTEND)) +                accresult |= ACCESS3_EXTEND; + +        /* ACCESS3_DELETE is ignored for now since that requires +         * knowing the permissions on the parent directory. +         */ + +        if (request & ACCESS3_EXECUTE) +                if (IA_PROT_XOTH (prot) && (!IA_ISDIR (type))) +                        accresult |= ACCESS3_EXECUTE; + +        return accresult; +} + + +uint32_t +nfs3_stat_to_accessbits (struct iatt *buf, uint32_t request, uid_t uid, +                         gid_t gid) +{ +        uint32_t        accresult = 0; +        ia_prot_t       prot = {0, }; +        ia_type_t       type = 0; + +        prot = buf->ia_prot; +        type = buf->ia_type; + +        if (buf->ia_uid == uid) +                accresult = nfs3_owner_accessbits (prot, type, request); +        else if (buf->ia_gid == gid) +                accresult = nfs3_group_accessbits (prot, type, request); +        else +                accresult = nfs3_other_accessbits (prot, type, request); + +        return accresult; +} + + +void +nfs3_fill_access3res (access3res *res, nfsstat3 status, struct iatt *buf, +                      uint32_t accbits, uid_t uid, gid_t gid, uint16_t xlid) +{ +        post_op_attr    objattr; +        uint32_t        accres = 0; + +        memset (res, 0, sizeof (*res)); +        res->status = status; +        if (status != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (buf, xlid); +        objattr = nfs3_stat_to_post_op_attr (buf); +        accres = nfs3_stat_to_accessbits (buf, accbits, uid, gid); + +        res->access3res_u.resok.obj_attributes = objattr; +        res->access3res_u.resok.access = accres; +} + +void +nfs3_prep_readdir3args (readdir3args *ra, struct nfs3_fh *fh) +{ +        memset (ra, 0, sizeof (*ra)); +        ra->dir.data.data_val = (void *)fh; +} + + +entry3 * +nfs3_fill_entry3 (gf_dirent_t *entry) +{ +        entry3          *ent = NULL; +        if (!entry) +                return NULL; + +        ent = CALLOC (1, sizeof (*ent)); +        if (!ent) +                return NULL; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Entry: %s", entry->d_name); +        ent->fileid = entry->d_ino; +        ent->cookie = entry->d_off; +        ent->name = CALLOC ((strlen (entry->d_name) + 1), sizeof (char)); +        if (!ent->name) { +                FREE (ent); +                ent = NULL; +                goto err; +        } +        strcpy (ent->name, entry->d_name); + +err: +        return ent; +} + + +void +nfs3_fill_post_op_fh3 (struct nfs3_fh *fh, post_op_fh3 *pfh) +{ +        uint32_t        fhlen = 0; + +        if ((!fh) || (!pfh)) +                return; + +        pfh->handle_follows = 1; +        fhlen = nfs3_fh_compute_size (fh); +        pfh->post_op_fh3_u.handle.data.data_val = (void *)fh; +        pfh->post_op_fh3_u.handle.data.data_len = fhlen; +} + + +post_op_fh3 +nfs3_fh_to_post_op_fh3 (struct nfs3_fh *fh) +{ +        post_op_fh3     pfh = {0, }; +        char            *fhp = NULL; + +        if (!fh) +                return pfh; + +        pfh.handle_follows = 1; + +        fhp = CALLOC (1, sizeof (*fh)); +        if (!fhp) +                return pfh; + +        memcpy (fhp, fh, sizeof (*fh)); +        nfs3_fill_post_op_fh3 ((struct nfs3_fh *)fhp, &pfh); +        return pfh; +} + + +entryp3 * +nfs3_fill_entryp3 (gf_dirent_t *entry, struct nfs3_fh *dirfh) +{ +        entryp3         *ent = NULL; +        struct nfs3_fh  newfh = {{0}, }; + +        if ((!entry) || (!dirfh)) +                return NULL; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Entry: %s, ino: %"PRIu64, +                entry->d_name, entry->d_ino); +        ent = CALLOC (1, sizeof (*ent)); +        if (!ent) +                return NULL; + +        ent->fileid = entry->d_ino; +        ent->cookie = entry->d_off; +        ent->name = CALLOC ((strlen (entry->d_name) + 1), sizeof (char)); +        if (!ent->name) { +                FREE (ent); +                ent = NULL; +                goto err; +        } +        strcpy (ent->name, entry->d_name); + +        nfs3_fh_build_child_fh (dirfh, &entry->d_stat, &newfh); +        nfs3_map_xlid_to_statdev (&entry->d_stat, dirfh->xlatorid); +        ent->name_attributes = nfs3_stat_to_post_op_attr (&entry->d_stat); +        ent->name_handle = nfs3_fh_to_post_op_fh3 (&newfh); +err: +        return ent; +} + + +void +nfs3_fill_readdir3res (readdir3res *res, nfsstat3 stat, uint64_t cverf, +                       struct iatt *dirstat, gf_dirent_t *entries, count3 count, +                       int is_eof, uint16_t xlid) +{ +        post_op_attr    dirattr; +        entry3          *ent = NULL; +        entry3          *headentry = NULL; +        entry3          *preventry = NULL; +        count3          filled = 0; +        gf_dirent_t     *listhead = NULL; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (dirstat, xlid); +        dirattr = nfs3_stat_to_post_op_attr (dirstat); +        res->readdir3res_u.resok.dir_attributes = dirattr; +        res->readdir3res_u.resok.reply.eof = (bool_t)is_eof; +        memcpy (res->readdir3res_u.resok.cookieverf, &cverf, sizeof (cverf)); + +        filled = NFS3_READDIR_RESOK_SIZE; +        /* First entry is just the list head */ +        listhead = entries; +        entries = entries->next; +        while (((entries) && (entries != listhead)) && (filled < count)) { +                /* +                if ((strcmp (entries->d_name, ".") == 0) || +                    (strcmp (entries->d_name, "..") == 0)) +                        goto nextentry; +                        */ +                ent = nfs3_fill_entry3 (entries); +                if (!ent) +                        break; + +                if (!headentry) +                        headentry = ent; + +                if (preventry) { +                        preventry->nextentry = ent; +                        preventry = ent; +                } else +                        preventry = ent; + +                filled += NFS3_ENTRY3_FIXED_SIZE + strlen (ent->name); +//nextentry: +                entries = entries->next; +        } + +        res->readdir3res_u.resok.reply.entries = headentry; + +        return; +} + + +void +nfs3_fill_readdirp3res (readdirp3res *res, nfsstat3 stat, struct nfs3_fh *dirfh, +                        uint64_t cverf, struct iatt *dirstat, +                        gf_dirent_t *entries, count3 dircount, count3 maxcount, +                        int is_eof) +{ +        post_op_attr    dirattr; +        entryp3         *ent = NULL; +        entryp3         *headentry = NULL; +        entryp3         *preventry = NULL; +        count3          filled = 0; +        gf_dirent_t     *listhead = NULL; +        int             fhlen = 0; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (dirstat, dirfh->xlatorid); +        dirattr = nfs3_stat_to_post_op_attr (dirstat); +        res->readdirp3res_u.resok.dir_attributes = dirattr; +        res->readdirp3res_u.resok.reply.eof = (bool_t)is_eof; +        memcpy (res->readdirp3res_u.resok.cookieverf, &cverf, sizeof (cverf)); + +        filled = NFS3_READDIR_RESOK_SIZE; +        /* First entry is just the list head */ +        listhead = entries; +        entries = entries->next; +        while (((entries) && (entries != listhead)) && (filled < maxcount)) { +                /* Linux does not display . and .. entries unless we provide +                 * these entries here. +                 */ +/*                if ((strcmp (entries->d_name, ".") == 0) || +                    (strcmp (entries->d_name, "..") == 0)) +                        goto nextentry; +                        */ +                ent = nfs3_fill_entryp3 (entries, dirfh); +                if (!ent) +                        break; + +                if (!headentry) +                        headentry = ent; + +                if (preventry) { +                        preventry->nextentry = ent; +                        preventry = ent; +                } else +                        preventry = ent; + +                fhlen = ent->name_handle.post_op_fh3_u.handle.data.data_len; +                filled += NFS3_ENTRYP3_FIXED_SIZE + fhlen + strlen (ent->name); +//nextentry: +                entries = entries->next; +        } + +        res->readdirp3res_u.resok.reply.entries = headentry; + +        return; +} + + +void +nfs3_prep_readdirp3args (readdirp3args *ra, struct nfs3_fh *fh) +{ +        memset (ra, 0, sizeof (*ra)); +        ra->dir.data.data_val = (void *)fh; +} + +void +nfs3_free_readdirp3res (readdirp3res *res) +{ +        entryp3 *ent = NULL; +        entryp3 *next = NULL; + +        if (!res) +                return; + +        ent = res->readdirp3res_u.resok.reply.entries; +        while (ent) { + +                next = ent->nextentry; +                FREE (ent->name); +                FREE (ent->name_handle.post_op_fh3_u.handle.data.data_val); +                FREE (ent); +                ent = next; +        } + +        return; +} + + +void +nfs3_free_readdir3res (readdir3res *res) +{ +        entry3 *ent = NULL; +        entry3 *next = NULL; + +        if (!res) +                return; + +        ent = res->readdir3res_u.resok.reply.entries; +        while (ent) { + +                next = ent->nextentry; +                FREE (ent->name); +                FREE (ent); +                ent = next; +        } + +        return; +} + +void +nfs3_prep_fsstat3args (fsstat3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->fsroot.data.data_val = (char *)fh; +} + + +void +nfs3_fill_fsstat3res (fsstat3res *res, nfsstat3 stat, struct statvfs *fsbuf, +                      struct iatt *postbuf, uint16_t xlid) +{ +        post_op_attr    poa; +        fsstat3resok    resok; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (postbuf, xlid); +        poa = nfs3_stat_to_post_op_attr (postbuf); +        resok.tbytes = (size3)(fsbuf->f_frsize * fsbuf->f_blocks); +        resok.fbytes = (size3)(fsbuf->f_bsize * fsbuf->f_bfree); +        resok.abytes = (size3)(fsbuf->f_bsize * fsbuf->f_bavail); +        resok.tfiles = (size3)(fsbuf->f_files); +        resok.ffiles = (size3)(fsbuf->f_ffree); +        resok.afiles = (size3)(fsbuf->f_favail); +        resok.invarsec = 0; + +        resok.obj_attributes = poa; +        res->fsstat3res_u.resok = resok; +} + + +int32_t +nfs3_sattr3_to_setattr_valid (sattr3 *sattr, struct iatt *buf, mode_t *omode) +{ +        int32_t         valid = 0; +        ia_prot_t       prot = {0, }; +        mode_t          mode = 0; + +        if (!sattr) +                return 0; + +        if (sattr->mode.set_it) { +                valid |= GF_SET_ATTR_MODE; + +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_ROWNER) { +                        mode |= S_IRUSR; +                        prot.owner.read = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_WOWNER) { +                        mode |= S_IWUSR; +                        prot.owner.write = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_XOWNER) { +                        mode |= S_IXUSR; +                        prot.owner.exec = 1; +                } + +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_RGROUP) { +                        mode |= S_IRGRP; +                        prot.group.read = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_WGROUP) { +                        mode |= S_IWGRP; +                        prot.group.write = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_XGROUP) { +                        mode |= S_IXGRP; +                        prot.group.exec = 1; +                } + +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_ROTHER) { +                        mode |= S_IROTH; +                        prot.other.read = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_WOTHER) { +                        mode |= S_IWOTH; +                        prot.other.write = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_XOTHER) { +                        mode |= S_IXOTH; +                        prot.other.exec = 1; +                } + +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_SETXUID) { +                        mode |= S_ISUID; +                        prot.suid = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_SETXGID) { +                        mode |= S_ISGID; +                        prot.sgid = 1; +                } +                if (sattr->mode.set_mode3_u.mode & NFS3MODE_SAVESWAPTXT) { +                        mode |= S_ISVTX; +                        prot.sticky = 1; +                } + +                if (buf) +                        buf->ia_prot = prot; +                /* Create fop still requires the old mode_t style argument. */ +                if (omode) +                        *omode = mode; +        } + +        if (sattr->uid.set_it) { +                valid |= GF_SET_ATTR_UID; +                if (buf) +                        buf->ia_uid = sattr->uid.set_uid3_u.uid; +        } + +        if (sattr->gid.set_it) { +                valid |= GF_SET_ATTR_GID; +                if (buf) +                        buf->ia_gid = sattr->gid.set_gid3_u.gid; +        } + +        if (sattr->size.set_it) { +                valid |= GF_SET_ATTR_SIZE; +                if (buf) +                        buf->ia_size = sattr->size.set_size3_u.size; +        } + +        if (sattr->atime.set_it == SET_TO_CLIENT_TIME) { +                valid |= GF_SET_ATTR_ATIME; +                if (buf) +                        buf->ia_atime = sattr->atime.set_atime_u.atime.seconds; +        } + +        if (sattr->atime.set_it == SET_TO_SERVER_TIME) { +                valid |= GF_SET_ATTR_ATIME; +                if (buf) +                        buf->ia_atime = time (NULL); +        } + +        if (sattr->mtime.set_it == SET_TO_CLIENT_TIME) { +                valid |= GF_SET_ATTR_MTIME; +                if (buf) +                        buf->ia_mtime = sattr->mtime.set_mtime_u.mtime.seconds; +        } + +        if (sattr->mtime.set_it == SET_TO_SERVER_TIME) { +                valid |= GF_SET_ATTR_MTIME; +                if (buf) +                        buf->ia_mtime = time (NULL); +        } + +        return valid; +} + + +wcc_data +nfs3_stat_to_wcc_data (struct iatt *pre, struct iatt *post) +{ +        wcc_data        wd = {{0}, }; + +        if (post) +                wd.after = nfs3_stat_to_post_op_attr (post); +        if (pre) +                wd.before = nfs3_stat_to_pre_op_attr (pre); + +        return wd; +} + +void +nfs3_fill_create3res (create3res *res, nfsstat3 stat, struct nfs3_fh *newfh, +                      struct iatt *newbuf, struct iatt *preparent, +                      struct iatt *postparent) +{ +        post_op_attr    poa = {0, }; +        wcc_data        dirwcc = {{0}, }; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_fill_post_op_fh3 (newfh, &res->create3res_u.resok.obj); +        nfs3_map_xlid_to_statdev (newbuf, newfh->xlatorid); +        poa = nfs3_stat_to_post_op_attr (newbuf); +        res->create3res_u.resok.obj_attributes = poa; +        if (preparent) { +                nfs3_map_xlid_to_statdev (preparent, newfh->xlatorid); +                nfs3_map_xlid_to_statdev (postparent, newfh->xlatorid); +                dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); +        } + +        res->create3res_u.resok.dir_wcc = dirwcc; +} + +void +nfs3_prep_create3args (create3args *args, struct nfs3_fh *fh, char *name) +{ + +        memset (args, 0, sizeof (*args)); +        args->where.dir.data.data_val = (void *)fh; +        args->where.name = name; +} + +void +nfs3_prep_setattr3args (setattr3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->object.data.data_val = (void *)fh; +} + + +void +nfs3_fill_setattr3res (setattr3res *res, nfsstat3 stat, struct iatt *preop, +                       struct iatt *postop, uint16_t xlid) +{ +        wcc_data        wcc; +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (preop, xlid); +        nfs3_map_xlid_to_statdev (postop, xlid); +        wcc = nfs3_stat_to_wcc_data (preop, postop); +        res->setattr3res_u.resok.obj_wcc = wcc; +} + + +void +nfs3_prep_mkdir3args (mkdir3args *args, struct nfs3_fh *dirfh, char *name) +{ + +        memset (args, 0, sizeof (*args)); +        args->where.dir.data.data_val = (void *)dirfh; +        args->where.name = name; +} + + +void +nfs3_fill_mkdir3res (mkdir3res *res, nfsstat3 stat, struct nfs3_fh *fh, +                     struct iatt *buf, struct iatt *preparent, +                     struct iatt *postparent) +{ +        wcc_data        dirwcc; +        post_op_attr    poa; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_fill_post_op_fh3 (fh, &res->mkdir3res_u.resok.obj); +        nfs3_map_xlid_to_statdev (buf, fh->xlatorid); +        poa = nfs3_stat_to_post_op_attr (buf); +        nfs3_map_xlid_to_statdev (preparent, fh->xlatorid); +        nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); +        dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); +        res->mkdir3res_u.resok.obj_attributes = poa; +        res->mkdir3res_u.resok.dir_wcc = dirwcc; + +} + + +void +nfs3_prep_symlink3args (symlink3args *args, struct nfs3_fh *dirfh, char *name, +                        char *target) +{ +        memset (args, 0, sizeof (*args)); +        args->where.dir.data.data_val = (void *)dirfh; +        args->where.name = name; +        args->symlink.symlink_data = target; +} + + +void +nfs3_fill_symlink3res (symlink3res *res, nfsstat3 stat, struct nfs3_fh *fh, +                       struct iatt *buf, struct iatt *preparent, +                       struct iatt *postparent) +{ +        wcc_data        dirwcc; +        post_op_attr    poa; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_fill_post_op_fh3 (fh, &res->symlink3res_u.resok.obj); +        nfs3_map_xlid_to_statdev (buf, fh->xlatorid); +        poa = nfs3_stat_to_post_op_attr (buf); +        nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); +        nfs3_map_xlid_to_statdev (preparent, fh->xlatorid); +        dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); +        res->symlink3res_u.resok.obj_attributes = poa; +        res->symlink3res_u.resok.dir_wcc = dirwcc; + +} + + +void +nfs3_prep_readlink3args (readlink3args *args, struct nfs3_fh *fh) +{ + +        memset (args, 0, sizeof (*args)); +        args->symlink.data.data_val = (void *)fh; +} + + +void +nfs3_fill_readlink3res (readlink3res *res, nfsstat3 stat, char *path, +                        struct iatt *buf, uint16_t xlid) +{ +        post_op_attr    poa; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; + +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (buf, xlid); +        poa = nfs3_stat_to_post_op_attr (buf); +        res->readlink3res_u.resok.data = (void *)path; +        res->readlink3res_u.resok.symlink_attributes = poa; +} + + +void +nfs3_prep_mknod3args (mknod3args *args, struct nfs3_fh *fh, char *name) +{ +        memset (args, 0, sizeof (*args)); +        args->where.dir.data.data_val = (void *)fh; +        args->where.name = name; + +} + +void +nfs3_fill_mknod3res (mknod3res *res, nfsstat3 stat, struct nfs3_fh *fh, +                     struct iatt *buf, struct iatt *preparent, +                     struct iatt *postparent) +{ +        post_op_attr    poa; +        wcc_data        wccdir; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_fill_post_op_fh3 (fh, &res->mknod3res_u.resok.obj); +        nfs3_map_xlid_to_statdev (buf, fh->xlatorid); +        poa = nfs3_stat_to_post_op_attr (buf); +        nfs3_map_xlid_to_statdev (preparent, fh->xlatorid); +        nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); +        wccdir = nfs3_stat_to_wcc_data (preparent, postparent); +        res->mknod3res_u.resok.obj_attributes = poa; +        res->mknod3res_u.resok.dir_wcc = wccdir; + +} + + +void +nfs3_fill_remove3res (remove3res *res, nfsstat3 stat, struct iatt *preparent, +                      struct iatt *postparent, uint16_t xlid) +{ +        wcc_data        dirwcc; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (preparent, xlid); +        nfs3_map_xlid_to_statdev (postparent, xlid); +        dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); +        res->remove3res_u.resok.dir_wcc = dirwcc; +} + + +void +nfs3_prep_remove3args (remove3args *args, struct nfs3_fh *fh, char *name) +{ +        memset (args, 0, sizeof (*args)); +        args->object.dir.data.data_val = (void *)fh; +        args->object.name = name; +} + + +void +nfs3_prep_rmdir3args (rmdir3args *args, struct nfs3_fh *fh, char *name) +{ +        memset (args, 0, sizeof (*args)); +        args->object.dir.data.data_val = (void *)fh; +        args->object.name = name; +} + + +void +nfs3_fill_rmdir3res (rmdir3res *res, nfsstat3 stat, struct iatt *preparent, +                     struct iatt *postparent, uint16_t xlid) +{ +        wcc_data        dirwcc; +        memset (res, 0, sizeof (*res)); +        res->status = stat; + +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (postparent, xlid); +        nfs3_map_xlid_to_statdev (preparent, xlid); +        dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); +        res->rmdir3res_u.resok.dir_wcc = dirwcc; +} + + +void +nfs3_prep_link3args (link3args *args, struct nfs3_fh *target, +                     struct nfs3_fh * dirfh, char *name) +{ +        memset (args, 0, sizeof (*args)); +        args->file.data.data_val = (void *)target; +        args->link.dir.data.data_val = (void *)dirfh; +        args->link.name = name; +} + + +void +nfs3_fill_link3res (link3res *res, nfsstat3 stat, struct iatt *buf, +                    struct iatt *preparent, struct iatt *postparent, +                    uint16_t xlid) +{ +        post_op_attr    poa; +        wcc_data        dirwcc; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (preparent, xlid); +        nfs3_map_xlid_to_statdev (postparent, xlid); +        nfs3_map_xlid_to_statdev (buf, xlid); +        poa = nfs3_stat_to_post_op_attr (buf); +        dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); +        res->link3res_u.resok.file_attributes = poa; +        res->link3res_u.resok.linkdir_wcc = dirwcc; +} + + + +void +nfs3_prep_rename3args (rename3args *args, struct nfs3_fh *olddirfh, +                       char *oldname, struct nfs3_fh *newdirfh, char *newname) +{ +        memset (args, 0, sizeof (*args)); + +        args->from.name = oldname; +        args->from.dir.data.data_val = (void *)olddirfh; +        args->to.name = newname; +        args->to.dir.data.data_val = (void *)newdirfh; + +} + + +void +nfs3_fill_rename3res (rename3res *res, nfsstat3 stat, struct iatt *buf, +                      struct iatt *preoldparent, struct iatt *postoldparent, +                      struct iatt *prenewparent, struct iatt *postnewparent, +                      uint16_t xlid) + +{ +        wcc_data        dirwcc; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (preoldparent, xlid); +        nfs3_map_xlid_to_statdev (postoldparent, xlid); +        nfs3_map_xlid_to_statdev (prenewparent, xlid); +        nfs3_map_xlid_to_statdev (postnewparent, xlid); +        nfs3_map_xlid_to_statdev (buf, xlid); +        dirwcc = nfs3_stat_to_wcc_data (preoldparent, postoldparent); +        res->rename3res_u.resok.fromdir_wcc = dirwcc; +        dirwcc = nfs3_stat_to_wcc_data (prenewparent, postnewparent); +        res->rename3res_u.resok.todir_wcc = dirwcc; +} + + +void +nfs3_prep_write3args (write3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->file.data.data_val = (void *)fh; +} + + +void +nfs3_fill_write3res (write3res *res, nfsstat3 stat, count3 count, +                     stable_how stable, uint64_t wverf, struct iatt *prestat, +                     struct iatt *poststat, uint16_t xlid) +{ +        write3resok     resok; +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (prestat, xlid); +        nfs3_map_xlid_to_statdev (poststat, xlid); +        resok.file_wcc = nfs3_stat_to_wcc_data (prestat, poststat); +        resok.count = count; +        resok.committed = stable; +        memcpy (resok.verf, &wverf, sizeof (wverf)); + +        res->write3res_u.resok = resok; +} + + +void +nfs3_prep_commit3args (commit3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->file.data.data_val = (void *)fh; +} + + +void +nfs3_fill_commit3res (commit3res *res, nfsstat3 stat, uint64_t wverf, +                      struct iatt *prestat, struct iatt *poststat,uint16_t xlid) +{ +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (poststat, xlid); +        nfs3_map_xlid_to_statdev (prestat, xlid); +        res->commit3res_u.resok.file_wcc = nfs3_stat_to_wcc_data (prestat, +                                                                  poststat); +        memcpy (res->commit3res_u.resok.verf, &wverf, sizeof (wverf)); +} + +void +nfs3_fill_read3res (read3res *res, nfsstat3 stat, count3 count, +                    struct iatt *poststat, int is_eof, uint16_t xlid) +{ +        post_op_attr    poa; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (poststat, xlid); +        poa = nfs3_stat_to_post_op_attr (poststat); +        res->read3res_u.resok.file_attributes = poa; +        res->read3res_u.resok.count = count; +        res->read3res_u.resok.eof = is_eof; +        res->read3res_u.resok.data.data_len = count; + +} + + +void +nfs3_prep_read3args (read3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->file.data.data_val = (void *)fh; +} + + +void +nfs3_fill_pathconf3res (pathconf3res *res, nfsstat3 stat, struct iatt *buf, +                        uint16_t xlid) +{ +        pathconf3resok  resok; + +        memset (res, 0, sizeof (*res)); +        res->status = stat; +        if (stat != NFS3_OK) +                return; + +        nfs3_map_xlid_to_statdev (buf, xlid); +        resok.obj_attributes = nfs3_stat_to_post_op_attr (buf); +        resok.linkmax = 256; +        resok.name_max = NFS_NAME_MAX; +        resok.no_trunc = TRUE; +        resok.chown_restricted = FALSE; +        resok.case_insensitive = FALSE; +        resok.case_preserving = TRUE; + +        res->pathconf3res_u.resok = resok; +} + + +void +nfs3_prep_pathconf3args (pathconf3args *args, struct nfs3_fh *fh) +{ +        memset (args, 0, sizeof (*args)); +        args->object.data.data_val = (void *)fh; +} + + +int +nfs3_verify_dircookie (struct nfs3_state *nfs3, fd_t *dirfd, cookie3 cookie, +                       uint64_t cverf, nfsstat3 *stat) +{ +        int             ret = -1; + +        if ((!nfs3) || (!dirfd)) +                return -1; + +        /* Can assume that this is first read on the dir, so cookie check +         * is successful by default. +         */ +        if (cookie == 0) +                return 0; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Verifying cookie: cverf: %"PRIu64 +                ", cookie: %"PRIu64, cverf, cookie); +        /* The cookie bad, no way cverf will be zero with a non-zero cookie. */ +        if ((cverf == 0) && (cookie != 0)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Bad cookie requested"); +                if (stat) +                        *stat = NFS3ERR_BAD_COOKIE; +                goto err; +        } + +        /* Yes, its true, our cookie is simply the fd_t address. +         * NOTE: We used have the check for cookieverf but VMWare client sends +         * a readdirp requests even after we've told it that EOF has been +         * reached on the directory. This causes a problem because we close a +         * dir fd_t after reaching EOF. The next readdirp sent by VMWare +         * contains the address of the closed fd_t as cookieverf. Since we +         * closed that fd_t, this readdirp results in a new opendir which will +         * give an fd_t that will fail this check below. +         */ +/*        if ((cverf != (uint64_t)dirfd)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Cookieverf does not match"); +                if (stat) +                        *stat = NFS3ERR_BAD_COOKIE; +                goto err; +        } +*/ +        gf_log (GF_NFS3, GF_LOG_TRACE, "Cookie verified"); +        if (stat) +                *stat = NFS3_OK; +        ret = 0; +err: +        return ret; +} + + +/* When remove a file, we need to unref the cached fd for an inode but this + * needs to happen only when the file was in fact opened. However, it is + * possible that fd_lookup on a file returns an fd because the file was in + * process of being created(which also returns an fd) but since this fd was not + * opened through this path, in the NFS3 remove path, we'll end up removing the + * reference that belongs to someone else. That means, nfs3 remove path should + * not unref unless it is sure that the file was cached open also. If it was, + * only then perform the fd_unref, else not. + * + * We determine that using a flag in the inode context. + */ +int +nfs3_set_inode_opened (xlator_t *nfsxl, inode_t *inode) +{ +        if ((!nfsxl) || (!inode)) +                return -1; + +        inode_ctx_put (inode, nfsxl, GF_NFS3_FD_CACHED); + +        return 0; +} + + +/* Returns 1 if inode was cached open, otherwise 0 */ +int +nfs3_cached_inode_opened (xlator_t *nfsxl, inode_t *inode) +{ +        int             ret = -1; +        uint64_t        cflag = 0; + +        if ((!nfsxl) || (!inode)) +                return -1; + +        ret = inode_ctx_get (inode, nfsxl, &cflag); +        if (ret == -1) +                ret = 0; +        else if (cflag == GF_NFS3_FD_CACHED) +                ret = 1; + +        return ret; +} + + +int32_t +nfs3_dir_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                   int32_t op_ret, int32_t op_errno, fd_t *fd) +{ +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                cs->resolve_ret = -1; +                cs->resolve_errno = op_errno; +                nfs3_call_resume (cs); +                goto err; +        } + +        cs->fd = fd_ref (fd); +        nfs3_set_inode_opened (cs->nfsx, cs->resolvedloc.inode); +        gf_log (GF_NFS3, GF_LOG_TRACE, "FD_REF: %d", fd->refcount); +        nfs3_call_resume (cs); +err: +        return 0; +} + + +int +__nfs3_dir_open_and_resume (nfs3_call_state_t *cs) +{ +        nfs_user_t      nfu = {0, }; +        int             ret = -EFAULT; + +        if (!cs) +                return ret; + +        nfs_user_root_create (&nfu); +        ret = nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, nfs3_dir_open_cbk, +                           cs); +        return ret; +} + + +int +nfs3_dir_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume) +{ +        fd_t    *fd = NULL; +        int     ret = -EFAULT; + +        if ((!cs)) +                return ret; + +        cs->resume_fn = resume; +        gf_log (GF_NFS3, GF_LOG_TRACE, "Opening: %s", cs->resolvedloc.path); +        fd = fd_lookup (cs->resolvedloc.inode, 0); +        if (fd) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "fd found in state: ref: %d", fd->refcount); +                cs->fd = fd_ref (fd);    /* Gets unrefd when the call state is wiped. */ +                cs->resolve_ret = 0; +                nfs3_call_resume (cs); +                ret = 0; +                goto err; +        } + +        ret = __nfs3_dir_open_and_resume (cs); + +err: +        return ret; +} + + +int +nfs3_flush_open_wait_call_states (nfs3_call_state_t *cs, fd_t *openedfd) +{ +        struct list_head        *inode_q = NULL; +        uint64_t                ctxaddr = 0; +        int                     ret = 0; +        nfs3_call_state_t       *cstmp = NULL; + +        if (!cs) +                return -1; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Flushing call state"); +        ret = inode_ctx_get (cs->resolvedloc.inode, cs->nfsx, &ctxaddr); +        if (ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "No inode queue present"); +                goto out; +        } + +        inode_q = (struct list_head *)(long)ctxaddr; +        if (!inode_q) +                goto out; + +        list_for_each_entry_safe (cs, cstmp, inode_q, openwait_q) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Calling resume"); +                cs->resolve_ret = 0; +                if (openedfd) { +                        gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd done: %d", +                        openedfd->refcount); +                        cs->fd = fd_ref (openedfd); +                } +                nfs3_call_resume (cs); +        } + +out: +        return 0; +} + + + +int +__nfs3_fdcache_update_entry (struct nfs3_state *nfs3, fd_t *fd) +{ +        uint64_t                ctxaddr = 0; +        struct nfs3_fd_entry    *fde = NULL; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Updating fd: 0x%lx", (long int)fd); +        fd_ctx_get (fd, nfs3->nfsx, &ctxaddr); +        fde = (struct nfs3_fd_entry *)(long)ctxaddr; +        list_del (&fde->list); +        list_add_tail (&fde->list, &nfs3->fdlru); + +        return 0; +} + + +int +nfs3_fdcache_update (struct nfs3_state *nfs3, fd_t *fd) +{ +        if ((!nfs3) || (!fd)) +                return -1; + +        LOCK (&nfs3->fdlrulock); +        { +                __nfs3_fdcache_update_entry (nfs3, fd); +        } +        UNLOCK (&nfs3->fdlrulock); + +        return 0; +} + + +int +__nfs3_fdcache_remove_entry (struct nfs3_state *nfs3, struct nfs3_fd_entry *fde) +{ +        gf_log (GF_NFS3, GF_LOG_TRACE, "Removing fd: 0x%lx: %d", +                (long int)fde->cachedfd, fde->cachedfd->refcount); +        list_del (&fde->list); +        fd_ctx_del (fde->cachedfd, nfs3->nfsx, NULL); +        fd_unref (fde->cachedfd); +        FREE (fde); +        --nfs3->fdcount; + +        return 0; +} + + +int +nfs3_fdcache_remove (struct nfs3_state *nfs3, fd_t *fd) +{ +        struct nfs3_fd_entry    *fde = NULL; +        uint64_t                ctxaddr = 0; + +        if ((!nfs3) || (!fd)) +                return -1; + +        LOCK (&nfs3->fdlrulock); +        { +                fd_ctx_get (fd, nfs3->nfsx, &ctxaddr); +                fde = (struct nfs3_fd_entry *)(long)ctxaddr; +                __nfs3_fdcache_remove_entry (nfs3, fde); +        } +        UNLOCK (&nfs3->fdlrulock); + +        return 0; +} + + +int +__nfs3_fdcache_replace (struct nfs3_state *nfs3) +{ +        struct nfs3_fd_entry    *fde = NULL; +        struct nfs3_fd_entry    *tmp = NULL; + +        if (!nfs3) +                return -1; + +        if (nfs3->fdcount <= GF_NFS3_FDCACHE_SIZE) +                return 0; + +        list_for_each_entry_safe (fde, tmp, &nfs3->fdlru, list) +                break; + +        __nfs3_fdcache_remove_entry (nfs3, fde); + +        return 0; +} + + + +int +nfs3_fdcache_add (struct nfs3_state *nfs3, fd_t *fd) +{ +        struct nfs3_fd_entry    *fde = NULL; +        int                     ret = -1; + +        if ((!nfs3) || (!fd)) +                return -1; + +        fde = CALLOC (1, sizeof (*fd)); +        if (!fde) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "fd entry allocation failed"); +                goto out; +        } + +        /* Already refd by caller. */ +        fde->cachedfd = fd; +        INIT_LIST_HEAD (&fde->list); + +        LOCK (&nfs3->fdlrulock); +        { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Adding fd: 0x%lx", +                        (long int) fd); +                fd_ctx_set (fd, nfs3->nfsx, (uint64_t)fde); +                fd_bind (fd); +                list_add_tail (&fde->list, &nfs3->fdlru); +                ++nfs3->fdcount; +                __nfs3_fdcache_replace (nfs3); +        } +        UNLOCK (&nfs3->fdlrulock); + +out: +        return ret; +} + + +int32_t +nfs3_file_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, fd_t *fd) +{ +        nfs3_call_state_t       *cs = NULL; +        struct nfs3_state       *nfs3 = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd failed"); +                cs->resolve_ret = -1; +                cs->resolve_errno = op_errno; +                fd = NULL; +        } else { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd done: %d", +                        fd->refcount); +        } + +        nfs3 = rpcsvc_request_program_private (cs->req); +        nfs3_flush_open_wait_call_states (cs, fd); +        nfs3_fdcache_add (nfs3, fd); +        return 0; +} + +/* Returns 1 if the current call is the first one to be queued. If so, the + * caller will need to send the open fop. If this is a non-first call to be + * queued, it means the fd opening is in progress. + * + * Returns 0, if this is a non-first call. + */ +int +__nfs3_queue_call_state (nfs3_call_state_t *cs) +{ +        struct list_head        *inode_q = NULL; +        int                     ret = -1; +        uint64_t                ctxaddr = 0; + +        ret = __inode_ctx_get (cs->resolvedloc.inode, cs->nfsx, &ctxaddr); +        if (ret == 0) { +                inode_q = (struct list_head *)(long)ctxaddr; +                goto attach_cs; +        } + +        inode_q = CALLOC (1, sizeof (*inode_q)); +        if (!inode_q) +                goto err; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Initing inode queue"); +        INIT_LIST_HEAD (inode_q); +        __inode_ctx_put (cs->resolvedloc.inode, cs->nfsx, (uint64_t)inode_q); + +attach_cs: +        if (list_empty (inode_q)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "First call in queue"); +                ret = 1; +        } else +                ret = 0; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Queueing call state"); +        list_add_tail (&cs->openwait_q, inode_q); + +err: +        return ret; +} + + +int +nfs3_queue_call_state (nfs3_call_state_t *cs) +{ +        int     ret = 0; +        if (!cs) +                return -1; + +        LOCK (&cs->resolvedloc.inode->lock); +        { +                ret = __nfs3_queue_call_state (cs); +        } +        UNLOCK (&cs->resolvedloc.inode->lock); + +        return ret; +} + + +int +__nfs3_file_open_and_resume (nfs3_call_state_t *cs) +{ +        nfs_user_t      nfu = {0, }; +        int             ret = -EFAULT; + +        if (!cs) +                return ret; + +        ret = nfs3_queue_call_state (cs); +        if (ret != 1) { +                ret = 0; +                goto out; +        } + +        nfs_user_root_create (&nfu); +        gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd"); +        ret = nfs_open (cs->vol, &nfu, &cs->resolvedloc, O_RDWR, +                        nfs3_file_open_cbk, cs); +out: +        return ret; +} + + + +int +nfs3_file_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume) +{ +        fd_t                    *fd = NULL; +        int                     ret = -EFAULT; +        struct nfs3_state       *nfs3 = NULL; + +        if ((!cs)) +                return ret; + +        cs->resume_fn = resume; +        gf_log (GF_NFS3, GF_LOG_TRACE, "Opening: %s", cs->resolvedloc.path); +        fd = fd_lookup (cs->resolvedloc.inode, 0); +        if (fd) { +                nfs3 = rpcsvc_request_program_private (cs->req); +                /* Already refd by fd_lookup, so no need to ref again. */ +                gf_log (GF_NFS3, GF_LOG_TRACE, "fd found in state: %d", +                        fd->refcount); +                nfs3_fdcache_update (nfs3, fd); +                cs->fd = fd;    /* Gets unrefd when the call state is wiped. */ +                cs->resolve_ret = 0; +                nfs3_call_resume (cs); +                ret = 0; +                goto err; +        } + +        ret = __nfs3_file_open_and_resume (cs); + +err: +        return ret; +} + + +void +nfs3_stat_to_errstr (uint32_t xid, char *op, nfsstat3 stat, int pstat, +                     char *errstr) +{ +        if ((!op) || (!errstr)) +                return; + +        sprintf (errstr, "XID: %x, %s: NFS: %d(%s), POSIX: %d(%s)", xid, op, +                 stat, nfsstat3_strerror (stat), pstat, strerror (pstat)); +} + +void +nfs3_log_common_call (uint32_t xid, char *op, struct nfs3_fh *fh) +{ +        char    fhstr[1024]; + +        nfs3_fh_to_str (fh, fhstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s", xid, op, +                fhstr); +} + + +void +nfs3_log_fh_entry_call (uint32_t xid, char *op, struct nfs3_fh *fh, +                        char *name) +{ +        char    fhstr[1024]; + +        nfs3_fh_to_str (fh, fhstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s, name: %s", xid, +                op, fhstr, name); +} + + +void +nfs3_log_rename_call (uint32_t xid, struct nfs3_fh *src, char *sname, +                      struct nfs3_fh *dst, char *dname) +{ +        char    sfhstr[1024]; +        char    dfhstr[1024]; + +        nfs3_fh_to_str (src, sfhstr); +        nfs3_fh_to_str (dst, dfhstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, RENAME: args: Src: %s, " +                "name: %s, Dst: %s, name: %s", xid, sfhstr, sname, dfhstr, +                dname); +} + + + +void +nfs3_log_create_call (uint32_t xid, struct nfs3_fh *fh, char *name, +                      createmode3 mode) +{ +        char    fhstr[1024]; +        char    *modestr = NULL; +        char    exclmode[] = "EXCLUSIVE"; +        char    unchkd[] = "UNCHECKED"; +        char    guarded[] = "GUARDED"; + +        nfs3_fh_to_str (fh, fhstr); +        if (mode == EXCLUSIVE) +                modestr = exclmode; +        else if (mode == GUARDED) +                modestr = guarded; +        else +                modestr = unchkd; + +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, CREATE: args: %s, name: %s," +                " mode: %s", xid, fhstr, name, modestr); +} + + +void +nfs3_log_mknod_call (uint32_t xid, struct nfs3_fh *fh, char *name, int type) +{ +        char    fhstr[1024]; +        char    *modestr = NULL; +        char    chr[] = "CHAR"; +        char    blk[] = "BLK"; +        char    sock[] = "SOCK"; +        char    fifo[] = "FIFO"; + +        nfs3_fh_to_str (fh, fhstr); +        if (type == NF3CHR) +                modestr = chr; +        else if (type == NF3BLK) +                modestr = blk; +        else if (type == NF3SOCK) +                modestr = sock; +        else +                modestr = fifo; + +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, MKNOD: args: %s, name: %s," +                " type: %s", xid, fhstr, name, modestr); +} + + + +void +nfs3_log_symlink_call (uint32_t xid, struct nfs3_fh *fh, char *name, char *tgt) +{ +        char    fhstr[1024]; + +        nfs3_fh_to_str (fh, fhstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, SYMLINK: args: %s, name: %s," +                " target: %s", xid, fhstr, name, tgt); +} + + +void +nfs3_log_link_call (uint32_t xid, struct nfs3_fh *fh, char *name, +                    struct nfs3_fh *tgt) +{ +        char    dfhstr[1024]; +        char    tfhstr[1024]; + +        nfs3_fh_to_str (fh, dfhstr); +        nfs3_fh_to_str (tgt, tfhstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, LINK: args: %s, name: %s," +                " target: %s", xid, dfhstr, name, tfhstr); +} + + +void +nfs3_log_rw_call (uint32_t xid, char *op, struct nfs3_fh *fh, offset3 offt, +                  count3 count, int stablewrite) +{ +        char    fhstr[1024]; + +        nfs3_fh_to_str (fh, fhstr); +        if (stablewrite == -1) +                gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s, offset:" +                        " %"PRIu64",  count: %"PRIu64, xid, op, fhstr, offt, +                        count); +        else +                gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s, offset:" +                        " %"PRIu64",  count: %"PRIu64", %s", xid, op, fhstr, +                        offt, count, +                        (stablewrite == UNSTABLE)?"UNSTABLE":"STABLE"); + +} + + +void +nfs3_log_common_res (uint32_t xid, char *op, nfsstat3 stat, int pstat) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, op, stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s", errstr); +} + +void +nfs3_log_readlink_res (uint32_t xid, nfsstat3 stat, int pstat, char *linkpath) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, "READLINK", stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, target: %s", errstr, linkpath); + +} + +void +nfs3_log_read_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, +                   int is_eof) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, "READ", stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, count: %"PRIu64", is_eof: %d", +                errstr, count, is_eof); +} + + +void +nfs3_log_write_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, +                    int stable, uint64_t wverf) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, "WRITE", stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, count: %"PRIu64", %s,wverf: %"PRIu64 +                , errstr, count, (stable == UNSTABLE)?"UNSTABLE":"STABLE", +                wverf); +} + + +void +nfs3_log_newfh_res (uint32_t xid, char *op, nfsstat3 stat, int pstat, +                    struct nfs3_fh *newfh) +{ +        char    errstr[1024]; +        char    fhstr[1024]; + +        nfs3_stat_to_errstr (xid, op, stat, pstat, errstr); +        nfs3_fh_to_str (newfh, fhstr); + +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, %s", errstr, fhstr); +} + + +void +nfs3_log_readdir_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, +                      count3 count, int is_eof) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, "READDIR", stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, count: %"PRIu64", cverf: %"PRIu64 +                ", is_eof: %d", errstr, count, cverf, is_eof); +} + + +void +nfs3_log_readdirp_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, +                       count3 dircount, count3 maxcount, int is_eof) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, "READDIRPLUS", stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, dircount: %"PRIu64", maxcount: %" +                PRIu64", cverf: %"PRIu64", is_eof: %d", errstr, dircount, +                maxcount, cverf, is_eof); +} + + +void +nfs3_log_commit_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t wverf) +{ +        char    errstr[1024]; + +        nfs3_stat_to_errstr (xid, "COMMIT", stat, pstat, errstr); +        gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, wverf: %"PRIu64, errstr, wverf); +} + + +void +nfs3_log_readdir_call (uint32_t xid, struct nfs3_fh *fh, count3 dircount, +                       count3 maxcount) +{ +        char    fhstr[1024]; + +        nfs3_fh_to_str (fh, fhstr); + +        if (maxcount == 0) +                gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, READDIR: args: %s," +                        " count: %d", xid, fhstr, (uint32_t)dircount); +        else +                gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, READDIRPLUS: args: %s," +                        " dircount: %d, maxcount: %d", xid, fhstr, +                        (uint32_t)dircount, (uint32_t)maxcount); +} + + +int +nfs3_fh_resolve_inode_done (nfs3_call_state_t *cs, inode_t *inode) +{ +        int             ret = -EFAULT; + +        if ((!cs) || (!inode)) +                return ret; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "FH inode resolved"); +        ret = nfs_inode_loc_fill (inode, &cs->resolvedloc); +        if (ret < 0) +                goto err; + +        nfs3_call_resume (cs); + +err: +        return ret; +} + +#define GF_NFS3_FHRESOLVE_FOUND         1 +#define GF_NFS3_FHRESOLVE_NOTFOUND      2 +#define GF_NFS3_FHRESOLVE_DIRFOUND      3 + +int +nfs3_fh_resolve_check_entry (struct nfs3_fh *fh, gf_dirent_t *candidate, +                             int hashidx) +{ +        struct iatt             *ia = NULL; +        int                     ret = GF_NFS3_FHRESOLVE_NOTFOUND; +        nfs3_hash_entry_t       entryhash = 0; + +        if ((!fh) || (!candidate)) +                return ret; + +        if ((strcmp (candidate->d_name, ".") == 0) || +            (strcmp (candidate->d_name, "..") == 0)) +                goto found_entry; + +        ia = &candidate->d_stat; +        if ((ia->ia_gen == fh->gen) && (ia->ia_ino == fh->ino)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Found entry: gen: %"PRId64 +                        " ino: %"PRId64", name: %s", ia->ia_gen, ia->ia_ino, +                        candidate->d_name); +                ret = GF_NFS3_FHRESOLVE_FOUND; +                goto found_entry; +        } + +        /* This condition ensures that we never have to be afraid of having +         * a directory hash conflict with a file hash. The consequence of +         * this condition is that we can now have unlimited files in a directory +         * and upto 65536 sub-directories in a directory. +         */ +        if (!IA_ISDIR (candidate->d_stat.ia_type)) +                goto found_entry; +        entryhash = fh->entryhash[hashidx]; +        if (entryhash == nfs3_fh_hash_entry (ia->ia_ino, ia->ia_gen)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Found hash match: %s: %d", +                        candidate->d_name, entryhash); +                ret = GF_NFS3_FHRESOLVE_DIRFOUND; +                goto found_entry; +        } + +found_entry: + +        return ret; +} + + +int32_t +nfs3_fh_resolve_entry_lookup_cbk (call_frame_t *frame, void *cookie, +                                  xlator_t *this, int32_t op_ret, +                                  int32_t op_errno, inode_t *inode, +                                  struct iatt *buf, dict_t *xattr, +                                  struct iatt *postparent) +{ +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        cs->resolve_ret = op_ret; +        cs->resolve_errno = op_errno; + +        if (op_ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Lookup failed: %s: %s", +                        cs->resolvedloc.path, strerror (op_errno)); +                goto err; +        } else +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry looked up: %s", +                        cs->resolvedloc.path); + +        inode_link (inode, cs->resolvedloc.parent, cs->resolvedloc.name, buf); +err: +        nfs3_call_resume (cs); +        return 0; +} + + + +int32_t +nfs3_fh_resolve_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                             int32_t op_ret, int32_t op_errno, +                             gf_dirent_t *entries); + +int +nfs3_fh_resolve_found_entry (nfs3_call_state_t *cs, gf_dirent_t *candidate) +{ +        uint64_t        dirino = 0; +        uint64_t        dirgen = 0; +        int             ret = 0; +        nfs_user_t      nfu = {0, }; + +        if ((!cs) || (!candidate)) +                return -EFAULT; + +        dirino = cs->resolvedloc.inode->ino; +        dirgen = cs->resolvedloc.inode->generation; + +        nfs_loc_wipe (&cs->resolvedloc); +        ret = nfs_entry_loc_fill (cs->vol->itable, dirino, dirgen, +                                  candidate->d_name, &cs->resolvedloc, +                                  NFS_RESOLVE_CREATE); +        if (ret == -ENOENT) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry not in itable, needs" +                        " lookup"); +                nfs_user_root_create (&nfu); +                ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, +                                      nfs3_fh_resolve_entry_lookup_cbk, +                                      cs); +        } else { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry got from itable"); +                nfs3_call_resume (cs); +        } + +        return ret; +} + + +int32_t +nfs3_fh_resolve_parent_lookup_cbk (call_frame_t *frame, void *cookie, +                                  xlator_t *this, int32_t op_ret, +                                  int32_t op_errno, inode_t *inode, +                                  struct iatt *buf, dict_t *xattr, +                                  struct iatt *postparent) +{ +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        cs->resolve_ret = op_ret; +        cs->resolve_errno = op_errno; + +        if (op_ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Lookup failed: %s: %s", +                        cs->resolvedloc.path, strerror (op_errno)); +                nfs3_call_resume (cs); +                goto err; +        } else +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry looked up: %s", +                        cs->resolvedloc.path); + +        inode_link (inode, cs->resolvedloc.parent, cs->resolvedloc.name, buf); +        nfs3_fh_resolve_entry_hard (cs); + +err: +        return 0; +} + + +int +nfs3_fh_resolve_found_parent (nfs3_call_state_t *cs, gf_dirent_t *candidate) +{ +        uint64_t        dirino = 0; +        uint64_t        dirgen = 0; +        int             ret = 0; +        nfs_user_t      nfu = {0, }; + +        if ((!cs) || (!candidate)) +                return -EFAULT; + +        dirino = cs->resolvedloc.inode->ino; +        dirgen = cs->resolvedloc.inode->generation; + +        nfs_loc_wipe (&cs->resolvedloc); +        ret = nfs_entry_loc_fill (cs->vol->itable, dirino, dirgen, +                                  candidate->d_name, &cs->resolvedloc, +                                  NFS_RESOLVE_CREATE); +        if (ret == -ENOENT) { +                nfs_user_root_create (&nfu); +                ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, +                                      nfs3_fh_resolve_parent_lookup_cbk, +                                      cs); +        } else +                nfs3_fh_resolve_entry_hard (cs); + +        return ret; +} + + +int +nfs3_fh_resolve_found (nfs3_call_state_t *cs, gf_dirent_t *candidate) +{ +        int             ret = 0; + +        if ((!cs) || (!candidate)) +                return -EFAULT; + +        if (!cs->resolventry) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Candidate entry was found"); +                ret = nfs3_fh_resolve_found_entry (cs, candidate); +        } else { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry's parent was found"); +                ret = nfs3_fh_resolve_found_parent (cs, candidate); +        } + +        return ret; +} + + +int32_t +nfs3_fh_resolve_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                             int32_t op_ret, int32_t op_errno, fd_t *fd) +{ +        nfs3_call_state_t       *cs = NULL; +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; + +        cs = frame->local; +        cs->resolve_ret = op_ret; +        cs->resolve_errno = op_errno; + +        if (op_ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Dir open failed: %s: %s", +                        cs->resolvedloc.path, strerror (op_errno)); +                nfs3_call_resume (cs); +                goto err; +        } else +                gf_log (GF_NFS3, GF_LOG_TRACE, "Reading directory: %s", +                        cs->resolvedloc.path); + +        nfs_user_root_create (&nfu); +        /* This function can be called in a recursive code path, so if another +         * directory was opened in an earlier call, we must unref through this +         * reference before opening another fd_t. +         */ +        if (cs->resolve_dir_fd) +                fd_unref (cs->resolve_dir_fd); + +        cs->resolve_dir_fd = fd_ref (fd); +        ret = nfs_readdirp (cs->vol, &nfu, fd, GF_NFS3_DTPREF, 0, +                            nfs3_fh_resolve_readdir_cbk, cs); + +err: +        return ret; +} + +int32_t +nfs3_fh_resolve_dir_lookup_cbk (call_frame_t *frame, void *cookie, +                                xlator_t *this, int32_t op_ret,int32_t op_errno, +                                inode_t *inode, struct iatt *buf, dict_t *xattr, +                                struct iatt *postparent) +{ +        nfs3_call_state_t       *cs = NULL; +        nfs_user_t              nfu = {0, }; + +        cs = frame->local; +        cs->resolve_ret = op_ret; +        cs->resolve_errno = op_errno; + +        if (op_ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Lookup failed: %s: %s", +                        cs->resolvedloc.path, strerror (op_errno)); +                nfs3_call_resume (cs); +                goto err; +        } else +                gf_log (GF_NFS3, GF_LOG_TRACE, "Dir will be opened: %s", +                        cs->resolvedloc.path); + +        nfs_user_root_create (&nfu); +        inode_link (inode, cs->resolvedloc.parent, cs->resolvedloc.name, buf); +        nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, +                     nfs3_fh_resolve_opendir_cbk, cs); + +err: +        return 0; +} + + + +int +nfs3_fh_resolve_dir_hard (nfs3_call_state_t *cs, uint64_t ino, uint64_t gen, +                          char *entry) +{ +        int             ret = -EFAULT; +        nfs_user_t      nfu = {0, }; + +        if (!cs) +                return ret; + +        cs->hashidx++; +        nfs_loc_wipe (&cs->resolvedloc); +        if (nfs3_fh_hash_index_is_beyond (&cs->resolvefh, cs->hashidx)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Hash index is beyond: idx %d, " +                        " fh idx: %d", cs->hashidx, cs->resolvefh.hashcount); +                nfs3_call_resume_estale (cs); +                ret = 0; +                goto out; +        } + +        nfs_user_root_create (&nfu); +        gf_log (GF_NFS3, GF_LOG_TRACE, "FH hard dir resolution: ino:" +                " %"PRIu64", gen: %"PRIu64", entry: %s, hashidx: %d", +                ino, gen, entry, cs->hashidx); +        ret = nfs_entry_loc_fill (cs->vol->itable, ino, gen, entry, +                                  &cs->resolvedloc, NFS_RESOLVE_CREATE); + +        if (ret == 0) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Dir will be opened: %s", +                        cs->resolvedloc.path); +                ret = nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, +                                   nfs3_fh_resolve_opendir_cbk, cs); +        } else if (ret == -ENOENT) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Dir needs lookup: %s", +                        cs->resolvedloc.path); +                ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, +                                      nfs3_fh_resolve_dir_lookup_cbk, cs); +        } +out: +        return ret; +} + + +int +nfs3_fh_resolve_check_response (nfs3_call_state_t *cs, gf_dirent_t *candidate, +                                int response, off_t last_offt) +{ +        uint64_t        dirino = 0; +        uint64_t        dirgen = 0; +        int             ret = -EFAULT; +        nfs_user_t      nfu = {0, }; + +        if (!cs) +                return ret; + +        dirino = cs->resolvedloc.inode->ino; +        dirgen = cs->resolvedloc.inode->generation; + +        if (response == GF_NFS3_FHRESOLVE_DIRFOUND) +                ret = nfs3_fh_resolve_dir_hard (cs, dirino, dirgen, +                                                candidate->d_name); +        else if (response == GF_NFS3_FHRESOLVE_FOUND) +                nfs3_fh_resolve_found (cs, candidate); +        else if (response == GF_NFS3_FHRESOLVE_NOTFOUND) { +                nfs_user_root_create (&nfu); +                ret = nfs_readdirp (cs->vol, &nfu, cs->resolve_dir_fd, +                                    GF_NFS3_DTPREF, last_offt, +                                    nfs3_fh_resolve_readdir_cbk, cs); +        } + +        return 0; +} + +int +nfs3_fh_resolve_search_dir (nfs3_call_state_t *cs, gf_dirent_t *entries) +{ +        gf_dirent_t     *candidate = NULL; +        int             ret = GF_NFS3_FHRESOLVE_NOTFOUND; +        off_t           lastoff = 0; + +        if ((!cs) || (!entries)) +                return -EFAULT; + +        if (list_empty (&entries->list)) +                goto not_found; + +        list_for_each_entry (candidate, &entries->list, list) { +                lastoff = candidate->d_off; +                gf_log (GF_NFS3, GF_LOG_TRACE, "Candidate: %s, ino: %"PRIu64 +                        ", gen: %"PRIu64, candidate->d_name, candidate->d_ino, +                        candidate->d_stat.ia_gen); +                ret = nfs3_fh_resolve_check_entry (&cs->resolvefh, candidate, +                                                   cs->hashidx); +                if (ret != GF_NFS3_FHRESOLVE_NOTFOUND) +                        break; +        } + +not_found: +        nfs3_fh_resolve_check_response (cs, candidate, ret, lastoff); +        return ret; +} + + +int32_t +nfs3_fh_resolve_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                             int32_t op_ret, int32_t op_errno, +                             gf_dirent_t *entries) +{ +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret <= 0) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Directory read done: %s: %s", +                        cs->resolvedloc.path, strerror (op_ret)); +                cs->resolve_ret = -1; +                cs->resolve_errno = ENOENT; +                nfs3_call_resume (cs); +                goto err; +        } + +        nfs3_fh_resolve_search_dir (cs, entries); + +err: +        return 0; +} + +/* Needs no extra argument since it knows that the fh to be resolved is in + * resolvefh and that it needs to start looking from the root. + */ +int +nfs3_fh_resolve_inode_hard (nfs3_call_state_t *cs) +{ +        int             ret = -EFAULT; +        nfs_user_t      nfu = {0, }; + +        if (!cs) +                return ret; + +        cs->hashidx++; +        nfs_loc_wipe (&cs->resolvedloc); +        if (nfs3_fh_hash_index_is_beyond (&cs->resolvefh, cs->hashidx)) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Hash index is beyond: idx %d, " +                        " fh idx: %d", cs->hashidx, cs->resolvefh.hashcount); +                nfs3_call_resume_estale (cs); +                ret = 0; +                goto out; +        } + +        nfs_user_root_create (&nfu); +        gf_log (GF_NFS3, GF_LOG_TRACE, "FH hard resolution: ino:" +                " %"PRIu64", gen: %"PRIu64", hashidx: %d", cs->resolvefh.ino, +                cs->resolvefh.gen, cs->hashidx); +        ret = nfs_ino_loc_fill (cs->vol->itable, 1, 0, &cs->resolvedloc); + +        if (ret == 0) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Dir will be opened: %s", +                        cs->resolvedloc.path); +                ret = nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, +                                   nfs3_fh_resolve_opendir_cbk, cs); +        } else if (ret == -ENOENT) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Dir needs lookup: %s", +                        cs->resolvedloc.path); +                ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, +                                      nfs3_fh_resolve_dir_lookup_cbk, cs); +        } + +out: +        return ret; +} + + +int +nfs3_fh_resolve_entry_hard (nfs3_call_state_t *cs) +{ +        int             ret = -EFAULT; +        nfs_user_t      nfu = {0, }; + +        if (!cs) +                return ret; + +        nfs_loc_wipe (&cs->resolvedloc); +        nfs_user_root_create (&nfu); +        gf_log (GF_NFS3, GF_LOG_TRACE, "FH hard resolution: ino:" +                " %"PRIu64", gen: %"PRIu64", entry: %s, hashidx: %d", +                cs->resolvefh.ino, cs->resolvefh.gen, cs->resolventry, +                cs->hashidx); + +        ret = nfs_entry_loc_fill (cs->vol->itable, cs->resolvefh.ino, +                                  cs->resolvefh.gen, cs->resolventry, +                                  &cs->resolvedloc, NFS_RESOLVE_CREATE); + +        if (ret == -2) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry needs lookup: %s", +                        cs->resolvedloc.path); +                ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, +                                      nfs3_fh_resolve_entry_lookup_cbk, cs); +                ret = 0; +        } else if (ret == -1) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Entry needs parent lookup: %s", +                        cs->resolvedloc.path); +                ret = nfs3_fh_resolve_inode_hard (cs); +        } else if (ret == 0) { +                cs->resolve_ret = 0; +                nfs3_call_resume (cs); +        } + +        return ret; +} + +int +nfs3_fh_resolve_inode (nfs3_call_state_t *cs) +{ +        inode_t         *inode = NULL; +        int             ret = -EFAULT; + +        if (!cs) +                return ret; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "FH needs inode resolution"); +        inode = inode_get (cs->vol->itable, cs->resolvefh.ino, +                           cs->resolvefh.gen); +        if (!inode) +                ret = nfs3_fh_resolve_inode_hard (cs); +        else +                ret = nfs3_fh_resolve_inode_done (cs, inode); + +        if (inode) +                inode_unref (inode); + +        return ret; +} + +int +nfs3_fh_resolve_entry (nfs3_call_state_t *cs) +{ +        int     ret = -EFAULT; + +        if (!cs) +                return ret; + +        ret = nfs3_fh_resolve_entry_hard (cs); +        if (ret < 0) { +                cs->resolve_ret = -1; +                cs->resolve_errno = ESTALE; +                nfs3_call_resume (cs); +        } + +        return 0; +} + +int +nfs3_fh_resolve_and_resume (nfs3_call_state_t *cs, struct nfs3_fh *fh, +                            char *entry, nfs3_resume_fn_t resum_fn) +{ +        int     ret = -EFAULT; + +        if ((!cs) || (!fh)) +                return ret; + +        cs->resume_fn = resum_fn; +        cs->resolvefh = *fh; +        cs->hashidx = 0; + +        if (!entry) +                ret = nfs3_fh_resolve_inode (cs); +        else { +                cs->resolventry = strdup (entry); +                if (!cs->resolventry) +                        goto err; + +                ret = nfs3_fh_resolve_entry (cs); +        } + +err: +        return ret; +} + + diff --git a/xlators/nfs/server/src/nfs3-helpers.h b/xlators/nfs/server/src/nfs3-helpers.h new file mode 100644 index 00000000000..a282bce6722 --- /dev/null +++ b/xlators/nfs/server/src/nfs3-helpers.h @@ -0,0 +1,342 @@ +/* +  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#ifndef _NFS3_HELPER_H_ +#define _NFS3_HELPER_H_ +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + + +#include "xlator.h" +#include "nfs3.h" +#include "nfs3-fh.h" +#include "msg-nfs3.h" +#include "xdr-nfs3.h" + +#include <sys/statvfs.h> + +#define GF_NFS3_FD_CACHED       0xcaced + +extern struct nfs3_fh +nfs3_extract_lookup_fh (lookup3args *args); + +extern char * +nfs3_extract_lookup_name (lookup3args *args); + +extern nfsstat3 +nfs3_errno_to_nfsstat3 (int errnum); + +extern void +nfs3_fill_lookup3res (lookup3res *res, nfsstat3 stat, struct nfs3_fh *newfh, +                      struct iatt *stbuf, struct iatt *postparent); + +extern post_op_attr +nfs3_stat_to_post_op_attr (struct iatt *buf); + +extern struct nfs3_fh +nfs3_extract_getattr_fh (getattr3args *args); + +extern void +nfs3_fill_getattr3res (getattr3res *res, nfsstat3 stat, struct iatt *buf, +                       uint16_t xlid); + +extern struct nfs3_fh +nfs3_extract_fsinfo_fh (fsinfo3args *args); + +extern void +nfs3_fill_fsinfo3res (struct nfs3_state *nfs3, fsinfo3res *res, +                      nfsstat3 status, struct iatt *fsroot, uint16_t xlid); + +/* Functions containing _prep_ are used specifically to work around + * the memory allocations that happen inside Sun RPC library. + * In that library, there are numerous places where every NFS request + * can result in really tiny malloc calls. I fear the memory fragmentation + * that will follow. After studying the points at and the way in which malloc + * is called in Sun RPC, I've come up with this work-around. It is based on + * the idea that if the user/caller of the xdr_to_XXXXargs functions can provide + * already allocated memory or provide references to memory areas on its stack + * just for the short-term purpose of decoding the message from XDR format, we + * can avoid the memory allocations in Sun RPC. This is based on the fact + * that Sun RPC first checks whether structure members which require memory + * are NULL or not and only then calls malloc. In this case, if the caller + * provided references are non-NULL, then the if-branches containing malloc + * in Sun RPC will be avoided. + * PS: You're not expected to understand this unless you've spent some time + * looking through the glibc/sunrpc sources. + */ +extern void +nfs3_prep_lookup3args (lookup3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_prep_getattr3args (getattr3args *args, struct nfs3_fh *fh); + +extern void +nfs3_prep_fsinfo3args (fsinfo3args *args, struct nfs3_fh *root); + +extern char * +nfsstat3_strerror(int stat); + +extern void +nfs3_prep_access3args (access3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_access3res (access3res *res, nfsstat3 status, struct iatt *buf, +                      uint32_t accbits, uid_t uid, gid_t gid, uint16_t xlid); + +extern char * +nfs3_fhcache_getpath (struct nfs3_state *nfs3, struct nfs3_fh *fh); + +extern int +nfs3_fhcache_putpath (struct nfs3_state *nfs3, struct nfs3_fh *fh, char *path); + +extern void +nfs3_prep_readdir3args (readdir3args *ra, struct nfs3_fh *fh); + +extern void +nfs3_fill_readdir3res (readdir3res *res, nfsstat3 stat, uint64_t cverf, +                       struct iatt *dirstat, gf_dirent_t *entries,count3 count, +                       int is_eof, uint16_t xlid); + +extern void +nfs3_prep_readdirp3args (readdirp3args *ra, struct nfs3_fh *fh); + +extern void +nfs3_fill_readdirp3res (readdirp3res *res, nfsstat3 stat, struct nfs3_fh *dirfh, +                        uint64_t cverf, struct iatt *dirstat, +                        gf_dirent_t *entries, count3 dircount, count3 maxcount, +                        int is_eof); + +extern void +nfs3_free_readdirp3res (readdirp3res *res); + +extern void +nfs3_free_readdir3res (readdir3res *res); + +extern void +nfs3_prep_fsstat3args (fsstat3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_fsstat3res (fsstat3res *res, nfsstat3 stat, struct statvfs *fsbuf, +                      struct iatt *postbuf, uint16_t xlid); + +extern int32_t +nfs3_sattr3_to_setattr_valid (sattr3 *sattr, struct iatt *buf, mode_t *omode); +extern void +nfs3_fill_create3res (create3res *res, nfsstat3 stat, struct nfs3_fh *newfh, +                      struct iatt *newbuf, struct iatt *preparent, +                      struct iatt *postparent); + +extern void +nfs3_prep_create3args (create3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_prep_setattr3args (setattr3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_setattr3res (setattr3res *res, nfsstat3 stat, struct iatt *preop, +                       struct iatt *postop, uint16_t xlid); + +extern void +nfs3_prep_mkdir3args (mkdir3args *args, struct nfs3_fh *dirfh, char *name); + +extern void +nfs3_fill_mkdir3res (mkdir3res *res, nfsstat3 stat, struct nfs3_fh *fh, +                     struct iatt *buf, struct iatt *preparent, +                     struct iatt *postparent); + +extern void +nfs3_prep_symlink3args (symlink3args *args, struct nfs3_fh *dirfh, char *name, +                        char *target); + +extern void +nfs3_fill_symlink3res (symlink3res *res, nfsstat3 stat, struct nfs3_fh *fh, +                       struct iatt *buf, struct iatt *preparent, +                       struct iatt *postparent); + +extern void +nfs3_prep_readlink3args (readlink3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_readlink3res (readlink3res *res, nfsstat3 stat, char *path, +                        struct iatt *buf, uint16_t xlid); + +extern void +nfs3_prep_mknod3args (mknod3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_fill_mknod3res (mknod3res *res, nfsstat3 stat, struct nfs3_fh *fh, +                     struct iatt *buf, struct iatt *preparent, +                     struct iatt *postparent); + +extern void +nfs3_fill_remove3res (remove3res *res, nfsstat3 stat, struct iatt *preparent, +                      struct iatt *postparent, uint16_t xlid); +extern void +nfs3_prep_remove3args (remove3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_fill_rmdir3res (rmdir3res *res, nfsstat3 stat, struct iatt *preparent, +                     struct iatt *postparent, uint16_t xlid); + +extern void +nfs3_prep_rmdir3args (rmdir3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_fill_link3res (link3res *res, nfsstat3 stat, struct iatt *buf, +                    struct iatt *preparent, struct iatt *postparent, +                    uint16_t xlid); + +extern void +nfs3_prep_link3args (link3args *args, struct nfs3_fh *target, +                     struct nfs3_fh * dirfh, char *name); + +extern void +nfs3_prep_rename3args (rename3args *args, struct nfs3_fh *olddirfh, +                       char *oldname, struct nfs3_fh *newdirfh, +                       char *newname); + +extern void +nfs3_fill_rename3res (rename3res *res, nfsstat3 stat, struct iatt *buf, +                      struct iatt *preoldparent, struct iatt *postoldparent, +                      struct iatt *prenewparent, struct iatt *postnewparent, +                      uint16_t xlid); + +extern void +nfs3_prep_write3args (write3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_write3res (write3res *res, nfsstat3 stat, count3 count, +                     stable_how stable, uint64_t wverf, struct iatt *prestat, +                     struct iatt *poststat, uint16_t xlid); + +extern void +nfs3_prep_commit3args (commit3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_commit3res (commit3res *res, nfsstat3 stat, uint64_t wverf, +                      struct iatt *prestat, struct iatt *poststat, +                      uint16_t xlid); + +extern void +nfs3_fill_read3res (read3res *res, nfsstat3 stat, count3 count, +                    struct iatt *poststat, int is_eof, uint16_t xlid); + +extern void +nfs3_prep_read3args (read3args *args, struct nfs3_fh *fh); + +extern void +nfs3_prep_pathconf3args (pathconf3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_pathconf3res (pathconf3res *res, nfsstat3 stat, struct iatt *buf, +                        uint16_t xlid); + +extern int +nfs3_cached_inode_opened (xlator_t *nfsxl, inode_t *inode); + +extern void +nfs3_log_common_res (uint32_t xid, char *op, nfsstat3 stat, int pstat); + +extern void +nfs3_log_readlink_res (uint32_t xid, nfsstat3 stat, int pstat, char *linkpath); + +extern void +nfs3_log_read_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, +                   int is_eof); + +extern void +nfs3_log_write_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, +                    int stable, uint64_t wverf); + +extern void +nfs3_log_newfh_res (uint32_t xid, char *op, nfsstat3 stat, int pstat, +                    struct nfs3_fh *newfh); + +extern void +nfs3_log_readdir_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, +                      count3 count, int is_eof); + +extern void +nfs3_log_readdirp_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, +                       count3 dircount, count3 maxcount, int is_eof); + +extern void +nfs3_log_commit_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t wverf); + +extern void +nfs3_log_common_call (uint32_t xid, char *op, struct nfs3_fh *fh); + +extern void +nfs3_log_fh_entry_call (uint32_t xid, char *op, struct nfs3_fh *fh, +                        char *name); + +extern void +nfs3_log_rw_call (uint32_t xid, char *op, struct nfs3_fh *fh, offset3 offt, +                  count3 count, int stablewrite); + +extern void +nfs3_log_create_call (uint32_t xid, struct nfs3_fh *fh, char *name, +                      createmode3 mode); + +extern void +nfs3_log_symlink_call (uint32_t xid, struct nfs3_fh *fh, char *name, char *tgt); + +extern void +nfs3_log_mknod_call (uint32_t xid, struct nfs3_fh *fh, char *name, int type); + +extern void +nfs3_log_rename_call (uint32_t xid, struct nfs3_fh *src, char *sname, +                      struct nfs3_fh *dst, char *dname); + +extern void +nfs3_log_link_call (uint32_t xid, struct nfs3_fh *fh, char *name, +                    struct nfs3_fh *tgt); + +extern void +nfs3_log_readdir_call (uint32_t xid, struct nfs3_fh *fh, count3 dircount, +                       count3 maxcount); + +extern int +nfs3_fh_resolve_entry_hard (nfs3_call_state_t *cs); + +extern int +nfs3_fh_resolve_inode (nfs3_call_state_t *cs); + +extern int +nfs3_fh_resolve_entry (nfs3_call_state_t *cs); + +extern int +nfs3_fh_resolve_and_resume (nfs3_call_state_t *cs, struct nfs3_fh *fh, +                            char *entry, nfs3_resume_fn_t resum_fn); + +extern int +nfs3_file_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume); + +extern int +nfs3_dir_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume); + +extern int +nfs3_verify_dircookie (struct nfs3_state *nfs3, fd_t *dirfd, cookie3 cookie, +                       uint64_t cverf, nfsstat3 *stat); + +extern int +nfs3_fdcache_remove (struct nfs3_state *nfs3, fd_t *fd); +#endif diff --git a/xlators/nfs/server/src/nfs3.c b/xlators/nfs/server/src/nfs3.c new file mode 100644 index 00000000000..41e474b396d --- /dev/null +++ b/xlators/nfs/server/src/nfs3.c @@ -0,0 +1,4836 @@ +/* +  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "rpcsvc.h" +#include "dict.h" +#include "xlator.h" +#include "mount3.h" +#include "xdr-nfs3.h" +#include "msg-nfs3.h" +#include "iobuf.h" +#include "nfs3.h" +#include "mem-pool.h" +#include "logging.h" +#include "nfs-fops.h" +#include "nfs-inodes.h" +#include "nfs-generics.h" +#include "nfs3-helpers.h" + + +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/statvfs.h> +#include <time.h> + +#define nfs3_validate_strlen_or_goto(str, len, label, status, retval)   \ +        do      {                                                       \ +                if ((str)) {                                            \ +                        if (strlen ((str)) > (len)) {                   \ +                                status = NFS3ERR_NAMETOOLONG;           \ +                                retval = -ENAMETOOLONG;                 \ +                                goto label;                             \ +                        }                                               \ +                }                                                       \ +        } while (0);                                                    \ + +#define nfs3_validate_nfs3_state(request, state, status, label, retval) \ +        do      {                                                       \ +                state = rpcsvc_request_program_private (request);       \ +                if (!nfs3) {                                            \ +                        gf_log (GF_NFS3, GF_LOG_ERROR, "NFSv3 state "   \ +                                "missing from RPC request");            \ +                        status = NFS3ERR_SERVERFAULT;                   \ +                        ret = -EFAULT;                                  \ +                        goto label;                                     \ +                }                                                       \ +        } while (0);                                                    \ + +#define nfs3_export_access(nfs3state, xlid) ((nfs3state)->exports[xlid]).access + +#define nfs3_check_rw_volaccess(nfs3state, xlid, status, label)          \ +        do {                                                             \ +                if (nfs3_export_access (nfs3state,xlid)!=GF_NFS3_VOLACCESS_RW){\ +                        gf_log (GF_NFS3, GF_LOG_TRACE, "No read-write access");\ +                        status = NFS3ERR_ROFS;                          \ +                        goto label;                                     \ +                }                                                       \ +        } while (0)                                                     \ + + + +#define nfs3_map_fh_to_volume(nfs3state, handle, rqst, volume, status, label) \ +        do {                                                            \ +                volume = nfs3_fh_to_xlator ((nfs3state)->exportslist, handle); \ +                if (!volume) {                                          \ +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to map " \ +                                "FH to vol");                           \ +                        status = NFS3ERR_STALE;                         \ +                        goto label;                                     \ +                } else {                                                \ +                        gf_log (GF_NFS3, GF_LOG_TRACE, "FH to Volume: %s"\ +                                ,volume->name);                         \ +                        rpcsvc_request_set_private (req, volume);       \ +                }                                                       \ +        } while (0);                                                    \ + + +#define nfs3_validate_gluster_fh(handle, status, errlabel)              \ +        do {                                                            \ +                if ((handle)) {                                         \ +                        if (!nfs3_fh_validate (handle)) {               \ +                                status = NFS3ERR_BADHANDLE;             \ +                                goto errlabel;                          \ +                        }                                               \ +                }                                                       \ +        } while (0)                                                     \ + +#define nfs3_check_fh_resolve_status(cst, nfstat, erlabl)               \ +        do {                                                            \ +                if ((cst)->resolve_ret == -1) {                         \ +                        nfstat = nfs3_errno_to_nfsstat3 (cst->resolve_errno);\ +                        goto erlabl;                                    \ +                }                                                       \ +        } while (0)                                                     \ + +#define nfs3_check_new_fh_resolve_status(cst, nfstat, erlabl)           \ +        do {                                                            \ +                if (((cst)->resolve_ret == -1) &&                       \ +                    ((cst)->resolve_errno != ENOENT)) {                 \ +                        nfstat = nfs3_errno_to_nfsstat3 (cs->resolve_errno);\ +                        goto erlabl;                                    \ +                }                                                       \ +        } while (0)                                                     \ + + +#define nfs3_funge_solaris_zerolen_fh(nfs3st, fhd, enam, nfsst, erl)    \ +        do {                                                            \ +                xlator_t        *fungexl = NULL;                        \ +                fungexl =nfs_mntpath_to_xlator ((nfs3st)->exportslist,enam);\ +                if (!fungexl) {                                         \ +                        (nfsst) = NFS3ERR_NOENT;                        \ +                        goto erl;                                       \ +                }                                                       \ +                                                                        \ +                (fhd)->xlatorid = nfs_xlator_to_xlid ((nfs3st)->exportslist, \ +                                                      fungexl);         \ +                (fhd)->gen = 0;                                         \ +                (fhd)->ino = 1;                                         \ +                (enam) = NULL;                                          \ +        } while (0)                                                     \ + + +int +nfs3_solaris_zerolen_fh (struct nfs3_fh *fh, int fhlen) +{ +        if (!fh) +                return 0; + +        if (nfs3_fh_validate (fh)) +                return 0; + +        if (fhlen == 0) +                return 1; + +        return 0; +} + + +/* Function pointer that represents the generic prototypes of functions used + * to serialize NFS3 message structures into the XDR format. + * For usage, see the nfs3svc_XXX_cbk functions. + */ +typedef ssize_t (*nfs3_serializer) (struct iovec outmsg, void *args); + +nfs3_call_state_t * +nfs3_call_state_init (struct nfs3_state *s, rpcsvc_request_t *req, xlator_t *v) +{ +        nfs3_call_state_t       *cs = NULL; + +        if ((!s) || (!req) || (!v)) +                return NULL; + +        cs = (nfs3_call_state_t *) mem_get (s->localpool); +        if (!cs) +                return NULL; + +        memset (cs, 0, sizeof (*cs)); +        INIT_LIST_HEAD (&cs->entries.list); +        INIT_LIST_HEAD (&cs->openwait_q); +        cs->operrno = EINVAL; +        cs->req = req; +        cs->vol = v; +        cs->nfsx = s->nfsx; + +        return cs; +} + +void +nfs3_call_state_wipe (nfs3_call_state_t *cs) +{ +        struct nfs3_state       *nfs3 = NULL; +        if (!cs) +                return; + +        nfs3 = rpcsvc_request_program_private (cs->req); +        if (cs->fd) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "fd ref: %d", cs->fd->refcount); +                fd_unref (cs->fd); +        } + +        if (cs->resolve_dir_fd) +                fd_unref (cs->resolve_dir_fd); + +        if (cs->resolventry) +                FREE (cs->resolventry); + +        if (cs->pathname) +                FREE (cs->pathname); + +        if (!list_empty (&cs->entries.list)) +                gf_dirent_free (&cs->entries); + +        list_del (&cs->openwait_q); +        nfs_loc_wipe (&cs->oploc); +        nfs_loc_wipe (&cs->resolvedloc); +        if (cs->iob) +                iobuf_unref (cs->iob); +        memset (cs, 0, sizeof (*cs)); +        mem_put (nfs3->localpool, cs); +        /* Already refd by fd_lookup, so no need to ref again. */ +} + + +#define nfs3_handle_call_state_init(nfs3state, calls, rq, vl ,opstat, errlabel)\ +        do {                                                            \ +                calls = nfs3_call_state_init ((nfs3state), (rq), (vl)); \ +                if (!calls) {                                           \ +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to "     \ +                                "init call state");                     \ +                        opstat = NFS3ERR_SERVERFAULT;                   \ +                        goto errlabel;                                  \ +                }                                                       \ +        } while (0)                                                     \ + + + +struct iobuf * +nfs3_serialize_reply (rpcsvc_request_t *req, void *arg, nfs3_serializer sfunc, +                      struct iovec *outmsg) +{ +        struct nfs3_state       *nfs3 = NULL; +        struct iobuf            *iob = NULL; +        ssize_t                 retlen = -1; + +        nfs3 = (struct nfs3_state *)rpcsvc_request_program_private (req); +        if (!nfs3) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "NFSv3 state not found in RPC" +                        " request"); +                goto ret; +        } + +        /* First, get the io buffer into which the reply in arg will +         * be serialized. +         */ +        iob = iobuf_get (nfs3->iobpool); +        if (!iob) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to get iobuf"); +                goto ret; +        } + +        iobuf_to_iovec (iob, outmsg); +        /* Use the given serializer to translate the give C structure in arg +         * to XDR format which will be written into the buffer in outmsg. +         */ +        /* retlen is used to received the error since size_t is unsigned and we +         * need -1 for error notification during encoding. +         */ +        retlen = sfunc (*outmsg, arg); +        if (retlen == -1) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to encode message"); +                goto ret; +        } + +        outmsg->iov_len = retlen; +ret: +        if (retlen == -1) { +                iobuf_unref (iob); +                iob = NULL; +        } + +        return iob; +} + + + +/* Generic reply function for NFSv3 specific replies. */ +int +nfs3svc_submit_reply (rpcsvc_request_t *req, void *arg, nfs3_serializer sfunc) +{ +        struct iovec            outmsg = {0, }; +        struct iobuf            *iob = NULL; +        int                     ret = -1; + +        if (!req) +                return -1; + +        iob = nfs3_serialize_reply (req, arg, sfunc, &outmsg); +        if (!iob) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to serialize reply"); +                goto ret; +        } + +        /* Then, submit the message for transmission. */ +        ret = rpcsvc_submit_message (req, outmsg, iob); + +        /* Now that we've done our job of handing the message to the RPC layer +         * we can safely unref the iob in the hope that RPC layer must have +         * ref'ed the iob on receiving into the txlist. +         */ +        iobuf_unref (iob); +        if (ret == -1) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Reply submission failed"); +                goto ret; +        } + +        ret = 0; +ret: +        return ret; +} + + +int +nfs3svc_submit_vector_reply (rpcsvc_request_t *req, void *arg, +                             nfs3_serializer sfunc, struct iovec *payload, +                             struct iobref *piobref) +{ +        struct iovec            outmsg = {0, }; +        struct iobuf            *iob = NULL; +        int                     ret = -1; + +        if (!req) +                return -1; + +        iob = nfs3_serialize_reply (req, arg, sfunc, &outmsg); +        if (!iob) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to serialize reply"); +                goto err; +        } + +        ret = rpcsvc_request_attach_vector (req, outmsg, iob, NULL, 0); +        iobuf_unref (iob); + +        if (piobref) +                ret = rpcsvc_request_attach_vector (req, *payload, NULL, piobref +                                                    , 1); + +        if (ret == -1) +                goto err; +        ret = rpcsvc_submit_vectors (req); +err: + +        return ret; +} + + +uint16_t +nfs3_request_xlator_id (rpcsvc_request_t *rq) +{ +        struct nfs3_state       *nfs3 = NULL; +        xlator_t                *xl = NULL; + +        if (!rq) +                return 0; + +        xl = rpcsvc_request_private (rq); +        nfs3 = rpcsvc_request_program_private (rq); +        return nfs_xlator_to_xlid (nfs3->exportslist, xl); +} + + +int +nfs3svc_null (rpcsvc_request_t *req) +{ +        struct iovec    dummyvec = {0, }; +        if (!req) +                return RPCSVC_ACTOR_ERROR; + +        rpcsvc_submit_generic (req, dummyvec, NULL); +        return RPCSVC_ACTOR_SUCCESS; +} + + +int +nfs3_getattr_reply (rpcsvc_request_t *req, nfsstat3 status, struct iatt *buf) +{ +        getattr3res     res; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_getattr3res (&res, status, buf, xlid); +        nfs3svc_submit_reply (req, &res, +                              (nfs3_serializer)xdr_serialize_getattr3res); + +        return 0; +} + + +int32_t +nfs3svc_getattr_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                            int32_t op_ret, int32_t op_errno, inode_t *inode, +                            struct iatt *buf, dict_t *xattr, +                            struct iatt *postparent) +{ +        rpcsvc_request_t        *req = NULL; +        nfsstat3                status = NFS3_OK; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        req = cs->req; + +        if (op_ret == -1) +                status = nfs3_errno_to_nfsstat3 (op_errno); + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "GETATTR", status, +                             op_errno); + +        nfs3_getattr_reply (cs->req, status, buf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int32_t +nfs3svc_getattr_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                          int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        rpcsvc_request_t        *req = NULL; +        nfsstat3                status = NFS3_OK; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        req = cs->req; + +        if (op_ret == -1) +                status = nfs3_errno_to_nfsstat3 (op_errno); + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "GETATTR", status, +                             op_errno); + +        nfs3_getattr_reply (cs->req, status, buf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3_getattr_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        /* If inode which is to be getattr'd is the root, we need to do a +         * lookup instead because after a server reboot, it is not necessary +         * for the root to have been looked up when the getattr on the root is +         * sent. AND, this causes a problem for stat-prefetch in that it +         * expects even the root inode to have been looked up. +         */ +        if (cs->resolvedloc.inode->ino == 1) +                ret = nfs_lookup (cs->vol, &nfu, &cs->resolvedloc, +                                  nfs3svc_getattr_lookup_cbk, cs); +        else +                ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, +                                nfs3svc_getattr_stat_cbk, cs); + +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Stat fop failed: %s: %s", +                        cs->oploc.path, strerror (-ret)); +                stat = nfs3_errno_to_nfsstat3 (-ret); +        } + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "GETATTR", +                                     stat, -ret); +                nfs3_getattr_reply (cs->req, stat, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3_getattr (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cstate = NULL; + +        if ((!req) || (!fh)) +                return -1; + +        nfs3_log_common_call (rpcsvc_request_xid (req), "GETATTR", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cstate, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cstate, fh, NULL,nfs3_getattr_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "GETATTR", stat, +                                     -ret); +                nfs3_getattr_reply (req, stat, NULL); +                ret = 0; +                nfs3_call_state_wipe (cstate); +        } + +        return ret; +} + + +int +nfs3svc_getattr (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        getattr3args            args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; + +        nfs3_prep_getattr3args (&args, &fh); +        if (xdr_to_getattr3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_getattr (req, &fh); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "GETATTR procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_setattr_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *preop, +                    struct iatt *postop) +{ +        setattr3res     res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_setattr3res (&res, stat, preop, postop, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer) xdr_serialize_setattr3res); +        return 0; +} + + +int32_t +nfs3svc_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                      int32_t op_ret, int32_t op_errno, struct iatt *prebuf, +                      struct iatt *postbuf) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        struct iatt             *prestat = NULL; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        /* If the first stat was got from the guarded setattr callback, or +         * from an earlier setattr call then we'll need to use that stat +         * instead of the preop returned here. +         */ +        if (cs->preparent.ia_ino != 0) +                prestat = &cs->preparent; +        else +                prestat = prebuf; + +        stat = NFS3_OK; +nfs3err: +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", stat, +                             op_errno); +        nfs3_setattr_reply (cs->req, stat, prestat, postbuf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int32_t +nfs3svc_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                     int32_t op_ret, int32_t op_errno, struct iatt *preop, +                     struct iatt *postop) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -1; +        struct iatt             *prebuf = NULL; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        /* If the first stat was got from the guarded setattr callback, then +         * we'll need to use that stat instead of the preop returned here. +         */ +        if (cs->preparent.ia_ino != 0) +                prebuf = &cs->preparent; +        else { +                prebuf = preop; +                /* Store the current preop in case we need to send a truncate, +                 * in which case the preop to be returned will be this one. +                 */ +                cs->preparent = *preop; +        } + +        ret = 0; +        /* Only truncate if the size is not already same as the requested +         * truncation and also only if this is not a directory. +         */ +        if ((gf_attr_size_set (cs->setattr_valid)) && +            (!IA_ISDIR (postop->ia_type))) { +                nfs_request_user_init (&nfu, cs->req); +                ret = nfs_truncate (cs->vol, &nfu, &cs->resolvedloc, +                                    cs->stbuf.ia_size, nfs3svc_truncate_cbk,cs); + +                if (ret < 0) +                        stat = nfs3_errno_to_nfsstat3 (-ret); +        } else { +                ret = -1;       /* Force a reply in the branch below. */ +                stat = NFS3_OK; +        } + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", +                                     stat, op_errno); +                nfs3_setattr_reply (cs->req, stat, prebuf, postop); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + + +int32_t +nfs3svc_setattr_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                          int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + +        int                     ret = -EFAULT; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        if (buf->ia_ctime != cs->timestamp.seconds) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Timestamps not in sync"); +                stat = NFS3ERR_NOT_SYNC; +                goto nfs3err; +        } + +        /* Not a clean way but no motivation to add a new member to local. */ +        cs->preparent = *buf; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, +                           cs->setattr_valid, nfs3svc_setattr_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", +                                     stat, op_errno); +                nfs3_setattr_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +nfs3_setattr_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        /* If no ctime check is required, head straight to setting the attrs. */ +        if (cs->sattrguardcheck) +                ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, +                                nfs3svc_setattr_stat_cbk, cs); +        else +                ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, +                                   cs->setattr_valid, nfs3svc_setattr_cbk, cs); + +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", +                                     stat, -ret); +                nfs3_setattr_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_setattr (rpcsvc_request_t *req, struct nfs3_fh *fh, sattr3 *sattr, +              sattrguard3 *guard) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh) || (!sattr) || (!guard)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_common_call (rpcsvc_request_xid (req), "SETATTR", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, &cs->stbuf, +                                                          NULL); +        if (guard->check) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Guard check required"); +                cs->timestamp = guard->sattrguard3_u.obj_ctime; +                cs->sattrguardcheck = 1; +        } else { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Guard check not required"); +                cs->sattrguardcheck = 0; +        } + +        if (!cs->setattr_valid) { +                ret = -EINVAL;  /* Force a reply */ +                stat = NFS3_OK; +                goto nfs3err; +        } + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_setattr_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "SETATTR", stat, +                                     -ret); +                nfs3_setattr_reply (req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + + +int +nfs3svc_setattr (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        setattr3args            args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; + +        nfs3_prep_setattr3args (&args, &fh); +        if (xdr_to_setattr3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_setattr (req, &fh, &args.new_attributes, &args.guard); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "SETATTR procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; + +} + + +int +nfs3_lookup_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *newfh, +                   struct iatt *stbuf, struct iatt *postparent) +{ +        lookup3res      res = {0, }; + +        nfs3_fill_lookup3res (&res, stat, newfh, stbuf, postparent); +        return nfs3svc_submit_reply (req, &res, +                                     (nfs3_serializer)xdr_serialize_lookup3res); +} + + +int +nfs3svc_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, inode_t *inode, +                    struct iatt *buf, dict_t *xattr, struct iatt *postparent) +{ +        struct nfs3_fh                  newfh = {{0}, }; +        nfsstat3                        status = NFS3_OK; +        nfs3_call_state_t               *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                status = nfs3_errno_to_nfsstat3 (op_errno); +                goto xmit_res; +        } + +        nfs3_fh_build_child_fh (&cs->parent, buf, &newfh); + +xmit_res: +        nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "LOOKUP", status, +                            op_errno, &newfh); +        nfs3_lookup_reply (cs->req, status, &newfh, buf, postparent); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3svc_lookup_parentdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                              int32_t op_ret, int32_t op_errno, inode_t *inode, +                              struct iatt *buf, dict_t *xattr, +                              struct iatt *postparent) +{ +        struct nfs3_fh                  newfh = {{0}, }; +        nfsstat3                        status = NFS3_OK; +        nfs3_call_state_t               *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                status = nfs3_errno_to_nfsstat3 (op_errno); +                goto xmit_res; +        } + +        nfs3_fh_build_parent_fh (&cs->fh, buf, &newfh); + +xmit_res: +        nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "LOOKUP", status, +                            op_errno, &newfh); +        nfs3_lookup_reply (cs->req, status, &newfh, buf, postparent); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + + +int +nfs3_lookup_parentdir_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; +        inode_t                         *parent = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); + +        /* At this point now, the loc in cs is for the directory file handle +         * sent by the client. This loc needs to be transformed into a loc that +         * represents the parent dir of cs->resolvedloc.inode. +         */ +        nfs_request_user_init (&nfu, cs->req); + +        /* Save the file handle from the LOOKUP request. We'll use this to +         * build the file handle of the parent directory. +         */ +        cs->fh = cs->resolvefh; +        parent = inode_ref (cs->resolvedloc.parent); +        nfs_loc_wipe (&cs->resolvedloc); +        ret = nfs_inode_loc_fill (parent, &cs->resolvedloc); +        if (ret < 0) +                goto errtostat; + +        ret = nfs_lookup (cs->vol, &nfu, &cs->resolvedloc, +                          nfs3svc_lookup_parentdir_cbk, cs); +errtostat: +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LOOKUP", +                                     stat, -ret); +                nfs3_lookup_reply (cs->req, stat, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        if (parent) +                inode_unref (parent); + +        return ret; +} + + +int +nfs3_lookup_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        cs->parent = cs->resolvefh; +        ret = nfs_lookup (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_lookup_cbk, +                          cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LOOKUP", +                                     stat, -ret); +                nfs3_lookup_reply (cs->req, stat, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_is_parentdir_entry (char *entry) +{ +        int     ret = 0; + +        if (!entry) +                return 0; + +        if (strcmp (entry, "..") == 0) +                ret = 1; + +        return ret; +} + + +int +nfs3_lookup (rpcsvc_request_t *req, struct nfs3_fh *fh, int fhlen, char *name) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh) || (!name)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "LOOKUP", fh, name); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        if (nfs3_solaris_zerolen_fh (fh, fhlen)) +                nfs3_funge_solaris_zerolen_fh (nfs3, fh, name, stat, nfs3err); +        else +                nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        if (!nfs3_is_parentdir_entry (name)) +                ret = nfs3_fh_resolve_and_resume (cs, fh, name, +                                                  nfs3_lookup_resume); +        else +                ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, +                                                  nfs3_lookup_parentdir_resume); + +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "LOOKUP", stat, +                                     -ret); +                nfs3_lookup_reply (req, stat, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_lookup (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          fh = {{0}, }; +        lookup3args             args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; + +        nfs3_prep_lookup3args (&args, &fh, name); +        if (xdr_to_lookup3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_lookup (req, &fh, args.what.dir.data.data_len, name); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "LOOKUP procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_access_reply (rpcsvc_request_t *req, nfsstat3 status, struct iatt *buf, +                   uint32_t accbits) +{ +        access3res      res; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_access3res (&res, status, buf, accbits, +                              rpcsvc_request_uid (req), rpcsvc_request_gid (req) +                              , xlid); +        nfs3svc_submit_reply (req, &res, +                              (nfs3_serializer)xdr_serialize_access3res); +        return 0; +} + + +int32_t +nfs3svc_access_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        nfsstat3                status = NFS3_OK; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; + +        if (op_ret == -1) +                status = nfs3_errno_to_nfsstat3 (op_errno); + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "ACCESS", status, +                             op_errno); +        nfs3_access_reply (cs->req, status, buf, cs->accessbits); +        nfs3_call_state_wipe (cs); + +        return 0; +} + +int +nfs3_access_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        cs->fh = cs->resolvefh; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_access_cbk,cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "ACCESS", +                                     stat, -ret); +                nfs3_access_reply (cs->req, stat, NULL, 0); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3_access (rpcsvc_request_t *req, struct nfs3_fh *fh, uint32_t accbits) +{ +        xlator_t                *vol = NULL; +        struct nfs3_state       *nfs3 = NULL; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; + +        if ((!req) || (!fh)) +                return -1; + +        nfs3_log_common_call (rpcsvc_request_xid (req), "ACCESS", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); +        cs->accessbits = accbits; + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_access_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "ACCESS", stat, +                                     -ret); +                nfs3_access_reply (req, stat, NULL, 0); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_access (rpcsvc_request_t *req) +{ +        struct nfs3_fh  fh = {{0}, }; +        access3args     args; +        int             ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; + +        nfs3_prep_access3args (&args, &fh); +        if (xdr_to_access3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_access (req, &fh, args.access); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "ACCESS procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_readlink_reply (rpcsvc_request_t *req, nfsstat3 stat, char *path, +                     struct iatt *buf) +{ +        readlink3res    res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_readlink3res (&res, stat, path, buf, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_readlink3res); + +        return 0; +} + + +int32_t +nfs3svc_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                      int32_t op_ret, int32_t op_errno, const char *path, +                      struct iatt *buf) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        stat = NFS3_OK; + +nfs3err: +        nfs3_log_readlink_res (rpcsvc_request_xid (cs->req), stat, op_errno, +                               (char *)path); +        nfs3_readlink_reply (cs->req, stat, (char *)path, buf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3_readlink_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; +        nfs_user_t                      nfu = {0, }; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_readlink (cs->vol, &nfu, &cs->resolvedloc, +                            nfs3svc_readlink_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READLINK", +                                     stat, -ret); +                nfs3_readlink_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_readlink (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_common_call (rpcsvc_request_xid (req), "READLINK", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_readlink_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "READLINK", stat, +                                     -ret); +                nfs3_readlink_reply (req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_readlink (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        readlink3args           args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; + +        nfs3_prep_readlink3args (&args, &fh); +        if (xdr_to_readlink3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_readlink (req, &fh); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "READLINK procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_read_reply (rpcsvc_request_t *req, nfsstat3 stat, +                 count3 count, struct iovec *vec, struct iobref *iobref, +                 struct iatt *poststat, int is_eof) +{ +        read3res                res = {0, }; +        uint16_t                xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_read3res (&res, stat, count, poststat, is_eof, xlid); +        if (stat == NFS3_OK) { +                /* iob can be zero if the file size was zero. If so, op_ret +                 * would be 0 and count = 0. +                 */ +                if (count != 0) { +                        xdr_bytes_round_up (vec, 1048576); +                        nfs3svc_submit_vector_reply (req, (void *)&res, +                                                     (nfs3_serializer) +                                                  xdr_serialize_read3res_nocopy, +                                                    vec, iobref); +                } else +                        nfs3svc_submit_reply (req, (void *)&res, +                                              (nfs3_serializer) +                                              xdr_serialize_read3res_nocopy); +        } else +                nfs3svc_submit_reply (req, (void *)&res, +                                      (nfs3_serializer) +                                      xdr_serialize_read3res_nocopy); + +        return 0; +} + + +int32_t +nfs3svc_read_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                  int32_t op_ret, int32_t op_errno, struct iovec *vector, +                  int32_t count, struct iatt *stbuf, struct iobref *iobref) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     is_eof = 0; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto err; +        } else +                stat = NFS3_OK; + +        if (op_errno == ENOENT) +                is_eof = 1; + +err: +        nfs3_log_read_res (rpcsvc_request_xid (cs->req), stat, op_errno, +                           op_ret, is_eof); +        nfs3_read_reply (cs->req, stat, op_ret, vector, iobref, stbuf, is_eof); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3_read_fd_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_read (cs->vol, &nfu, cs->fd, cs->datacount, cs->dataoffset, +                        nfs3svc_read_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READ", stat, +                                     -ret); +                nfs3_read_reply (cs->req, stat, 0, NULL, NULL, NULL, 0); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_read_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        ret = nfs3_file_open_and_resume (cs, nfs3_read_fd_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READ", stat, +                                     -ret); +                nfs3_read_reply (cs->req, stat, 0, NULL, NULL, NULL, 0); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + +int +nfs3_read (rpcsvc_request_t *req, struct nfs3_fh *fh, offset3 offset, +           count3 count) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_rw_call (rpcsvc_request_xid (req), "READ", fh, offset, count, +                          -1); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->datacount = count; +        cs->dataoffset = offset; +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_read_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "READ", stat, +                                     -ret); +                nfs3_read_reply (req, stat, 0, NULL, NULL, NULL, 0); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_read (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        read3args               args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; + +        nfs3_prep_read3args (&args, &fh); +        if (xdr_to_read3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_read (req, &fh, args.offset, args.count); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "READ procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_write_reply (rpcsvc_request_t *req, nfsstat3 stat, count3 count, +                  stable_how stable, uint64_t wverf, struct iatt *prestat, +                  struct iatt *poststat) +{ +        write3res       res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_write3res (&res, stat, count, stable, wverf, prestat, +                             poststat, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_write3res); + +        return 0; +} + +int32_t +nfs3svc_write_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                         int32_t op_ret, int32_t op_errno, struct iatt *prebuf, +                         struct iatt *postbuf) +{ +        struct nfs3_state       *nfs3 = NULL; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        nfs3 = rpcsvc_request_program_private (cs->req); + +        if (op_ret == -1) +                stat = nfs3_errno_to_nfsstat3 (op_errno); +        else +                stat = NFS3_OK; + +        nfs3_log_write_res (rpcsvc_request_xid (cs->req), stat, op_errno, +                            cs->maxcount, cs->writetype, nfs3->serverstart); +        nfs3_write_reply (cs->req, stat, cs->maxcount, cs->writetype, +                          nfs3->serverstart, &cs->stbuf, postbuf); +        nfs3_call_state_wipe (cs); +        return 0; +} + + +int32_t +nfs3svc_write_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                   int32_t op_ret, int32_t op_errno, struct iatt *prebuf, +                   struct iatt *postbuf) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; +        struct nfs3_state       *nfs3 = NULL; + +        cs = frame->local; +        nfs3 = rpcsvc_request_program_private (cs->req); +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto err; +        } + +        /* So that we do send a reply if an unstable write was requested. */ +        ret = -1; +        stat = NFS3_OK; +        cs->maxcount = op_ret; +        if (cs->writetype == UNSTABLE) +                goto err; + +        nfs_request_user_init (&nfu, cs->req); +        /* Store the current preattr so that this can be used as the pre attr +         * when fsync returns. We dont want to use the preattr in fsync because +         * the write fop happened before the fsync. +         */ +        cs->stbuf = *prebuf; +        ret = nfs_fsync (cs->vol, &nfu, cs->fd, 0, nfs3svc_write_fsync_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +err: +        if (ret < 0) { +                nfs3_log_write_res (rpcsvc_request_xid (cs->req), stat, +                                    op_errno, cs->maxcount, cs->writetype, +                                    nfs3->serverstart); +                nfs3_write_reply (cs->req, stat, cs->maxcount, +                                  cs->writetype, nfs3->serverstart, prebuf, +                                  postbuf); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +__nfs3_write_resume (nfs3_call_state_t *cs) +{ +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; + +        if (!cs) +                return ret; + +        nfs_request_user_init (&nfu, cs->req); +        /* It is possible that the RPC record contains more bytes than +         * than the size of write requested in this request. This means, +         * that in the RPC message buffer, there could be more bytes +         * beyind the @count bytes. Since @payload is referring to the write +         * data directly inside the RPC request buffer(..since we performed a +         * no-copy deXDRing..), we might end up writing more data than +         * requested, because till now payload.iov_len accounts for all the +         * bytes not just the write request bytes. These extra bytes are present +         * as a requirement of the XDR encoding to round up the all string and +         * opaque data buffers to multiples of 4 bytes. +         */ +        cs->datavec.iov_len = cs->datacount; +        ret = nfs_write (cs->vol, &nfu, cs->fd, cs->iob, &cs->datavec, 1, +                         cs->dataoffset, nfs3svc_write_cbk, cs); + +        return ret; +} + + +int +nfs3_write_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); + +        ret = __nfs3_write_resume (cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "WRITE", +                                     stat, -ret); +                nfs3_write_reply (cs->req, stat, 0, cs->writetype, 0, NULL, +                                  NULL); +                nfs3_call_state_wipe (cs); +        } +        return ret; +} + + +int +nfs3_write_open_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        ret = nfs3_file_open_and_resume (cs, nfs3_write_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "WRITE", +                                     stat, -ret); +                nfs3_write_reply (cs->req, stat, 0, cs->writetype, 0, NULL, +                                  NULL); +                nfs3_call_state_wipe (cs); +        } +        return ret; +} + + + +int +nfs3_write (rpcsvc_request_t *req, struct nfs3_fh *fh, offset3 offset, +            count3 count, stable_how stable, struct iovec payload, +            struct iobuf *iob) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh) || (!payload.iov_base)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_rw_call (rpcsvc_request_xid (req), "WRITE", fh, offset, count, +                          stable); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); +        cs->datacount = count; +        cs->dataoffset = offset; +        cs->writetype = stable; +        cs->iob = iob; +        cs->datavec = payload; + + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_write_open_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "WRITE", stat, +                                     -ret); +                nfs3_write_reply (req, stat, 0, stable, 0, NULL, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + +#define NFS3_VECWRITE_READFHLEN         1 +#define NFS3_VECWRITE_READFH            2 +#define NFS3_VECWRITE_READREST          3 + +#define NFS3_WRITE_POSTFH_SIZE          20 + + +int +nfs3svc_write_vecsizer (rpcsvc_request_t *req, ssize_t *readsize, int *newbuf) +{ +        ssize_t         ret = RPCSVC_ACTOR_ERROR; +        int             state = 0; +        uint32_t        fhlen = 0; +        uint32_t        fhlen_n = 0; +        write3args      *args = NULL; + +        if (!req) +                return ret; + +        state = (long)rpcsvc_request_private (req); +        *newbuf = 0; +        if (state == 0) { +                rpcsvc_request_set_private (req, NFS3_VECWRITE_READFHLEN); +                *readsize = 4; +                ret = 0; +        } else if (state == NFS3_VECWRITE_READFHLEN) { +                fhlen_n = *(uint32_t *)req->msg.iov_base; +                fhlen = ntohl (fhlen_n); +                *readsize = xdr_length_round_up (fhlen, NFS3_FHSIZE); +                rpcsvc_request_set_private (req, NFS3_VECWRITE_READFH); +                ret = 0; +        } else if (state == NFS3_VECWRITE_READFH) { +                *readsize = NFS3_WRITE_POSTFH_SIZE; +                rpcsvc_request_set_private (req, NFS3_VECWRITE_READREST); +                ret = 0; +        } else if (state == NFS3_VECWRITE_READREST) { +                args = CALLOC (1, sizeof (*args)); +                if (!args) +                        goto rpcerr; + +                if (xdr_to_write3args_nocopy (req->msg, args, NULL) <= 0) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                        rpcsvc_request_seterr (req, GARBAGE_ARGS); +                        goto rpcerr; +                } +                rpcsvc_request_set_private (req, args); +                ret = xdr_length_round_up (args->data.data_len, 1048576); +                *readsize = ret; +                *newbuf = 1; +                ret = 0; +        } +        ret = 0; + +rpcerr: +        return ret; +} + + +int +nfs3svc_write_vec (rpcsvc_request_t *req, struct iobuf *iob) +{ +        write3args              *args = NULL; +        int                     ret = RPCSVC_ACTOR_ERROR; +        struct iovec            payload = {0, }; + +        if ((!req) || (!iob)) +                return ret; + +        args = rpcsvc_request_private (req); +        iobuf_to_iovec (iob, &payload); +        iobuf_ref (iob); +        ret = nfs3_write (req, (struct nfs3_fh *)args->file.data.data_val, +                          args->offset, args->count, args->stable, payload,iob); +        xdr_free_write3args_nocopy (args); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "WRITE procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +        return ret; +} + + + +int +nfs3svc_write (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        write3args              args; +        int                     ret = RPCSVC_ACTOR_ERROR; +        struct iovec            payload = {0, }; + +        if (!req) +                return ret; +        nfs3_prep_write3args (&args, &fh); +        if (xdr_to_write3args_nocopy (req->msg, &args, &payload) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        /* To ensure that the iobuf for the current record does not +         * get returned to the iobpool, we need to keep a reference for +         * ourselves because the RPC call handler who called us will unref its +         * own ref of the record's iobuf when it is done handling the request. +         */ +        rpcsvc_request_record_ref (req); +        ret = nfs3_write (req, &fh, args.offset, args.count, args.stable, +                          payload, rpcsvc_request_record_iob (req)); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "WRITE procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_create_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *newfh, +                   struct iatt *newbuf, struct iatt *preparent, +                   struct iatt *postparent) +{ +        create3res      res = {0, }; + +        nfs3_fill_create3res (&res, stat, newfh, newbuf, preparent, postparent); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_create3res); +        return 0; +} + + +int32_t +nfs3svc_create_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                            int32_t op_ret, int32_t op_errno, +                            struct iatt *preop, struct iatt *postop) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        stat = NFS3_OK; +nfs3err: +        nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "CREATE", stat, +                            op_errno, &cs->fh); +        nfs3_create_reply (cs->req, stat, &cs->fh, postop, &cs->preparent, +                           &cs->postparent); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int32_t +nfs3svc_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, fd_t *fd, inode_t *inode, +                    struct iatt *buf, struct iatt *preparent, +                    struct iatt *postparent) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + +        /* Means no attributes were required to be set. */ +        if (!cs->setattr_valid) { +                stat = NFS3_OK; +                ret = -1; +                goto nfs3err; +        } + +        cs->preparent = *preparent; +        cs->postparent = *postparent; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, +                           cs->setattr_valid, nfs3svc_create_setattr_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "CREATE", +                                    stat, op_errno, &cs->fh); +                nfs3_create_reply (cs->req, stat, &cs->fh, buf, preparent, +                                   postparent); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + +int +nfs3_create_common (nfs3_call_state_t *cs) +{ +        int                     ret = -EFAULT; +        int                     flags = 0; +        nfs_user_t              nfu = {0, }; + +        if (!cs) +                return ret; + +        if ((cs->createmode == UNCHECKED) || (cs->createmode = EXCLUSIVE)) +                flags = O_RDWR; +        else if (cs->createmode == GUARDED) +                flags = (O_RDWR | O_EXCL); + +        nfs_request_user_init (&nfu, cs->req); +        /* We can avoid sending the setattr call later if only the mode is +         * required to be set. This is possible because the create fop allows +         * us to specify a mode arg. +         */ +        if (cs->setattr_valid & GF_SET_ATTR_MODE) { +                cs->setattr_valid &= ~GF_SET_ATTR_MODE; +                ret = nfs_create (cs->vol, &nfu, &cs->resolvedloc, flags, +                                  cs->mode, nfs3svc_create_cbk, cs); +        } else +                ret = nfs_create (cs->vol, &nfu, &cs->resolvedloc, flags, +                                  NFS_DEFAULT_CREATE_MODE, nfs3svc_create_cbk, +                                  cs); + +        return ret; +} + + +int32_t +nfs3svc_create_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                         int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        int                     ret = -EFAULT; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        nfs_request_user_init (&nfu, cs->req); +        if (op_ret == -1) { +                ret = -op_errno; +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        if (cs->preparent.ia_mtime == buf->ia_mtime) +                stat = NFS3_OK; +        else +                stat = NFS3ERR_EXIST; + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "CREATE", +                                     stat, op_errno); +                nfs3_create_reply (cs->req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +nfs3_create_exclusive (nfs3_call_state_t *cs) +{ +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; + +        if (!cs) +                return ret; + +        /* HACK warning. */ +        cs->preparent.ia_mtime = cs->cookieverf; +        cs->preparent.ia_atime = 9669; +        nfs_request_user_init (&nfu, cs->req); + +        /* If the file already existed we need to get that attributes so we can +         * compare and check whether a previous create operation was +         * interrupted due to server failure or dropped packets. +         */ +        if ((cs->resolve_ret == 0) || +            ((cs->resolve_ret == -1) && (cs->resolve_errno != ENOENT))) { +                ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, +                                nfs3svc_create_stat_cbk, cs); +                goto nfs3err; +        } + +        if (cs->setattr_valid & GF_SET_ATTR_MODE) { +                cs->setattr_valid &= ~GF_SET_ATTR_MODE; +                ret = nfs_create (cs->vol, &nfu, &cs->resolvedloc, O_RDWR, +                                  cs->mode, nfs3svc_create_cbk, cs); +        } else +                ret = nfs_create (cs->vol, &nfu, &cs->oploc, O_RDWR, +                                  NFS_DEFAULT_CREATE_MODE, nfs3svc_create_cbk, +                                  cs); + +nfs3err: +        return ret; +} + + +int +nfs3_create_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); +        if (cs->createmode == EXCLUSIVE) +                ret = nfs3_create_exclusive (cs); +        else +                ret = nfs3_create_common (cs); + +        /* Handle a failure return from either of the create functions above. */ +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "CREATE", +                                     stat, -ret); +                nfs3_create_reply (cs->req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + +int +nfs3_create (rpcsvc_request_t *req, struct nfs3_fh *dirfh, char *name, +             createmode3 mode, sattr3 *sattr, uint64_t cverf) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!dirfh) || (!name) || (!sattr)) +                return -1; + +        nfs3_log_create_call (rpcsvc_request_xid (req), dirfh, name, mode); +        nfs3_validate_gluster_fh (dirfh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->cookieverf = cverf; +        cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, +                                                          &cs->mode); +        cs->createmode = mode; +        cs->parent = *dirfh; + +        ret = nfs3_fh_resolve_and_resume (cs, dirfh, name, nfs3_create_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "CREATE", stat, +                                     -ret); +                nfs3_create_reply (req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_create (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          dirfh = {{0}, }; +        create3args             args; +        int                     ret = RPCSVC_ACTOR_ERROR; +        uint64_t                cverf = 0; + +        if (!req) +                return ret; + +        nfs3_prep_create3args (&args, &dirfh, name); +        if (xdr_to_create3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        cverf = *(uint64_t *)args.how.createhow3_u.verf; +        ret = nfs3_create (req, &dirfh, name, args.how.mode, +                           &args.how.createhow3_u.obj_attributes, cverf); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "CREATE procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_mkdir_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *fh, +                  struct iatt *buf, struct iatt *preparent, +                  struct iatt *postparent) +{ +        mkdir3res       res = {0, }; + +        nfs3_fill_mkdir3res (&res, stat, fh, buf, preparent, postparent); +        nfs3svc_submit_reply (req, &res, +                              (nfs3_serializer)xdr_serialize_mkdir3res); +        return 0; +} + +int32_t +nfs3svc_mkdir_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                           int32_t op_ret, int32_t op_errno, +                           struct iatt *preop, struct iatt *postop) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        stat = NFS3_OK; +nfs3err: +        nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKDIR", stat, +                            op_errno, &cs->fh); +        nfs3_mkdir_reply (cs->req, stat, &cs->fh, postop, &cs->preparent, +                          &cs->postparent); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int32_t +nfs3svc_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                   int32_t op_ret, int32_t op_errno, inode_t *inode, +                   struct iatt *buf, struct iatt *preparent, +                   struct iatt *postparent) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + +        /* Means no attributes were required to be set. */ +        if (!cs->setattr_valid) { +                stat = NFS3_OK; +                goto nfs3err; +        } + +        cs->preparent = *preparent; +        cs->postparent = *postparent; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, +                           cs->setattr_valid, nfs3svc_mkdir_setattr_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKDIR", stat, +                                    op_errno, &cs->fh); +                nfs3_mkdir_reply (cs->req, stat, &cs->fh, buf, preparent, +                                  postparent); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +nfs3_mkdir_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); + +        if (gf_attr_mode_set (cs->setattr_valid)) { +                cs->setattr_valid &= ~GF_SET_ATTR_MODE; +                ret = nfs_mkdir (cs->vol, &nfu, &cs->resolvedloc, +                                 cs->mode, nfs3svc_mkdir_cbk, cs); +        } else +                ret = nfs_mkdir (cs->vol, &nfu, &cs->resolvedloc, +                                 cs->mode, nfs3svc_mkdir_cbk, cs); + +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "MKDIR",stat, +                                     -ret); +                nfs3_mkdir_reply (cs->req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + + +int +nfs3_mkdir (rpcsvc_request_t *req, struct nfs3_fh *dirfh, char *name, +            sattr3 *sattr) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!dirfh) || (!name) || (!sattr)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "MKDIR", dirfh, name); +        nfs3_validate_gluster_fh (dirfh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->parent = *dirfh; +        cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, +                                                          &cs->mode); +        ret = nfs3_fh_resolve_and_resume (cs, dirfh, name, nfs3_mkdir_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "MKDIR", stat, +                                     -ret); +                nfs3_mkdir_reply (req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_mkdir (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          dirfh = {{0}, }; +        mkdir3args              args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_mkdir3args (&args, &dirfh, name); +        if (xdr_to_mkdir3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_mkdir (req, &dirfh, name, &args.attributes); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "MKDIR procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_symlink_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *fh, +                    struct iatt *buf, struct iatt *preparent, +                    struct iatt *postparent) +{ +        symlink3res     res = {0, }; + +        nfs3_fill_symlink3res (&res, stat, fh, buf, preparent, postparent); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_symlink3res); + +        return 0; +} + + +int32_t +nfs3svc_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                     int32_t op_ret, int32_t op_errno, inode_t *inode, +                     struct iatt *buf, struct iatt *preparent, +                     struct iatt *postparent) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t               *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); +        stat = NFS3_OK; + +nfs3err: +        nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "SYMLINK", stat, +                            op_errno, &cs->fh); +        nfs3_symlink_reply (cs->req, stat, &cs->fh, buf, preparent, +                            postparent); +        nfs3_call_state_wipe (cs); +        return 0; +} + + +int +nfs3_symlink_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; +        nfs_user_t                      nfu = {0, }; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_symlink (cs->vol, &nfu, cs->pathname, &cs->resolvedloc, +                           nfs3svc_symlink_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SYMLINK", +                                     stat, -ret); +                nfs3_symlink_reply (cs->req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_symlink (rpcsvc_request_t *req, struct nfs3_fh *dirfh, char *name, +              char *target, sattr3 *sattr) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!dirfh) || (!name) || (!target) || (!sattr)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_symlink_call (rpcsvc_request_xid (req), dirfh, name, target); +        nfs3_validate_gluster_fh (dirfh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->parent = *dirfh; +        cs->pathname = strdup (target); +        if (!cs->pathname) { +                ret = -1; +                stat = NFS3ERR_SERVERFAULT; +                goto nfs3err; +        } + +        ret = nfs3_fh_resolve_and_resume (cs, dirfh, name, nfs3_symlink_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "SYMLINK", stat, +                                     -ret); +                nfs3_symlink_reply (req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_symlink (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          dirfh = {{0}, }; +        char                    target[NFS_PATH_MAX]; +        symlink3args            args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_symlink3args (&args, &dirfh, name, target); +        if (xdr_to_symlink3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_symlink (req, &dirfh, name, target, +                            &args.symlink.symlink_attributes); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "SYMLINK procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_mknod_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *fh, +                  struct iatt *buf, struct iatt *preparent, +                  struct iatt *postparent) +{ +        mknod3res       res = {0, }; +        nfs3_fill_mknod3res (&res, stat, fh, buf, preparent, postparent); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_mknod3res); + +        return 0; +} + +int32_t +nfs3svc_mknod_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                           int32_t op_ret, int32_t op_errno, +                           struct iatt *preop, struct iatt *postop) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        stat = NFS3_OK; +nfs3err: +        nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKNOD", stat, +                            op_errno, &cs->fh); +        nfs3_mknod_reply (cs->req, stat, &cs->fh, postop, &cs->preparent, +                          &cs->postparent); +        nfs3_call_state_wipe (cs); +        return 0; +} + + + +int32_t +nfs3svc_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                   int32_t op_ret, int32_t op_errno, inode_t *inode, +                   struct iatt *buf, struct iatt *preparent, +                   struct iatt *postparent) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -1; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + +        /* Means no attributes were required to be set. */ +        if (!cs->setattr_valid) { +                stat = NFS3_OK; +                ret = -1; +                goto nfs3err; +        } + +        cs->preparent = *preparent; +        cs->postparent = *postparent; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, +                           cs->setattr_valid, nfs3svc_mknod_setattr_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKNOD", stat, +                                    op_errno, &cs->fh); +                nfs3_mknod_reply (cs->req, stat, &cs->fh, buf, preparent, +                                  postparent); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +nfs3_mknod_device (nfs3_call_state_t *cs) +{ +        int                             ret = -EFAULT; +        dev_t                           devnum = 0; +        mode_t                          mode = 0; +        nfs_user_t                      nfu = {0, }; + +        if (!cs) +                return ret; + +        devnum = makedev (cs->devnums.specdata1, cs->devnums.specdata2); +        if (cs->mknodtype == NF3CHR) +                mode = S_IFCHR; +        else +                mode = S_IFBLK; + +        nfs_request_user_init (&nfu, cs->req); +        if (gf_attr_mode_set (cs->setattr_valid)) { +                cs->setattr_valid &= ~GF_SET_ATTR_MODE; +                mode |= cs->mode; +                ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, devnum, +                                 nfs3svc_mknod_cbk, cs); +        } else +                ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, devnum, +                                 nfs3svc_mknod_cbk, cs); + +        return ret; +} + + +int +nfs3_mknod_fifo (nfs3_call_state_t *cs) +{ +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        mode_t                          mode = S_IFIFO; + +        if (!cs) +                return ret; + +        nfs_request_user_init (&nfu, cs->req); +        if (gf_attr_mode_set (cs->setattr_valid)) { +                cs->setattr_valid &= ~GF_SET_ATTR_MODE; +                mode |= cs->mode; +                ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, 0, +                                 nfs3svc_mknod_cbk, cs); +        } else +                ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, 0, +                                 nfs3svc_mknod_cbk, cs); + + +        return ret; +} + + +int +nfs3_mknod_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); +        switch (cs->mknodtype) { + +        case NF3CHR: +        case NF3BLK: +                ret = nfs3_mknod_device (cs); +                break; +        case NF3SOCK: +        case NF3FIFO: +                ret = nfs3_mknod_fifo (cs); +                break; +        default: +                ret = -EBADF; +                break; +        } + +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "MKNOD",stat, +                                     -ret); +                nfs3_mknod_reply (cs->req, stat, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + + +int +nfs3_mknod (rpcsvc_request_t *req, struct nfs3_fh *fh, char *name, +            mknoddata3 *nodedata) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; +        sattr3                          *sattr = NULL; + +        if ((!req) || (!fh) || (!name) || (!nodedata)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_mknod_call (rpcsvc_request_xid (req), fh, name,nodedata->type); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->mknodtype = nodedata->type; +        switch (nodedata->type) { +        case NF3CHR: +        case NF3BLK: +                cs->devnums = nodedata->mknoddata3_u.device.spec; +                sattr = &nodedata->mknoddata3_u.device.dev_attributes; +                cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, +                                                                  &cs->mode); +                break; +        case NF3SOCK: +        case NF3FIFO: +                sattr = &nodedata->mknoddata3_u.pipe_attributes; +                cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, +                                                                  &cs->mode); +                break; +        default: +                ret = -EBADF; +                break; +        } + +        cs->parent = *fh; +        ret = nfs3_fh_resolve_and_resume (cs, fh, name, nfs3_mknod_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "MKNOD", stat, +                                     -ret); +                nfs3_mknod_reply (req, stat, NULL, NULL, NULL, NULL); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_mknod (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          fh = {{0}, }; +        mknod3args              args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_mknod3args (&args, &fh, name); +        if (xdr_to_mknod3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_mknod (req, &fh, name, &args.what); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "MKNOD procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + +int +nfs3_remove_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *preparent +                   , struct iatt *postparent) +{ +        remove3res      res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_remove3res (&res, stat, preparent, postparent, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_remove3res); +        return 0; +} + + + +int32_t +nfs3svc_remove_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, struct iatt *preparent, +                    struct iatt *postparent) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        fd_t                    *openfd = NULL; +        nfs3_call_state_t       *cs = NULL; +        struct nfs3_state       *nfs3 = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto do_not_unref_cached_fd; +        } +        stat = NFS3_OK; +        /* Close any cached fds so that when any currently active write +         * finishes, the file is finally removed. +         */ +         openfd = fd_lookup (cs->resolvedloc.inode, 0); +         nfs3 = rpcsvc_request_program_private (cs->req); +         if (openfd) { +                fd_unref (openfd); +                nfs3_fdcache_remove (nfs3, openfd); +         } + +do_not_unref_cached_fd: +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "REMOVE", stat, +                             op_errno); +        nfs3_remove_reply (cs->req, stat, preparent, postparent); +        inode_unref (cs->resolvedloc.inode); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +__nfs3_remove (nfs3_call_state_t *cs) +{ +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        ia_type_t                       type = 0; + +        if (!cs) +                return ret; +        type = cs->resolvedloc.inode->ia_type; +        nfs_request_user_init (&nfu, cs->req); +        if (IA_ISDIR (type)) +                ret = nfs_rmdir (cs->vol, &nfu, &cs->resolvedloc, +                                 nfs3svc_remove_cbk, cs); +        else +                ret = nfs_unlink (cs->vol, &nfu, &cs->resolvedloc, +                                  nfs3svc_remove_cbk, cs); + +        return ret; +} + + +int +nfs3_remove_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        ret = __nfs3_remove (cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "REMOVE", +                                     stat, -ret); +                nfs3_remove_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_remove (rpcsvc_request_t *req, struct nfs3_fh *fh, char *name) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh) || (!name)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "REMOVE", fh, name); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cs, fh, name, nfs3_remove_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "REMOVE", stat, +                                     -ret); +                nfs3_remove_reply (req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_remove (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          fh = {{0}, }; +        remove3args             args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_remove3args (&args, &fh, name); +        if (xdr_to_remove3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_remove (req, &fh, name); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "REMOVE procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_rmdir_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *preparent, +                  struct iatt *postparent) +{ +        rmdir3res       res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_rmdir3res (&res, stat, preparent, postparent, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_rmdir3res); +        return 0; +} + + +int32_t +nfs3svc_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                   int32_t op_ret, int32_t op_errno, struct iatt *preparent, +                   struct iatt *postparent) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) +                stat = nfs3_errno_to_nfsstat3 (op_errno); +        else { +                inode_unref (cs->resolvedloc.inode); +                stat = NFS3_OK; +        } + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RMDIR", stat, +                             op_errno); +        nfs3_rmdir_reply (cs->req, stat, preparent, postparent); +        nfs3_call_state_wipe (cs); + +        return 0; +} + +int +nfs3_rmdir_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; +        nfs_user_t                      nfu = {0, }; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_rmdir (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_rmdir_cbk,cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RMDIR", +                                     stat, -ret); +                nfs3_rmdir_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + + +int +nfs3_rmdir (rpcsvc_request_t *req, struct nfs3_fh *fh, char *name) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh) || (!name)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "RMDIR", fh, name); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cs, fh, name, nfs3_rmdir_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "RMDIR", stat, +                                     -ret); +                nfs3_rmdir_reply (req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_rmdir (rpcsvc_request_t *req) +{ +        char                    name[NFS_PATH_MAX]; +        struct nfs3_fh          fh = {{0}, }; +        rmdir3args              args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_rmdir3args (&args, &fh, name); +        if (xdr_to_rmdir3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_rmdir (req, &fh, name); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "RMDIR procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_rename_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *buf, +                   struct iatt *preoldparent, struct iatt *postoldparent, +                   struct iatt *prenewparent, struct iatt *postnewparent) +{ +        rename3res      res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_rename3res (&res, stat, buf, preoldparent, postoldparent, +                              prenewparent, postnewparent, xlid); + +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer) xdr_serialize_rename3res); + +        return 0; +} + + + +int32_t +nfs3svc_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, struct iatt *buf, +                    struct iatt *preoldparent, struct iatt *postoldparent, +                    struct iatt *prenewparent, struct iatt *postnewparent) +{ +        int                     ret = -EFAULT; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        stat = NFS3_OK; +nfs3err: +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RENAME", stat,-ret); +        nfs3_rename_reply (cs->req, stat, buf, preoldparent, postoldparent, +                           prenewparent, postnewparent); +        nfs3_call_state_wipe (cs); +        return 0; +} + + +int +nfs3_rename_resume_dst (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; +        nfs_user_t                      nfu = {0, }; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); +        cs->parent = cs->resolvefh; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_rename (cs->vol, &nfu, &cs->oploc, &cs->resolvedloc, +                          nfs3svc_rename_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RENAME", +                                     stat, -ret); +                nfs3_rename_reply (cs->req, stat, NULL, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + + +int +nfs3_rename_resume_src (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        /* Copy the resolved loc for the source file into another loc +         * for safekeeping till we resolve the dest loc. +         */ +        nfs_loc_copy (&cs->oploc, &cs->resolvedloc); +        nfs_loc_wipe (&cs->resolvedloc); + +        ret = nfs3_fh_resolve_and_resume (cs, &cs->fh, cs->pathname, +                                          nfs3_rename_resume_dst); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RENAME", +                                     stat, -ret); +                nfs3_rename_reply (cs->req, stat, NULL, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_rename (rpcsvc_request_t *req, struct nfs3_fh *olddirfh, char *oldname, +             struct nfs3_fh *newdirfh, char *newname) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!olddirfh) || (!oldname) || (!newdirfh) || (!newname)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_rename_call (rpcsvc_request_xid (req), olddirfh, oldname, +                              newdirfh, newname); +        nfs3_validate_gluster_fh (olddirfh, stat, nfs3err); +        nfs3_validate_gluster_fh (newdirfh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto(oldname, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_validate_strlen_or_goto(newname, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, olddirfh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, olddirfh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        /* While we resolve the source (fh, name) pair, we need to keep a copy +         * of the dest (fh,name) pair. +         */ +        cs->fh = *newdirfh; +        cs->pathname = strdup (newname); +        if (!cs->pathname) { +                stat = NFS3ERR_SERVERFAULT; +                ret = -1; +                goto nfs3err; +        } + +        ret = nfs3_fh_resolve_and_resume (cs, olddirfh, oldname, +                                          nfs3_rename_resume_src); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "RENAME", stat, +                                     -ret); +                nfs3_rename_reply (req, stat, NULL, NULL, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_rename (rpcsvc_request_t *req) +{ +        char                    newname[NFS_PATH_MAX]; +        char                    oldname[NFS_PATH_MAX]; +        struct nfs3_fh          olddirfh = {{0}, }; +        struct nfs3_fh          newdirfh = {{0}, }; +        rename3args             args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_rename3args (&args, &olddirfh, oldname, &newdirfh, newname); +        if (xdr_to_rename3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_rename (req, &olddirfh, oldname, &newdirfh, newname); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "RENAME procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_link_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *buf, +                 struct iatt *preparent, struct iatt *postparent) +{ +        link3res        res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_link3res (&res, stat, buf, preparent, postparent, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_link3res); + +        return 0; +} + + +int32_t +nfs3svc_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                  int32_t op_ret, int32_t op_errno, inode_t *inode, +                  struct iatt *buf, struct iatt *preparent, +                  struct iatt *postparent) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) +                stat = nfs3_errno_to_nfsstat3 (op_errno); +        else +                stat = NFS3_OK; + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LINK", stat, +                             op_errno); +        nfs3_link_reply (cs->req, stat, buf, preparent, postparent); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3_link_resume_lnk (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; +        nfs_user_t              nfu = {0, }; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_link (cs->vol, &nfu, &cs->oploc, &cs->resolvedloc, +                        nfs3svc_link_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LINK", stat, +                                     -ret); +                nfs3_link_reply (cs->req, stat, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } +        return ret; +} + + +int +nfs3_link_resume_tgt (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_loc_copy (&cs->oploc, &cs->resolvedloc); +        nfs_loc_wipe (&cs->resolvedloc); + +        ret = nfs3_fh_resolve_and_resume (cs, &cs->fh, cs->pathname, +                                          nfs3_link_resume_lnk); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LINK", stat, +                                     -ret); +                nfs3_link_reply (cs->req, stat, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_link (rpcsvc_request_t *req, struct nfs3_fh *targetfh, +           struct nfs3_fh *dirfh, char *newname) +{ +        xlator_t                *vol = NULL; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        struct nfs3_state       *nfs3 = NULL; +        nfs3_call_state_t       *cs = NULL; + +        if ((!req) || (!targetfh) || (!dirfh) || (!newname)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_validate_gluster_fh (dirfh, stat, nfs3err); +        nfs3_validate_gluster_fh (targetfh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_validate_strlen_or_goto(newname, NFS_NAME_MAX, nfs3err, stat, ret); +        nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->fh = *dirfh; +        cs->pathname = strdup (newname); +        if (!cs->pathname) { +                stat = NFS3ERR_SERVERFAULT; +                ret = -1; +                goto nfs3err; +        } + +        ret = nfs3_fh_resolve_and_resume (cs, targetfh, NULL, +                                          nfs3_link_resume_tgt); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "LINK", stat, +                                     -ret); +                nfs3_link_reply (req, stat, NULL, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + +int +nfs3svc_link (rpcsvc_request_t *req) +{ +        char                    newpath[NFS_PATH_MAX]; +        struct nfs3_fh          dirfh = {{0}, }; +        struct nfs3_fh          targetfh = {{0}, }; +        link3args               args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_link3args (&args, &targetfh, &dirfh, newpath); +        if (xdr_to_link3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_link (req, &targetfh, &dirfh, newpath); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "LINK procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_readdirp_reply (rpcsvc_request_t *req, nfsstat3 stat,struct nfs3_fh *dirfh, +                     uint64_t cverf, struct iatt *dirstat, gf_dirent_t *entries, +                     count3 dircount, count3 maxcount, int is_eof) +{ +        readdirp3res     res = {0, }; + +        nfs3_fill_readdirp3res (&res, stat, dirfh, cverf, dirstat, entries, +                                dircount, maxcount, is_eof); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer) xdr_serialize_readdirp3res); +        nfs3_free_readdirp3res (&res); + +        return 0; +} + + +int +nfs3_readdir_reply (rpcsvc_request_t *req, nfsstat3 stat, uint64_t cverf, +                    struct iatt *dirstat, gf_dirent_t *entries, count3 count, +                    int is_eof) +{ +        readdir3res     res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_readdir3res (&res, stat, cverf, dirstat, entries, count, +                               is_eof, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer) xdr_serialize_readdir3res); +        nfs3_free_readdir3res (&res); + +        return 0; +} + + +int32_t +nfs3svc_readdir_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                           int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     is_eof = 0; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto nfs3err; +        } + +        /* Check whether we encountered a end of directory stream while +         * readdir'ing. +         */ +        if (cs->operrno == ENOENT) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "Reached end-of-directory"); +                is_eof = 1; +        } + +        stat = NFS3_OK; +nfs3err: +        if (cs->maxcount == 0) { +                nfs3_log_readdir_res (rpcsvc_request_xid (cs->req), stat, +                                      op_errno, (uint64_t)cs->fd, +                                      cs->dircount, is_eof); +                nfs3_readdir_reply (cs->req, stat, (uint64_t)cs->fd, +                                    buf, &cs->entries, cs->dircount, +                                    is_eof); +        } else { +                nfs3_log_readdirp_res (rpcsvc_request_xid (cs->req), stat, +                                       op_errno, (uint64_t)cs->fd, +                                       cs->dircount, cs->maxcount, is_eof); +                nfs3_readdirp_reply (cs->req, stat, &cs->parent, +                                     (uint64_t)cs->fd, buf, +                                     &cs->entries, cs->dircount, +                                     cs->maxcount, is_eof); +        } + +        if (is_eof) { +                gf_log (GF_NFS3, GF_LOG_TRACE, "EOF REF: %d", cs->fd->refcount); +                fd_unref (cs->fd); +        } + +        gf_log (GF_NFS3, GF_LOG_TRACE, "CS WIPE REF: %d", cs->fd->refcount); +        nfs3_call_state_wipe (cs); +        return 0; +} + + +int32_t +nfs3svc_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                     int32_t op_ret, int32_t op_errno, gf_dirent_t *entries) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto err; +        } + +        cs->operrno = op_errno; +        list_splice_init (&entries->list, &cs->entries.list); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_fstat (cs->vol, &nfu, cs->fd, nfs3svc_readdir_fstat_cbk, cs); +        if (ret < 0) { +                op_ret = -1; +                stat = nfs3_errno_to_nfsstat3 (-ret); +                op_errno = -ret; +        } + +err: +        if (op_ret >= 0) +                goto ret; + +        if (cs->maxcount == 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READDIR", +                                     stat, op_errno); +                nfs3_readdir_reply (cs->req, stat, 0, NULL, NULL, 0, 0); +        } else { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READDIRP" +                                     , stat, op_errno); +                nfs3_readdirp_reply (cs->req, stat, NULL, 0, NULL, NULL, +                                     0, 0, 0); +        } + +        /* For directories, we force a purge from the fd cache on close +         * so that next time the dir is read, we'll get any changed directory +         * entries. +         */ +        fd_unref (cs->fd); +        nfs3_call_state_wipe (cs); +ret: +        return 0; +} + +int +nfs3_readdir_process (nfs3_call_state_t *cs) +{ +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; + +        if (!cs) +                return ret; + +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_readdirp (cs->vol, &nfu, cs->fd, cs->dircount, cs->cookie, +                            nfs3svc_readdir_cbk, cs); +        return ret; +} + + +int +nfs3_readdir_read_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; +        struct nfs3_state       *nfs3 = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs3 = rpcsvc_request_program_private (cs->req); +        ret = nfs3_verify_dircookie (nfs3, cs->fd, cs->cookie, cs->cookieverf, +                                     &stat); +        if (ret < 0)    /* Stat already set by verifier function above. */ +                goto nfs3err; + +        ret = nfs3_readdir_process (cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                if (cs->maxcount == 0) { +                        nfs3_log_common_res (rpcsvc_request_xid (cs->req), +                                             "READDIR", stat, -ret); +                        nfs3_readdir_reply (cs->req, stat, 0, NULL, NULL, 0, 0); +                } else { +                        nfs3_log_common_res (rpcsvc_request_xid (cs->req), +                                             "READDIRP", stat, -ret); +                        nfs3_readdirp_reply (cs->req, stat, NULL, 0, NULL, NULL, +                                             0, 0, 0); +                } +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +nfs3_readdir_open_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        ret = nfs3_dir_open_and_resume (cs, nfs3_readdir_read_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                if (cs->maxcount == 0) { +                        nfs3_log_common_res (rpcsvc_request_xid (cs->req), +                                             "READDIR", stat, -ret); +                        nfs3_readdir_reply (cs->req, stat, 0, NULL, NULL, 0, 0); +                } else { +                        nfs3_log_common_res (rpcsvc_request_xid (cs->req), +                                             "READDIRP", stat, -ret); +                        nfs3_readdirp_reply (cs->req, stat, NULL, 0, NULL, NULL, +                                             0, 0, 0); +                } +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + + +int +nfs3_readdir (rpcsvc_request_t *req, struct nfs3_fh *fh, cookie3 cookie, +              uint64_t cverf, count3 dircount, count3 maxcount) +{ +        xlator_t                *vol = NULL; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        struct nfs3_state       *nfs3 = NULL; +        nfs3_call_state_t       *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_readdir_call (rpcsvc_request_xid (req), fh, dircount,maxcount); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->cookieverf = cverf; +        cs->dircount = dircount; +        cs->maxcount = maxcount; +        cs->cookie = cookie; +        cs->parent = *fh; +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, +                                          nfs3_readdir_open_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                if (maxcount == 0) { +                        nfs3_log_common_res (rpcsvc_request_xid (req), "READDIR" +                                             , stat, -ret); +                        nfs3_readdir_reply (req, stat, 0, NULL, NULL, 0, 0); +                } else { +                        nfs3_log_common_res (rpcsvc_request_xid (req),"READDIRP" +                                             , stat, -ret); +                        nfs3_readdirp_reply (req, stat, NULL, 0, NULL, NULL, 0, +                                             0, 0); +                } +                /* Ret must be NULL after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3svc_readdir (rpcsvc_request_t *req) +{ +        readdir3args    ra; +        struct nfs3_fh  fh = {{0},}; +        int             ret = RPCSVC_ACTOR_ERROR; +        uint64_t        verf = 0; + +        if (!req) +                return ret; +        nfs3_prep_readdir3args (&ra, &fh); +        if (xdr_to_readdir3args (req->msg, &ra) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        verf = *(uint64_t *)ra.cookieverf; +        ret = nfs3_readdir (req, &fh, ra.cookie, verf, ra.count, 0); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "READDIR procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3svc_readdirp (rpcsvc_request_t *req) +{ +        readdirp3args   ra; +        struct nfs3_fh  fh = {{0},}; +        int             ret = RPCSVC_ACTOR_ERROR; +        uint64_t        cverf = 0; + +        if (!req) +                return ret; +        nfs3_prep_readdirp3args (&ra, &fh); +        if (xdr_to_readdirp3args (req->msg, &ra) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        cverf = *(uint64_t *)ra.cookieverf; +        ret = nfs3_readdir (req, &fh, ra.cookie, cverf, ra.dircount, +                            ra.maxcount); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "READDIRP procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_fsstat_reply (rpcsvc_request_t *req, nfsstat3 stat, struct statvfs *fsbuf, +                   struct iatt *postbuf) +{ +        fsstat3res      res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_fsstat3res (&res, stat, fsbuf, postbuf, xlid); +        return nfs3svc_submit_reply (req, &res, +                                     (nfs3_serializer)xdr_serialize_fsstat3res); + +} + + +int32_t +nfs3_fsstat_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                      int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) +                stat = nfs3_errno_to_nfsstat3 (op_errno); +        else +                stat = NFS3_OK; + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSTAT", stat, +                             op_errno); +        nfs3_fsstat_reply (cs->req, stat, &cs->fsstat, buf); +        nfs3_call_state_wipe (cs); +        return 0; +} + + +int32_t +nfs3_fsstat_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                        int32_t op_ret, int32_t op_errno, struct statvfs *buf) +{ +        nfs_user_t              nfu = {0, }; +        int                     ret = -EFAULT; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; +        if (op_ret == -1) { +                ret = -op_errno; +                stat = nfs3_errno_to_nfsstat3 (op_errno); +                goto err; +        } + +        /* Then get the stat for the fs root in order to fill in the +         * post_op_attr. +         */ +        cs->fsstat = *buf; +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, nfs3_fsstat_stat_cbk, +                        cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSTAT", +                                     stat, -ret); +                nfs3_fsstat_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return 0; +} + + +int +nfs3_fsstat_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; +        nfs_user_t              nfu = {0, }; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        /* First, we need to get the statvfs for the subvol */ +        ret = nfs_statfs (cs->vol, &nfu, &cs->resolvedloc, +                          nfs3_fsstat_statfs_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSTAT", +                                                         stat, -ret); +                nfs3_fsstat_reply (cs->req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + + +int +nfs3_fsstat (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_common_call (rpcsvc_request_xid (req), "FSSTAT", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_fsstat_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "FSTAT", stat, +                                     -ret); +                nfs3_fsstat_reply (req, stat, NULL, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_fsstat (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        fsstat3args             args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_fsstat3args (&args, &fh); +        if (xdr_to_fsstat3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_fsstat (req, &fh); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "FSTAT procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_fsinfo_reply (rpcsvc_request_t *req, nfsstat3 status, struct iatt *fsroot) +{ +        fsinfo3res              res; +        struct nfs3_state       *nfs3 = NULL; +        uint16_t                xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3 = rpcsvc_request_program_private (req); +        nfs3_fill_fsinfo3res (nfs3, &res, status, fsroot, xlid); + +        nfs3svc_submit_reply (req, &res, +                              (nfs3_serializer)xdr_serialize_fsinfo3res); +        return 0; +} + + +int32_t +nfs3svc_fsinfo_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        nfsstat3                status = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; + +        cs = frame->local; + +        if (op_ret == -1) +                status = nfs3_errno_to_nfsstat3 (op_errno); +        else +                status = NFS3_OK; + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSINFO", status, +                             op_errno); + +        nfs3_fsinfo_reply (cs->req, status, buf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3_fsinfo_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); + +        ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc,nfs3svc_fsinfo_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSINFO", +                                     stat, -ret); +                nfs3_fsinfo_reply (cs->req, stat, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + +int +nfs3_fsinfo (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_common_call (rpcsvc_request_xid (req), "FSINFO", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_fsinfo_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "FSINFO", stat, +                                     -ret); +                nfs3_fsinfo_reply (req, stat, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_fsinfo (rpcsvc_request_t *req) +{ +        int             ret = RPCSVC_ACTOR_ERROR; +        fsinfo3args     args; +        struct nfs3_fh  root = {{0}, }; + +        if (!req) +                return ret; + +        nfs3_prep_fsinfo3args (&args, &root); +        if (xdr_to_fsinfo3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding arguments"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_fsinfo (req, &root); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "FSINFO procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +int +nfs3_pathconf_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *buf) +{ +        pathconf3res    res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_pathconf3res (&res, stat, buf, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_pathconf3res); +        return 0; +} + + +int32_t +nfs3svc_pathconf_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                      int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ +        struct iatt             *sbuf = NULL; +        nfs3_call_state_t       *cs = NULL; +        nfsstat3                stat = NFS3ERR_SERVERFAULT; + +        cs = frame->local; +        if (op_ret == -1) +                stat = nfs3_errno_to_nfsstat3 (op_errno); +        else { +                /* If stat fop failed, we can still send the other components +                 * in a pathconf reply. +                 */ +                sbuf = buf; +                stat = NFS3_OK; +        } + +        nfs3_log_common_res (rpcsvc_request_xid (cs->req), "PATHCONF", stat, +                             op_errno); +        nfs3_pathconf_reply (cs->req, stat, sbuf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + + +int +nfs3_pathconf_resume (void *carg) +{ +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        nfs_user_t                      nfu = {0, }; +        nfs3_call_state_t               *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_pathconf_cbk, +                        cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "PATHCONF", +                                     stat, -ret); +                nfs3_pathconf_reply (cs->req, stat, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + +int +nfs3_pathconf (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_common_call (rpcsvc_request_xid (req), "PATHCONF", fh); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_pathconf_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "PATHCONF", stat, +                                     -ret); +                nfs3_pathconf_reply (req, stat, NULL); +                nfs3_call_state_wipe (cs); +                /* Ret must be 0 after this so that the caller does not +                 * also send an RPC reply. +                 */ +                ret = 0; +        } + +        return ret; +} + + +int +nfs3svc_pathconf (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        pathconf3args           args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_pathconf3args (&args, &fh); +        if (xdr_to_pathconf3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_pathconf (req, &fh); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "PATHCONF procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + +int +nfs3_commit_reply (rpcsvc_request_t *req, nfsstat3 stat, uint64_t wverf, +                   struct iatt *prestat, struct iatt *poststat) +{ +        commit3res      res = {0, }; +        uint16_t        xlid = 0; + +        xlid = nfs3_request_xlator_id (req); +        nfs3_fill_commit3res (&res, stat, wverf, prestat, poststat, xlid); +        nfs3svc_submit_reply (req, (void *)&res, +                              (nfs3_serializer)xdr_serialize_commit3res); + +        return 0; +} + + +int32_t +nfs3svc_commit_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                    int32_t op_ret, int32_t op_errno, struct iatt *prebuf, +                    struct iatt *postbuf) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        nfs3_call_state_t       *cs = NULL; +        struct nfs3_state       *nfs3 = NULL; + +        cs = frame->local; +        if (op_ret == -1) +                stat = nfs3_errno_to_nfsstat3 (op_errno); +        else +                stat = NFS3_OK; + +        nfs3 = rpcsvc_request_program_private (cs->req); +        nfs3_log_commit_res (rpcsvc_request_xid (cs->req), stat, op_errno, +                             nfs3->serverstart); +        nfs3_commit_reply (cs->req, stat, nfs3->serverstart, prebuf, postbuf); +        nfs3_call_state_wipe (cs); + +        return 0; +} + +int +nfs3_commit_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs_user_t              nfu = {0, }; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); +        nfs_request_user_init (&nfu, cs->req); +        ret = nfs_fsync (cs->vol, &nfu, cs->fd, 0, nfs3svc_commit_cbk, cs); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "COMMIT", +                                     stat, -ret); +                nfs3_commit_reply (cs->req, stat, 0, NULL, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + +int +nfs3_commit_open_resume (void *carg) +{ +        nfsstat3                stat = NFS3ERR_SERVERFAULT; +        int                     ret = -EFAULT; +        nfs3_call_state_t       *cs = NULL; + +        if (!carg) +                return ret; + +        cs = (nfs3_call_state_t *)carg; +        nfs3_check_fh_resolve_status (cs, stat, nfs3err); + +        ret = nfs3_file_open_and_resume (cs, nfs3_commit_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (cs->req), "COMMIT", +                                     stat, -ret); +                nfs3_commit_reply (cs->req, stat, 0, NULL, NULL); +                nfs3_call_state_wipe (cs); +        } + +        return ret; +} + + + +int +nfs3_commit (rpcsvc_request_t *req, struct nfs3_fh *fh, offset3 offset, +             count3 count) +{ +        xlator_t                        *vol = NULL; +        nfsstat3                        stat = NFS3ERR_SERVERFAULT; +        int                             ret = -EFAULT; +        struct nfs3_state               *nfs3 = NULL; +        nfs3_call_state_t               *cs = NULL; + +        if ((!req) || (!fh)) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); +                return -1; +        } + +        nfs3_log_rw_call (rpcsvc_request_xid (req), "COMMIT", fh, offset, count, +                          -1); +        nfs3_validate_gluster_fh (fh, stat, nfs3err); +        nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); +        nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); +        nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); +        nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + +        cs->datacount = count; +        cs->dataoffset = offset; +        ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, +                                          nfs3_commit_open_resume); +        if (ret < 0) +                stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: +        if (ret < 0) { +                nfs3_log_common_res (rpcsvc_request_xid (req), "COMMIT", stat, +                                     -ret); +                nfs3_commit_reply (req, stat, 0, NULL, NULL); +                nfs3_call_state_wipe (cs); +                ret = 0; +        } + +        return ret; +} + + + +int +nfs3svc_commit (rpcsvc_request_t *req) +{ +        struct nfs3_fh          fh = {{0}, }; +        commit3args             args; +        int                     ret = RPCSVC_ACTOR_ERROR; + +        if (!req) +                return ret; +        nfs3_prep_commit3args (&args, &fh); +        if (xdr_to_commit3args (req->msg, &args) <= 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); +                rpcsvc_request_seterr (req, GARBAGE_ARGS); +                goto rpcerr; +        } + +        ret = nfs3_commit (req, &fh, args.offset, args.count); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "COMMIT procedure failed"); +                rpcsvc_request_seterr (req, SYSTEM_ERR); +                ret = RPCSVC_ACTOR_ERROR; +        } + +rpcerr: +        return ret; +} + + +rpcsvc_actor_t          nfs3svc_actors[NFS3_PROC_COUNT] = { +        {"NULL",        NFS3_NULL,      nfs3svc_null,   NULL,   NULL}, +        {"GETATTR",     NFS3_GETATTR,   nfs3svc_getattr,NULL,   NULL}, +        {"SETATTR",     NFS3_SETATTR,   nfs3svc_setattr,NULL,   NULL}, +        {"LOOKUP",      NFS3_LOOKUP,    nfs3svc_lookup, NULL,   NULL}, +        {"ACCESS",      NFS3_ACCESS,    nfs3svc_access, NULL,   NULL}, +        {"READLINK",    NFS3_READLINK,  nfs3svc_readlink,NULL,  NULL}, +        {"READ",        NFS3_READ,      nfs3svc_read,   NULL,   NULL}, +        {"WRITE", NFS3_WRITE, nfs3svc_write, nfs3svc_write_vec, nfs3svc_write_vecsizer}, +        {"CREATE",      NFS3_CREATE,    nfs3svc_create, NULL,   NULL}, +        {"MKDIR",       NFS3_MKDIR,     nfs3svc_mkdir,  NULL,   NULL}, +        {"SYMLINK",     NFS3_SYMLINK,   nfs3svc_symlink,NULL,   NULL}, +        {"MKNOD",       NFS3_MKNOD,     nfs3svc_mknod,  NULL,   NULL}, +        {"REMOVE",      NFS3_REMOVE,    nfs3svc_remove, NULL,   NULL}, +        {"RMDIR",       NFS3_RMDIR,     nfs3svc_rmdir,  NULL,   NULL}, +        {"RENAME",      NFS3_RENAME,    nfs3svc_rename, NULL,   NULL}, +        {"LINK",        NFS3_LINK,      nfs3svc_link,   NULL,   NULL}, +        {"READDIR",     NFS3_READDIR,   nfs3svc_readdir,NULL,   NULL}, +        {"READDIRPLUS", NFS3_READDIRP,  nfs3svc_readdirp,NULL,  NULL}, +        {"FSSTAT",      NFS3_FSSTAT,    nfs3svc_fsstat, NULL,   NULL}, +        {"FSINFO",      NFS3_FSINFO,    nfs3svc_fsinfo, NULL,   NULL}, +        {"PATHCONF",    NFS3_PATHCONF,  nfs3svc_pathconf,NULL,  NULL}, +        {"COMMIT",      NFS3_COMMIT,    nfs3svc_commit, NULL,   NULL} +}; + + +rpcsvc_program_t        nfs3prog = { +                        .progname       = "NFS3", +                        .prognum        = NFS_PROGRAM, +                        .progver        = NFS_V3, +                        .progport       = GF_NFS3_PORT, +                        .progaddrfamily = AF_INET, +                        .proghost       = NULL, +                        .actors         = nfs3svc_actors, +                        .numactors      = NFS3_PROC_COUNT, +                        .conn_destroy   = NULL, +                        .conn_init      = NULL, + +                        /* Requests like FSINFO are sent before an auth scheme +                         * is inited by client. See RFC 2623, Section 2.3.2. */ +                        .min_auth       = AUTH_NULL, +}; + + +int +nfs3_init_options (struct nfs3_state *nfs3, xlator_t *nfsx) +{ +        int     ret = -1; +        char    *optstr = NULL; + +        if ((!nfs3) || (!nfsx)) +                return -1; + +        /* nfs3.read-size */ +        nfs3->readsize = GF_NFS3_RTPREF; +        if (dict_get (nfsx->options, "nfs3.read-size")) { +                ret = dict_get_str (nfsx->options, "nfs3.read-size", &optstr); +                if (ret < 0) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " +                                " option: nfs3.read-size"); +                        ret = -1; +                        goto err; +                } + +                ret = gf_string2bytesize (optstr, &nfs3->readsize); +                if (ret == -1) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to format" +                                " option: nfs3.read-size"); +                        ret = -1; +                        goto err; +                } +        } + +        /* nfs3.write-size */ +        nfs3->writesize = GF_NFS3_WTPREF; +        if (dict_get (nfsx->options, "nfs3.write-size")) { +                ret = dict_get_str (nfsx->options, "nfs3.write-size", &optstr); +                if (ret < 0) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " +                                " option: nfs3.write-size"); +                        ret = -1; +                        goto err; +                } + +                ret = gf_string2bytesize (optstr, &nfs3->writesize); +                if (ret == -1) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to format" +                                " option: nfs3.write-size"); +                        ret = -1; +                        goto err; +                } +        } + +        /* nfs3.readdir.size */ +        nfs3->readdirsize = GF_NFS3_DTPREF; +        if (dict_get (nfsx->options, "nfs3.readdir-size")) { +                ret = dict_get_str (nfsx->options,"nfs3.readdir-size", &optstr); +                if (ret < 0) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read" +                                " option: nfs3.readdir-size"); +                        ret = -1; +                        goto err; +                } + +                ret = gf_string2bytesize (optstr, &nfs3->readdirsize); +                if (ret == -1) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to format" +                                " option: nfs3.readdir-size"); +                        ret = -1; +                        goto err; +                } +        } + + +        /* We want to use the size of the biggest param for the io buffer size. +         */ +        nfs3->iobsize = nfs3->readsize; +        if (nfs3->iobsize < nfs3->writesize) +                nfs3->iobsize = nfs3->writesize; +        if (nfs3->iobsize < nfs3->readdirsize) +                nfs3->iobsize = nfs3->readdirsize; + +        /* But this is the true size of each iobuf. We need this size to +         * accomodate the NFS headers also in the same buffer. */ +        nfs3->iobsize = nfs3->iobsize * 2; + +        /* mem-factor */ +        nfs3->memfactor = GF_NFS3_DEFAULT_MEMFACTOR; +        ret = 0; +err: +        return ret; +} + +int +nfs3_init_subvolume_options (struct nfs3_export *exp, dict_t *options) +{ +        int             ret = -1; +        char            *optstr = NULL; +        char            searchkey[1024]; +        char            *name = NULL; + +        if ((!exp) || (!options)) +                return -1; + +        /* Volume Access */ +        name = exp->subvol->name; +        ret = snprintf (searchkey, 1024, "nfs3.%s.volume-access", name); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "snprintf failed"); +                ret = -1; +                goto err; +        } + +        exp->access = GF_NFS3_DEFAULT_VOLACCESS; +        if (dict_get (options, searchkey)) { +                ret = dict_get_str (options, searchkey, &optstr); +                if (ret < 0) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " +                                " option: %s", searchkey); +                        ret = -1; +                        goto err; +                } + +                if (strcmp (optstr, "read-only") == 0) +                        exp->access = GF_NFS3_VOLACCESS_RO; +        } + +        ret = snprintf (searchkey, 1024, "rpc-auth.%s.unix", name); +        if (ret < 0) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "snprintf failed"); +                ret = -1; +                goto err; +        } + +        if (dict_get (options, searchkey)) { +                ret = dict_get_str (options, searchkey, &optstr); +                if (ret < 0) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " +                                " option: %s", searchkey); +                        ret = -1; +                        goto err; +                } +        } + +        gf_log (GF_NFS3, GF_LOG_TRACE, "%s: %s", exp->subvol->name, +                (exp->access == GF_NFS3_VOLACCESS_RO)?"read-only":"read-write"); +        ret = 0; +err: +        return ret; +} + + +int +nfs3_init_subvolume (struct nfs3_state *nfs3, xlator_t *nfsx, xlator_t *subvol, +                     int xlid) +{ +        int                     ret = -1; +        struct nfs3_export      *exp = NULL; + +        if ((!nfs3) || (!nfsx) || (!subvol)) +                return -1; + +        exp = &nfs3->exports[xlid]; +        exp->subvol = subvol; + +        gf_log (GF_NFS3, GF_LOG_TRACE, "Initing state: %s", exp->subvol->name); + +        ret = nfs3_init_subvolume_options (exp, nfsx->options); +        if (ret == -1) +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init subvol"); + +        return ret; +} + + +int +nfs3_init_subvolumes (struct nfs3_state *nfs3, xlator_t *nfsx) +{ +        int                     xl_count = 0; +        int                     ret = -1; +        struct xlator_list      *xl_list = NULL; + +        if ((!nfs3) || (!nfsx)) +                return -1; + +        xl_list = nfsx->children; +        while (xl_list) { +                ++xl_count; +                xl_list = xl_list->next; +        } + +        nfs3->exports = CALLOC (xl_count, sizeof (struct nfs3_export)); +        if (!nfs3->exports) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Memory allocation failed"); +                goto err; +        } + +        xl_list = nfsx->children; +        xl_count = 0;           /* Re-using xl_count. */ +        while (xl_list) { +                ret = nfs3_init_subvolume (nfs3, nfsx, xl_list->xlator, +                                           xl_count); +                if (ret == -1) { +                        gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init subvol: " +                                "%s", xl_list->xlator->name); +                        goto err; +                } +                xl_list = xl_list->next; +                ++xl_count; +        } + +        ret = 0; +err: +        return ret; +} + + +struct nfs3_state * +nfs3_init_state (xlator_t *nfsx) +{ +        struct nfs3_state       *nfs3 = NULL; +        int                     ret = -1; +        unsigned int            localpool = 0; + + +        if (!nfsx) +                return NULL; + +        nfs3 = (struct nfs3_state *)CALLOC (1, sizeof (*nfs3)); +        if (!nfs3) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Memory allocation failed"); +                return NULL; +        } + +        ret = nfs3_init_options (nfs3, nfsx); +        if (ret == -1) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init options"); +                goto ret; +        } + +        nfs3->iobpool = nfsx->ctx->iobuf_pool; + +        localpool = nfs3->memfactor * GF_NFS_CONCURRENT_OPS_MULT; +        gf_log (GF_NFS3, GF_LOG_TRACE, "local pool: %d", localpool); +        nfs3->localpool = mem_pool_new (nfs3_call_state_t, localpool); +        if (!nfs3->localpool) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "local mempool creation failed"); +                ret = -1; +                goto ret; +        } + +        nfs3->nfsx = nfsx; +        nfs3->exportslist = nfsx->children; +        ret = nfs3_init_subvolumes (nfs3, nfsx); +        if (ret == -1) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init per-subvolume " +                        "state"); +                goto free_localpool; +        } + +        nfs3->serverstart = (uint64_t)time (NULL); +        INIT_LIST_HEAD (&nfs3->fdlru); +        LOCK_INIT (&nfs3->fdlrulock); +        nfs3->fdcount = 0; + +        ret = 0; + +free_localpool: +        if (ret == -1) +                mem_pool_destroy (nfs3->localpool); + +ret: +        if (ret == -1) { +                FREE (nfs3); +                nfs3 = NULL; +        } + +        return nfs3; +} + + +rpcsvc_program_t * +nfs3svc_init (xlator_t *nfsx) +{ +        struct nfs3_state       *nfs3 = NULL; + +        if (!nfsx) +                return NULL; + +        nfs3 = nfs3_init_state (nfsx); +        if (!nfs3) { +                gf_log (GF_NFS3, GF_LOG_ERROR, "NFSv3 state init failed"); +                return NULL; +        } + +        nfs3prog.private = nfs3; + +        return &nfs3prog; +} + + diff --git a/xlators/nfs/server/src/nfs3.h b/xlators/nfs/server/src/nfs3.h new file mode 100644 index 00000000000..1ec5a20a802 --- /dev/null +++ b/xlators/nfs/server/src/nfs3.h @@ -0,0 +1,202 @@ +/* +  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#ifndef _NFS3_H_ +#define _NFS3_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "rpcsvc.h" +#include "dict.h" +#include "xlator.h" +#include "iobuf.h" +#include "nfs.h" +#include "nfs3-fh.h" +#include "nfs-common.h" +#include "xdr-nfs3.h" +#include "mem-pool.h" + +#include <sys/statvfs.h> + +#define GF_NFS3                 GF_NFS"-nfsv3" +#define GF_NFS3_PORT            38467 + +#define GF_NFS3_DEFAULT_MEMFACTOR       15 +#define GF_NFS3_IOBPOOL_MULT            GF_NFS_CONCURRENT_OPS_MULT +#define GF_NFS3_CLTABLE_BUCKETS_MULT    2 +#define GF_NFS3_FDTABLE_BUCKETS_MULT    2 + + +/* Static values used for FSINFO +FIXME: This should be configurable */ +#define GF_NFS3_RTMAX      (64 * GF_UNIT_KB) +#define GF_NFS3_RTPREF     (64 * GF_UNIT_KB) +#define GF_NFS3_RTMULT     (4 * GF_UNIT_KB) +#define GF_NFS3_WTMAX      (64 * GF_UNIT_KB) +#define GF_NFS3_WTPREF     (64 * GF_UNIT_KB) +#define GF_NFS3_WTMULT     (4 * GF_UNIT_KB) +#define GF_NFS3_DTMIN      (4 * GF_UNIT_KB) +#define GF_NFS3_DTPREF     (64 * GF_UNIT_KB) +#define GF_NFS3_MAXFILE    (1 * GF_UNIT_PB) +/* FIXME: Handle time resolutions */ +#define GF_NFS3_TIMEDELTA_SECS     {1,0} +#define GF_NFS3_TIMEDELTA_NSECS    {0,1} +#define GF_NFS3_TIMEDELTA_MSECS    {0,1000000} + +#define GF_NFS3_FS_PROP    (FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME) + +#define GF_NFS3_DIRFD_VALID        1 +#define GF_NFS3_DIRFD_INVALID      0 + +#define GF_NFS3_VOLACCESS_RW    1 +#define GF_NFS3_VOLACCESS_RO    2 + + +#define GF_NFS3_FDCACHE_SIZE    512 +/* This should probably be moved to a more generic layer so that if needed + * different versions of NFS protocol can use the same thing. + */ +struct nfs3_fd_entry { +        fd_t                    *cachedfd; +        struct list_head        list; +}; + +/* Per subvolume nfs3 specific state */ +struct nfs3_export { +        xlator_t                *subvol; +        int                     access; +}; + +#define GF_NFS3_DEFAULT_VOLACCESS       (GF_NFS3_VOLACCESS_RW) + +/* The NFSv3 protocol state */ +struct nfs3_state { + +        /* The NFS xlator pointer. The NFS xlator can be running +         * multiple versions of the NFS protocol. +         */ +        xlator_t                *nfsx; + +        /* The iob pool from which memory allocations are made for receiving +         * and sending network messages. +         */ +        struct iobuf_pool       *iobpool; + +        /* List of child subvolumes for the NFSv3 protocol. +         * Right now, is simply referring to the list of children in nfsx above. +         */ +        xlator_list_t           *exportslist; + +        struct nfs3_export      *exports; +        /* Mempool for allocations of struct nfs3_local */ +        struct mem_pool         *localpool; + +        /* Server start-up timestamp, currently used for write verifier. */ +        uint64_t                serverstart; + +        /* NFSv3 Protocol configurables */ +        size_t                  readsize; +        size_t                  writesize; +        size_t                  readdirsize; + +        /* Size of the iobufs used, depends on the sizes of the three params +         * above. +         */ +        size_t                  iobsize; + +        unsigned int            memfactor; + +        struct list_head        fdlru; +        gf_lock_t               fdlrulock; +        int                     fdcount; +}; + + +typedef int (*nfs3_resume_fn_t) (void *cs); +/* Structure used to communicate state between a fop and its callback. + * Not all members are used at all times. Usage is fop and NFS request + * dependent. + * + * I wish we could have a smaller structure for communicating state + * between callers and callbacks. It could be broken into smaller parts + * but I feel that will lead to a proliferation of types/structures and then + * we'll just be tracking down which structure is used by which fop, not + * to mention that having one type allows me to used a single mem-pool. + * Imagine the chaos if we need a mem-pool for each one of those sub-structures. + */ +struct nfs3_local { +        rpcsvc_request_t        *req; +        xlator_t                *vol; +        nfs3_resume_fn_t        resume_fn; +        xlator_t                *nfsx; + +        /* The list hook to attach this call state to the inode's queue till +         * the opening of the fd on the inode completes. +         */ +        struct list_head        openwait_q; + +        /* Per-NFSv3 Op state */ +        struct nfs3_fh          parent; +        struct nfs3_fh          fh; +        fd_t                    *fd; +        uint32_t                accessbits; +        int                     operrno; +        count3                  dircount; +        count3                  maxcount; +        struct statvfs          fsstat; +        gf_dirent_t             entries; +        struct iatt             stbuf; +        struct iatt             preparent; +        struct iatt             postparent; +        int32_t                 setattr_valid; +        nfstime3                timestamp; +        loc_t                   oploc; +        int                     writetype; +        count3                  datacount; +        offset3                 dataoffset; +        struct iobuf            *iob; +        createmode3             createmode; +        uint64_t                cookieverf; +        int                     sattrguardcheck; +        char                    *pathname; +        ftype3                  mknodtype; +        specdata3               devnums; +        cookie3                 cookie; +        struct iovec            datavec; +        mode_t                  mode; + +        /* NFSv3 FH resolver state */ +        struct nfs3_fh          resolvefh; +        loc_t                   resolvedloc; +        int                     resolve_ret; +        int                     resolve_errno; +        int                     hashidx; +        fd_t                    *resolve_dir_fd; +        char                    *resolventry; +}; + +typedef struct nfs3_local nfs3_call_state_t; + + +extern rpcsvc_program_t * +nfs3svc_init (xlator_t *nfsx); +#endif  | 
