diff options
Diffstat (limited to 'xlators/nfs/server/src/nfs3-helpers.c')
-rw-r--r-- | xlators/nfs/server/src/nfs3-helpers.c | 2825 |
1 files changed, 2825 insertions, 0 deletions
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; +} + + |