diff options
Diffstat (limited to 'amd/nfs_subr.c')
-rw-r--r-- | amd/nfs_subr.c | 1197 |
1 files changed, 1116 insertions, 81 deletions
diff --git a/amd/nfs_subr.c b/amd/nfs_subr.c index 80d3ca8e311c..e463ff72c63a 100644 --- a/amd/nfs_subr.c +++ b/amd/nfs_subr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1997-2014 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -16,11 +16,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgment: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -78,6 +74,17 @@ struct am_fh { } u; }; +struct am_fh3 { + u_int fhh_gen; /* generation number */ + union { + struct { + int fhh_type; /* old or new am_fh */ + pid_t fhh_pid; /* process id */ + int fhh_id; /* map id */ + } s; + char fhh_path[AM_FHSIZE3-sizeof(u_int)]; /* path to am_node */ + } u; +}; /* forward declarations */ /* converting am-filehandles to mount-points */ @@ -96,10 +103,10 @@ do_readlink(am_node *mp, int *error_return) * otherwise if a link exists use that, * otherwise use the mount point. */ - if (mp->am_mnt->mf_ops->readlink) { + if (mp->am_al->al_mnt->mf_ops->readlink) { int retry = 0; - mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry); - if (mp == 0) { + mp = (*mp->am_al->al_mnt->mf_ops->readlink) (mp, &retry); + if (mp == NULL) { *error_return = retry; return 0; } @@ -109,7 +116,7 @@ do_readlink(am_node *mp, int *error_return) if (mp->am_link) { ln = mp->am_link; } else { - ln = mp->am_mnt->mf_mount; + ln = mp->am_al->al_mnt->mf_mount; } return ln; @@ -130,14 +137,14 @@ nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) { static nfsattrstat res; am_node *mp; - int retry; + int retry = 0; time_t now = clocktime(NULL); if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "getattr:"); mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); - if (mp == 0) { + if (mp == NULL) { if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "\tretry=%d", retry); @@ -209,7 +216,7 @@ nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid); mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE); - if (mp == 0) { + if (mp == NULL) { if (retry < 0) { amd_stats.d_drops++; return 0; @@ -220,9 +227,9 @@ nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) am_node *ap; if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name); - ap = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE); + ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE); if (ap && error < 0) - ap = mp->am_mnt->mf_ops->mount_child(ap, &error); + ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error); if (ap == 0) { if (error < 0) { amd_stats.d_drops++; @@ -285,7 +292,7 @@ nfs_quick_reply(am_node *mp, int error) * Free up transp. It's only used for one reply. */ XFREE(mp->am_transp); - dlog("Quick reply sent for %s", mp->am_mnt->mf_mount); + dlog("Quick reply sent for %s", mp->am_al->al_mnt->mf_mount); } } @@ -301,7 +308,7 @@ nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) plog(XLOG_DEBUG, "readlink:"); mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); - if (mp == 0) { + if (mp == NULL) { readlink_retry: if (retry < 0) { amd_stats.d_drops++; @@ -379,7 +386,7 @@ unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) int retry; am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE); - if (mp == 0) { + if (mp == NULL) { if (retry < 0) { amd_stats.d_drops++; return 0; @@ -396,8 +403,8 @@ unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name); - mp = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE); - if (mp == 0) { + mp = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE); + if (mp == NULL) { /* * Ignore retries... */ @@ -510,7 +517,7 @@ nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) plog(XLOG_DEBUG, "readdir:"); mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE); - if (mp == 0) { + if (mp == NULL) { if (retry < 0) { amd_stats.d_drops++; return 0; @@ -519,7 +526,7 @@ nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) } else { if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path); - res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir) + res.rdr_status = nfs_error((*mp->am_al->al_mnt->mf_ops->readdir) (mp, argp->rda_cookie, &res.rdr_u.rdr_reply_u, e_res, argp->rda_count)); mp->am_stats.s_readdir++; @@ -541,7 +548,7 @@ nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) plog(XLOG_DEBUG, "statfs:"); mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); - if (mp == 0) { + if (mp == NULL) { if (retry < 0) { amd_stats.d_drops++; return 0; @@ -562,8 +569,8 @@ nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) /* check if map is browsable and show_statfs_entries=yes */ if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && - mp->am_mnt && mp->am_mnt->mf_mopts) { - mnt.mnt_opts = mp->am_mnt->mf_mopts; + mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) { + mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts; if (amu_hasmntopt(&mnt, "browsable")) { count_map_entries(mp, &fp->sfrok_blocks, @@ -599,7 +606,7 @@ count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int blocks = bfree = bavail = 0; if (!mp) goto out; - mf = mp->am_mnt; + mf = mp->am_al->al_mnt; if (!mf) goto out; mmp = (mnt_map *) mf->mf_private; @@ -625,58 +632,18 @@ out: *out_bavail = bavail; } - -/* - * Convert from file handle to automount node. - */ static am_node * -fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) +validate_ap(am_node *node, int *rp, u_int fhh_gen) { - struct am_fh *fp = (struct am_fh *) fhp; - am_node *ap = 0; - - if (fp->u.s.fhh_type != 0) { - /* New filehandle type */ - int len = sizeof(*fhp) - sizeof(fp->fhh_gen); - char *path = xmalloc(len+1); - /* - * Because fhp is treated as a filehandle we use memcpy - * instead of xstrlcpy. - */ - memcpy(path, (char *) fp->u.fhh_path, len); - path[len] = '\0'; - /* dlog("fh_to_mp3: new filehandle: %s", path); */ - - ap = path_to_exported_ap(path); - XFREE(path); - } else { - /* dlog("fh_to_mp3: old filehandle: %d", fp->u.s.fhh_id); */ - /* - * Check process id matches - * If it doesn't then it is probably - * from an old kernel-cached filehandle - * which is now out of date. - */ - if (fp->u.s.fhh_pid != get_server_pid()) { - dlog("fh_to_mp3: wrong pid %ld != my pid %ld", - (long) fp->u.s.fhh_pid, get_server_pid()); - goto drop; - } - - /* - * Get hold of the supposed mount node - */ - ap = get_exported_ap(fp->u.s.fhh_id); - } - + am_node *ap = node; /* * Check the generation number in the node * matches the one from the kernel. If not * then the old node has been timed out and * a new one allocated. */ - if (ap != NULL && ap->am_gen != fp->fhh_gen) - ap = 0; + if (node != NULL && node->am_gen != fhh_gen) + ap = NULL; /* * If it doesn't exists then drop the request @@ -690,12 +657,12 @@ fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) * for it. This implements the replicated filesystem * retries. */ - if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) { + if (ap->am_al->al_mnt && FSRV_ISDOWN(ap->am_al->al_mnt->mf_server) && ap->am_parent) { int error; am_node *orig_ap = ap; - dlog("fh_to_mp3: %s (%s) is hung: lookup alternative file server", - orig_ap->am_path, orig_ap->am_mnt->mf_info); + dlog("%s: %s (%s) is hung: lookup alternative file server", __func__, + orig_ap->am_path, orig_ap->am_al->al_mnt->mf_info); /* * Update modify time of parent node. @@ -712,11 +679,11 @@ fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) * to the caller. */ if (vop == VLOOK_CREATE) { - ap = orig_ap->am_parent->am_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop); + ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop); if (ap && error < 0) - ap = orig_ap->am_parent->am_mnt->mf_ops->mount_child(ap, &error); + ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->mount_child(ap, &error); } else { - ap = 0; + ap = NULL; error = ESTALE; } if (ap == 0) { @@ -735,13 +702,13 @@ fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) new_ttl(orig_ap); } -#endif +#endif /* 0 */ /* * Disallow references to objects being unmounted, unless * they are automount points. */ - if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) && + if (ap->am_al->al_mnt && (ap->am_al->al_mnt->mf_flags & MFF_UNMOUNTING) && !(ap->am_flags & AMF_ROOT)) { if (amd_state == Finishing) *rp = ENOENT; @@ -752,7 +719,7 @@ fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) new_ttl(ap); drop: - if (!ap || !ap->am_mnt) { + if (!ap || !ap->am_al->al_mnt) { /* * If we are shutting down then it is likely * that this node has disappeared because of @@ -764,14 +731,60 @@ drop: */ if (amd_state == Finishing) *rp = ENOENT; - else + else { *rp = ESTALE; - amd_stats.d_stale++; + amd_stats.d_stale++; + } } return ap; } +/* + * Convert from file handle to automount node. + */ +static am_node * +fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) +{ + struct am_fh *fp = (struct am_fh *) fhp; + am_node *ap = NULL; + + if (fp->u.s.fhh_type != 0) { + /* New filehandle type */ + int len = sizeof(*fhp) - sizeof(fp->fhh_gen); + char *path = xmalloc(len+1); + /* + * Because fhp is treated as a filehandle we use memcpy + * instead of xstrlcpy. + */ + memcpy(path, (char *) fp->u.fhh_path, len); + path[len] = '\0'; + dlog("%s: new filehandle: %s", __func__, path); + + ap = path_to_exported_ap(path); + XFREE(path); + } else { + dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id); + /* + * Check process id matches + * If it doesn't then it is probably + * from an old kernel-cached filehandle + * which is now out of date. + */ + if (fp->u.s.fhh_pid != get_server_pid()) { + dlog("%s: wrong pid %ld != my pid %ld", __func__, + (long) fp->u.s.fhh_pid, get_server_pid()); + goto done; + } + + /* + * Get hold of the supposed mount node + */ + ap = get_exported_ap(fp->u.s.fhh_id); + } +done: + return validate_ap(ap, rp, fp->fhh_gen); +} static am_node * fh_to_mp(am_nfs_fh *fhp) @@ -781,6 +794,56 @@ fh_to_mp(am_nfs_fh *fhp) return fh_to_mp3(fhp, &dummy, VLOOK_CREATE); } +static am_node * +fh3_to_mp3(am_nfs_fh3 *fhp, int *rp, int vop) +{ + struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data; + am_node *ap = NULL; + + if (fp->u.s.fhh_type != 0) { + /* New filehandle type */ + int len = sizeof(*fp) - sizeof(fp->fhh_gen); + char *path = xmalloc(len+1); + /* + * Because fhp is treated as a filehandle we use memcpy + * instead of xstrlcpy. + */ + memcpy(path, (char *) fp->u.fhh_path, len); + path[len] = '\0'; + dlog("%s: new filehandle: %s", __func__, path); + + ap = path_to_exported_ap(path); + XFREE(path); + } else { + dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id); + /* + * Check process id matches + * If it doesn't then it is probably + * from an old kernel-cached filehandle + * which is now out of date. + */ + if (fp->u.s.fhh_pid != get_server_pid()) { + dlog("%s: wrong pid %ld != my pid %ld", __func__, + (long) fp->u.s.fhh_pid, get_server_pid()); + goto done; + } + + /* + * Get hold of the supposed mount node + */ + ap = get_exported_ap(fp->u.s.fhh_id); + } +done: + return validate_ap(ap, rp, fp->fhh_gen); +} + +static am_node * +fh3_to_mp(am_nfs_fh3 *fhp) +{ + int dummy; + + return fh3_to_mp3(fhp, &dummy, VLOOK_CREATE); +} /* * Convert from automount node to file handle. @@ -826,3 +889,975 @@ mp_to_fh(am_node *mp, am_nfs_fh *fhp) /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */ } } +void +mp_to_fh3(am_node *mp, am_nfs_fh3 *fhp) +{ + u_int pathlen; + struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data; + + memset((char *) fhp, 0, sizeof(am_nfs_fh3)); + fhp->am_fh3_length = AM_FHSIZE3; + + /* Store the generation number */ + fp->fhh_gen = mp->am_gen; + + pathlen = strlen(mp->am_path); + if (pathlen <= sizeof(*fp) - sizeof(fp->fhh_gen)) { + /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */ + + /* + * Because fhp is treated as a filehandle we use memcpy instead of + * xstrlcpy. + */ + memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */ + } else { + /* + * Take the process id + */ + fp->u.s.fhh_pid = get_server_pid(); + + /* + * ... the map number + */ + fp->u.s.fhh_id = mp->am_mapno; + + /* + * ... and the generation number (previously stored) + * to make a "unique" triple that will never + * be reallocated except across reboots (which doesn't matter) + * or if we are unlucky enough to be given the same + * pid as a previous amd (very unlikely). + */ + /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */ + } +} + +#ifdef HAVE_FS_NFS3 +static am_ftype3 ftype_to_ftype3(nfsftype ftype) +{ + if (ftype == NFFIFO) + return AM_NF3FIFO; + else + return ftype; +} + +static void nfstime_to_am_nfstime3(nfstime *time, am_nfstime3 *time3) +{ + time3->seconds = time->seconds; + time3->nseconds = time->useconds * 1000; +} + +static void rdev_to_am_specdata3(u_int rdev, am_specdata3 *rdev3) +{ + /* No device node here */ + rdev3->specdata1 = (u_int) -1; + rdev3->specdata2 = (u_int) -1; +} + +static void fattr_to_fattr3(nfsfattr *fattr, am_fattr3 *fattr3) +{ + fattr3->type = ftype_to_ftype3(fattr->na_type); + fattr3->mode = (am_mode3) fattr->na_mode; + fattr3->nlink = fattr->na_nlink; + fattr3->uid = (am_uid3) fattr->na_uid; + fattr3->gid = (am_uid3) fattr->na_gid; + fattr3->size = (am_size3) fattr->na_size; + fattr3->used = (am_size3) fattr->na_size; + rdev_to_am_specdata3(fattr->na_rdev, &fattr3->rdev); + fattr3->fsid = (uint64) fattr->na_fsid; + fattr3->fileid = (uint64) fattr->na_fileid; + nfstime_to_am_nfstime3(&fattr->na_atime, &fattr3->atime); + nfstime_to_am_nfstime3(&fattr->na_mtime, &fattr3->mtime); + nfstime_to_am_nfstime3(&fattr->na_ctime, &fattr3->ctime); +} + +static void fattr_to_wcc_attr(nfsfattr *fattr, am_wcc_attr *wcc_attr) +{ + wcc_attr->size = (am_size3) fattr->na_size; + nfstime_to_am_nfstime3(&fattr->na_mtime, &wcc_attr->mtime); + nfstime_to_am_nfstime3(&fattr->na_ctime, &wcc_attr->ctime); +} + +static am_nfsstat3 return_estale_or_rofs(am_nfs_fh3 *fh, + am_pre_op_attr *pre_op, + am_post_op_attr *post_op) +{ + am_node *mp; + + mp = fh3_to_mp(fh); + if (!mp) { + pre_op->attributes_follow = 0; + post_op->attributes_follow = 0; + return nfs_error(ESTALE); + } else { + am_fattr3 *fattr3 = &post_op->am_post_op_attr_u.attributes; + am_wcc_attr *wcc_attr = &pre_op->am_pre_op_attr_u.attributes; + nfsfattr *fattr = &mp->am_fattr; + pre_op->attributes_follow = 1; + fattr_to_wcc_attr(fattr, wcc_attr); + post_op->attributes_follow = 1; + fattr_to_fattr3(fattr, fattr3); + return nfs_error(EROFS); + } +} + +static am_nfsstat3 unlink3_or_rmdir3(am_diropargs3 *argp, + am_wcc_data *wcc_data, int unlinkp) +{ + static am_nfsstat3 res; + am_nfs_fh3 *dir = &argp->dir; + am_filename3 name = argp->name; + am_pre_op_attr *pre_op_dir = &wcc_data->before; + am_post_op_attr *post_op_dir = &wcc_data->after; + nfsfattr *fattr; + am_wcc_attr *wcc_attr; + am_node *mp, *ap; + int retry; + + post_op_dir->attributes_follow = 0; + + mp = fh3_to_mp3(dir, &retry, VLOOK_DELETE); + if (!mp) { + pre_op_dir->attributes_follow = 0; + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res = nfs_error(retry); + goto out; + } + + pre_op_dir->attributes_follow = 1; + fattr = &mp->am_fattr; + wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes; + fattr_to_wcc_attr(fattr, wcc_attr); + + if (mp->am_fattr.na_type != NFDIR) { + res = nfs_error(ENOTDIR); + goto out; + } + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, name); + + ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &retry, VLOOK_DELETE); + if (!ap) { + /* + * Ignore retries... + */ + if (retry < 0) + retry = 0; + /* + * Usual NFS workaround... + */ + else if (retry == ENOENT) + retry = 0; + res = nfs_error(retry); + } else { + forcibly_timeout_mp(mp); + res = AM_NFS3_OK; + } + +out: + return res; +} + +voidp +am_nfs3_null_3_svc(voidp argp, struct svc_req *rqstp) +{ + static char * result; + + return (voidp) &result; +} + +am_GETATTR3res * +am_nfs3_getattr_3_svc(am_GETATTR3args *argp, struct svc_req *rqstp) +{ + static am_GETATTR3res result; + am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object; + am_fattr3 *fattr3; + nfsfattr *fattr; + am_node *mp; + int retry = 0; + time_t now = clocktime(NULL); + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "getattr_3:"); + + mp = fh3_to_mp3(fh, &retry, VLOOK_CREATE); + if (!mp) { + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tretry=%d", retry); + + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + result.status = nfs_error(retry); + return &result; + } + + fattr = &mp->am_fattr; + fattr3 = (am_fattr3 *) &result.res_u.ok.obj_attributes; + fattr_to_fattr3(fattr, fattr3); + + result.status = AM_NFS3_OK; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tstat(%s), size = %lu, mtime=%d.%d", + mp->am_path, + (am_size3) fattr3->size, + (u_int) fattr3->mtime.seconds, + (u_int) fattr3->mtime.nseconds); + + /* Delay unmount of what was looked up */ + if (mp->am_timeo_w < 4 * gopt.am_timeo_w) + mp->am_timeo_w += gopt.am_timeo_w; + mp->am_ttl = now + mp->am_timeo_w; + + mp->am_stats.s_getattr++; + + return &result; +} + +am_SETATTR3res * +am_nfs3_setattr_3_svc(am_SETATTR3args *argp, struct svc_req *rqstp) +{ + static am_SETATTR3res result; + am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object; + am_pre_op_attr *pre_op_obj = &result.res_u.fail.obj_wcc.before; + am_post_op_attr *post_op_obj = &result.res_u.fail.obj_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "setattr_3:"); + + result.status = return_estale_or_rofs(fh, pre_op_obj, post_op_obj); + + return &result; +} + +am_LOOKUP3res * +am_nfs3_lookup_3_svc(am_LOOKUP3args *argp, struct svc_req *rqstp) +{ + static am_LOOKUP3res result; + am_nfs_fh3 *dir = &argp->what.dir; + am_post_op_attr *post_op_dir; + am_post_op_attr *post_op_obj; + am_node *mp; + int retry; + uid_t uid; + gid_t gid; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "lookup_3:"); + + /* finally, find the effective uid/gid from RPC request */ + if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0) + plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials"); + xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid); + xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid); + + mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE); + if (!mp) { + post_op_dir = &result.res_u.fail.dir_attributes; + post_op_dir->attributes_follow = 0; + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + result.status = nfs_error(retry); + } else { + post_op_dir = &result.res_u.ok.dir_attributes; + post_op_obj = &result.res_u.ok.obj_attributes; + am_filename3 name; + am_fattr3 *fattr3; + nfsfattr *fattr; + am_node *ap; + int error; + + /* dir attributes */ + post_op_dir->attributes_follow = 1; + fattr = &mp->am_fattr; + fattr3 = &post_op_dir->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + post_op_obj->attributes_follow = 0; + + name = argp->what.name; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tlookup_3(%s, %s)", mp->am_path, name); + + ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &error, VLOOK_CREATE); + if (ap && error < 0) + ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error); + if (ap == 0) { + if (error < 0) { + amd_stats.d_drops++; + return 0; + } + result.status = nfs_error(error); + } else { + /* + * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This + * should reduce the chance for race condition between unmounting an + * entry synchronously, and re-mounting it asynchronously. + */ + if (ap->am_ttl < mp->am_ttl) + ap->am_ttl = mp->am_ttl; + + mp_to_fh3(ap, &result.res_u.ok.object); + + /* mount attributes */ + post_op_obj->attributes_follow = 1; + fattr = &ap->am_fattr; + fattr3 = &post_op_obj->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + result.status = AM_NFS3_OK; + } + mp->am_stats.s_lookup++; + } + return &result; +} + +am_ACCESS3res * +am_nfs3_access_3_svc(am_ACCESS3args *argp, struct svc_req *rqstp) +{ + static am_ACCESS3res result; + + am_nfs_fh3 *obj = &argp->object; + u_int accessbits = argp->access; + u_int accessmask = AM_ACCESS3_LOOKUP|AM_ACCESS3_READ; + am_post_op_attr *post_op_obj; + am_node *mp; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "access_3:"); + + mp = fh3_to_mp(obj); + if (!mp) { + post_op_obj = &result.res_u.fail.obj_attributes; + post_op_obj->attributes_follow = 0; + result.status = nfs_error(ENOENT); + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "access_3: ENOENT"); + } else { + nfsfattr *fattr = &mp->am_fattr; + am_fattr3 *fattr3; + post_op_obj = &result.res_u.ok.obj_attributes; + fattr3 = &post_op_obj->am_post_op_attr_u.attributes; + post_op_obj->attributes_follow = 1; + fattr_to_fattr3(fattr, fattr3); + + result.res_u.ok.access = accessbits & accessmask; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "access_3: b=%x m=%x", accessbits, accessmask); + + result.status = AM_NFS3_OK; + } + + return &result; +} + +am_READLINK3res * +am_nfs3_readlink_3_svc(am_READLINK3args *argp, struct svc_req *rqstp) +{ + static am_READLINK3res result; + + am_nfs_fh3 *symlink = (am_nfs_fh3 *) &argp->symlink; + am_post_op_attr *post_op_sym; + am_node *mp; + int retry = 0; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "readlink_3:"); + + mp = fh3_to_mp3(symlink, &retry, VLOOK_CREATE); + if (!mp) { + readlink_retry: + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + post_op_sym = &result.res_u.fail.symlink_attributes; + post_op_sym->attributes_follow = 0; + result.status = nfs_error(retry); + } else { + nfsfattr *fattr; + am_fattr3 *fattr3; + char *ln; + + ln = do_readlink(mp, &retry); + if (!ln) + goto readlink_retry; + + if (amuDebug(D_TRACE) && ln) + plog(XLOG_DEBUG, "\treadlink_3(%s) = %s", mp->am_path, ln); + + result.res_u.ok.data = ln; + + post_op_sym = &result.res_u.ok.symlink_attributes; + post_op_sym->attributes_follow = 1; + fattr = &mp->am_fattr; + fattr3 = &post_op_sym->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + mp->am_stats.s_readlink++; + result.status = AM_NFS3_OK; + } + + return &result; +} + +am_READ3res * +am_nfs3_read_3_svc(am_READ3args *argp, struct svc_req *rqstp) +{ + static am_READ3res result; + + am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; + am_post_op_attr *post_op_file; + am_node *mp; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "read_3:"); + + post_op_file = &result.res_u.fail.file_attributes; + result.status = nfs_error(EACCES); + + mp = fh3_to_mp(file); + if (!mp) + post_op_file->attributes_follow = 0; + else { + nfsfattr *fattr = &mp->am_fattr; + am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes; + post_op_file->attributes_follow = 1; + fattr_to_fattr3(fattr, fattr3); + } + + return &result; +} + +am_WRITE3res * +am_nfs3_write_3_svc(am_WRITE3args *argp, struct svc_req *rqstp) +{ + static am_WRITE3res result; + + am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; + am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before; + am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "write_3:"); + + result.status = return_estale_or_rofs(file, pre_op_file, post_op_file); + + return &result; +} + +am_CREATE3res * +am_nfs3_create_3_svc(am_CREATE3args *argp, struct svc_req *rqstp) +{ + static am_CREATE3res result; + + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; + am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; + am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "create_3:"); + + result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); + + return &result; +} + +am_MKDIR3res * +am_nfs3_mkdir_3_svc(am_MKDIR3args *argp, struct svc_req *rqstp) +{ + static am_MKDIR3res result; + + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; + am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; + am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mkdir_3:"); + + result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); + + return &result; +} + +am_SYMLINK3res * +am_nfs3_symlink_3_svc(am_SYMLINK3args *argp, struct svc_req *rqstp) +{ + static am_SYMLINK3res result; + + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; + am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; + am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "symlink_3:"); + + result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); + + return &result; +} + +am_MKNOD3res * +am_nfs3_mknod_3_svc(am_MKNOD3args *argp, struct svc_req *rqstp) +{ + static am_MKNOD3res result; + + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; + am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; + am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mknod_3:"); + + result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); + return &result; +} + +am_REMOVE3res * +am_nfs3_remove_3_svc(am_REMOVE3args *argp, struct svc_req *rqstp) +{ + static am_REMOVE3res result; + + am_diropargs3 *obj = &argp->object; + am_wcc_data dir_wcc; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "remove_3:"); + + result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE); + + result.res_u.ok.dir_wcc = dir_wcc; + + return &result; +} + +am_RMDIR3res * +am_nfs3_rmdir_3_svc(am_RMDIR3args *argp, struct svc_req *rqstp) +{ + static am_RMDIR3res result; + + am_diropargs3 *obj = &argp->object; + am_wcc_data dir_wcc; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "rmdir_3:"); + + result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE); + + result.res_u.ok.dir_wcc = dir_wcc; + + return &result; +} + +am_RENAME3res * +am_nfs3_rename_3_svc(am_RENAME3args *argp, struct svc_req *rqstp) +{ + static am_RENAME3res result; + + am_nfs_fh3 *fromdir = (am_nfs_fh3 *) &argp->from.dir; + am_nfs_fh3 *todir = (am_nfs_fh3 *) &argp->to.dir; + am_filename3 name = argp->to.name; + am_node *to_mp, *from_mp; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "rename_3:"); + + if (!(from_mp = fh3_to_mp(fromdir)) || !(to_mp = fh3_to_mp(todir))) + result.status = nfs_error(ESTALE); + /* + * If the kernel is doing clever things with referenced files + * then let it pretend... + */ + else { + am_wcc_attr *wcc_attr; + am_fattr3 *fattr3; + am_wcc_data *to_wcc_data, *from_wcc_data; + am_pre_op_attr *pre_op_to, *pre_op_from; + am_post_op_attr *post_op_to, *post_op_from; + nfsfattr *fattr; + + to_wcc_data = &result.res_u.ok.todir_wcc; + + pre_op_to = &to_wcc_data->before; + post_op_to = &to_wcc_data->after; + + pre_op_to->attributes_follow = 1; + fattr = &to_mp->am_fattr; + wcc_attr = &pre_op_to->am_pre_op_attr_u.attributes; + fattr_to_wcc_attr(fattr, wcc_attr); + post_op_to->attributes_follow = 1; + fattr3 = &post_op_to->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + from_wcc_data = &result.res_u.ok.fromdir_wcc; + + pre_op_from = &from_wcc_data->before; + post_op_from = &from_wcc_data->after; + + pre_op_from->attributes_follow = 1; + fattr = &from_mp->am_fattr; + wcc_attr = &pre_op_from->am_pre_op_attr_u.attributes; + fattr_to_wcc_attr(fattr, wcc_attr); + post_op_from->attributes_follow = 1; + fattr3 = &post_op_from->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + if (NSTREQ(name, ".nfs", 4)) + result.status = AM_NFS3_OK; + /* + * otherwise a failure + */ + else + result.status = nfs_error(EROFS); + } + + return &result; +} + +am_LINK3res * +am_nfs3_link_3_svc(am_LINK3args *argp, struct svc_req *rqstp) +{ + static am_LINK3res result; + + am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->link.dir; + am_post_op_attr *post_op_file; + am_pre_op_attr *pre_op_dir; + am_post_op_attr *post_op_dir; + am_node *mp_file, *mp_dir; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "link_3:"); + + post_op_file = &result.res_u.fail.file_attributes; + post_op_file->attributes_follow = 0; + + mp_file = fh3_to_mp(file); + if (mp_file) { + nfsfattr *fattr = &mp_file->am_fattr; + am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + } + + pre_op_dir = &result.res_u.fail.linkdir_wcc.before; + pre_op_dir->attributes_follow = 0; + post_op_dir = &result.res_u.fail.linkdir_wcc.after; + post_op_dir->attributes_follow = 0; + + mp_dir = fh3_to_mp(dir); + if (mp_dir) { + nfsfattr *fattr = &mp_dir->am_fattr; + am_fattr3 *fattr3 = &post_op_dir->am_post_op_attr_u.attributes; + am_wcc_attr *wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes; + + pre_op_dir->attributes_follow = 1; + fattr_to_wcc_attr(fattr, wcc_attr); + post_op_dir->attributes_follow = 1; + fattr_to_fattr3(fattr, fattr3); + } + + if (!mp_file || !mp_dir) + result.status = nfs_error(ESTALE); + else + result.status = nfs_error(EROFS); + + return &result; +} + +am_READDIR3res * +am_nfs3_readdir_3_svc(am_READDIR3args *argp, struct svc_req *rqstp) +{ + static am_READDIR3res result; + static am_entry3 entries[MAX_READDIR_ENTRIES]; + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir; + am_cookie3 cookie = argp->cookie; + am_cookieverf3 cookieverf; + am_count3 count = argp->count; + am_post_op_attr *post_op_dir; + am_node *mp; + int retry; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "readdir_3:"); + + memcpy(&cookieverf, &argp->cookieverf, sizeof(am_cookieverf3)); + + mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE); + if (mp == NULL) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + post_op_dir = &result.res_u.fail.dir_attributes; + post_op_dir->attributes_follow = 0; + result.status = nfs_error(retry); + } else { + am_dirlist3 *list = &result.res_u.ok.reply; + am_nfsstat3 status; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\treaddir_3(%s)", mp->am_path); + + status = mp->am_al->al_mnt->mf_ops->readdir(mp, + (voidp)&cookie, list, entries, count); + if (status == 0) { + post_op_dir = &result.res_u.ok.dir_attributes; + nfsfattr *fattr; + am_fattr3 *fattr3; + + fattr = &mp->am_fattr; + fattr3 = &post_op_dir->am_post_op_attr_u.attributes; + post_op_dir->attributes_follow = 1; + fattr_to_fattr3(fattr, fattr3); + result.status = AM_NFS3_OK; + } else { + post_op_dir = &result.res_u.fail.dir_attributes; + post_op_dir->attributes_follow = 0; + result.status = nfs_error(status); + } + + mp->am_stats.s_readdir++; + } + + return &result; +} + +am_READDIRPLUS3res * +am_nfs3_readdirplus_3_svc(am_READDIRPLUS3args *argp, struct svc_req *rqstp) +{ + static am_READDIRPLUS3res result; + am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir; + am_post_op_attr *post_op_dir; + nfsfattr *fattr; + am_fattr3 *fattr3; + am_node *mp; + int retry; + + mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE); + if (mp == NULL) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + post_op_dir = &result.res_u.fail.dir_attributes; + post_op_dir->attributes_follow = 0; + result.status = nfs_error(retry); + } else { + post_op_dir = &result.res_u.ok.dir_attributes; + fattr = &mp->am_fattr; + fattr3 = &post_op_dir->am_post_op_attr_u.attributes; + post_op_dir->attributes_follow = 1; + fattr_to_fattr3(fattr, fattr3); + result.status = AM_NFS3ERR_NOTSUPP; + } + + return &result; +} + +am_FSSTAT3res * +am_nfs3_fsstat_3_svc(am_FSSTAT3args *argp, struct svc_req *rqstp) +{ + static am_FSSTAT3res result; + + am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot; + am_post_op_attr *post_op_fsroot; + am_node *mp; + int retry; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "fsstat_3:"); + + mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE); + if (!mp) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + post_op_fsroot = &result.res_u.fail.obj_attributes; + post_op_fsroot->attributes_follow = 0; + result.status = nfs_error(retry); + } else { + am_FSSTAT3resok *ok = &result.res_u.ok; + u_int blocks, bfree, bavail; + nfsfattr *fattr; + am_fattr3 *fattr3; + mntent_t mnt; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tfsstat_3(%s)", mp->am_path); + + fattr = &mp->am_fattr; + post_op_fsroot = &ok->obj_attributes; + post_op_fsroot->attributes_follow = 1; + fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + /* + * just return faked up file system information + */ + ok->tbytes = 1024; + ok->invarsec = 0; + + /* check if map is browsable and show_statfs_entries=yes */ + if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && + mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) { + mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts; + if (amu_hasmntopt(&mnt, "browsable")) { + count_map_entries(mp, &blocks, &bfree, &bavail); + } + ok->fbytes = bfree; + ok->abytes = bavail; + ok->ffiles = bfree; + ok->afiles = bavail; + ok->tfiles = blocks; + } else { + ok->fbytes = 0; + ok->abytes = 0; + ok->ffiles = 0; + ok->afiles = 0; + ok->tfiles = 0; /* set to 1 if you don't want empty automounts */ + } + + result.status = AM_NFS3_OK; + mp->am_stats.s_statfs++; + } + + return &result; +} + +#define FSF3_HOMOGENEOUS 0x0008 + +am_FSINFO3res * +am_nfs3_fsinfo_3_svc(am_FSINFO3args *argp, struct svc_req *rqstp) +{ + static am_FSINFO3res result; + + am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot; + am_post_op_attr *post_op_fsroot; + am_node *mp; + int retry; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "fsinfo_3:"); + + mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE); + if (!mp) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + post_op_fsroot = &result.res_u.fail.obj_attributes; + post_op_fsroot->attributes_follow = 0; + result.status = nfs_error(retry); + } else { + am_FSINFO3resok *ok = &result.res_u.ok; + nfsfattr *fattr; + am_fattr3 *fattr3; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tfsinfo_3(%s)", mp->am_path); + + fattr = &mp->am_fattr; + post_op_fsroot = &ok->obj_attributes; + post_op_fsroot->attributes_follow = 1; + fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + /* + * just return faked up file system information + */ + ok->rtmax = 0; + ok->rtpref = 0; + ok->rtmult = 0; + ok->wtmax = 0; + ok->wtpref = 0; + ok->wtmult = 0; + ok->dtpref = 1024; + ok->maxfilesize = 0; + ok->time_delta.seconds = 1; + ok->time_delta.nseconds = 0; + ok->properties = FSF3_HOMOGENEOUS; + + result.status = AM_NFS3_OK; + mp->am_stats.s_fsinfo++; + } + + return &result; +} + +am_PATHCONF3res * +am_nfs3_pathconf_3_svc(am_PATHCONF3args *argp, struct svc_req *rqstp) +{ + static am_PATHCONF3res result; + + am_nfs_fh3 *obj = (am_nfs_fh3 *) &argp->object; + am_post_op_attr *post_op_obj; + am_node *mp; + int retry; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "pathconf_3:"); + + mp = fh3_to_mp3(obj, &retry, VLOOK_CREATE); + if (!mp) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + post_op_obj = &result.res_u.fail.obj_attributes; + post_op_obj->attributes_follow = 0; + result.status = nfs_error(retry); + } else { + am_PATHCONF3resok *ok = &result.res_u.ok; + nfsfattr *fattr; + am_fattr3 *fattr3; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tpathconf_3(%s)", mp->am_path); + + fattr = &mp->am_fattr; + post_op_obj = &ok->obj_attributes; + post_op_obj->attributes_follow = 1; + fattr3 = &post_op_obj->am_post_op_attr_u.attributes; + fattr_to_fattr3(fattr, fattr3); + + ok->linkmax = 0; + ok->name_max = NAME_MAX; + ok->no_trunc = 1; + ok->chown_restricted = 1; + ok->case_insensitive = 0; + ok->case_preserving = 1; + + result.status = AM_NFS3_OK; + mp->am_stats.s_pathconf++; + } + + return &result; +} + +am_COMMIT3res * +am_nfs3_commit_3_svc(am_COMMIT3args *argp, struct svc_req *rqstp) +{ + static am_COMMIT3res result; + + am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; + am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before; + am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "commit_3:"); + + result.status = return_estale_or_rofs(file, pre_op_file, post_op_file); + + return &result; +} +#endif /* HAVE_FS_NFS3 */ |