+
+extern struct fileops vnops;
+
+/*
+ * syscall for the rpc.lockd to use to translate a NFS file handle into
+ * an open descriptor.
+ *
+ * warning: do not remove the suser() call or this becomes one giant
+ * security hole.
+ */
+int
+fhopen( proc_t p,
+ struct fhopen_args *uap,
+ register_t *retval)
+{
+ vnode_t vp;
+ struct nfs_filehandle nfh;
+ struct nfs_export *nx;
+ struct nfs_export_options *nxo;
+ struct flock lf;
+ struct fileproc *fp, *nfp;
+ int fmode, error, type;
+ int indx;
+ kauth_cred_t cred = proc_ucred(p);
+ struct vfs_context context;
+ kauth_action_t action;
+
+ context.vc_proc = p;
+ context.vc_ucred = cred;
+
+ /*
+ * Must be super user
+ */
+ error = suser(cred, 0);
+ if (error)
+ return (error);
+
+ fmode = FFLAGS(uap->flags);
+ /* why not allow a non-read/write open for our lockd? */
+ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT))
+ return (EINVAL);
+
+ error = copyin(uap->u_fhp, &nfh.nfh_len, sizeof(nfh.nfh_len));
+ if (error)
+ return (error);
+ if ((nfh.nfh_len < (int)sizeof(struct nfs_exphandle)) ||
+ (nfh.nfh_len > (int)NFS_MAX_FH_SIZE))
+ return (EINVAL);
+ error = copyin(uap->u_fhp, &nfh, sizeof(nfh.nfh_len) + nfh.nfh_len);
+ if (error)
+ return (error);
+
+ lck_rw_lock_shared(&nfs_export_rwlock);
+ /* now give me my vnode, it gets returned to me with a reference */
+ error = nfsrv_fhtovp(&nfh, NULL, TRUE, &vp, &nx, &nxo);
+ lck_rw_done(&nfs_export_rwlock);
+ if (error)
+ return (error);
+
+ /*
+ * From now on we have to make sure not
+ * to forget about the vnode.
+ * Any error that causes an abort must vnode_put(vp).
+ * Just set error = err and 'goto bad;'.
+ */
+
+ /*
+ * from vn_open
+ */
+ if (vnode_vtype(vp) == VSOCK) {
+ error = EOPNOTSUPP;
+ goto bad;
+ }
+
+ /* disallow write operations on directories */
+ if (vnode_isdir(vp) && (fmode & (FWRITE | O_TRUNC))) {
+ error = EISDIR;
+ goto bad;
+ }
+
+ /* compute action to be authorized */
+ action = 0;
+ if (fmode & FREAD)
+ action |= KAUTH_VNODE_READ_DATA;
+ if (fmode & (FWRITE | O_TRUNC))
+ action |= KAUTH_VNODE_WRITE_DATA;
+ if ((error = vnode_authorize(vp, NULL, action, &context)) != 0)
+ goto bad;
+
+ if ((error = VNOP_OPEN(vp, fmode, &context)))
+ goto bad;
+ if ((error = vnode_ref_ext(vp, fmode)))
+ goto bad;
+
+ /*
+ * end of vn_open code
+ */
+
+ // starting here... error paths should call vn_close/vnode_put
+ if ((error = falloc(p, &nfp, &indx)) != 0) {
+ vn_close(vp, fmode & FMASK, cred, p);
+ goto bad;
+ }
+ fp = nfp;
+
+ fp->f_fglob->fg_flag = fmode & FMASK;
+ fp->f_fglob->fg_type = DTYPE_VNODE;
+ fp->f_fglob->fg_ops = &vnops;
+ fp->f_fglob->fg_data = (caddr_t)vp;
+
+ // XXX do we really need to support this with fhopen()?
+ if (fmode & (O_EXLOCK | O_SHLOCK)) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ if (fmode & O_EXLOCK)
+ lf.l_type = F_WRLCK;
+ else
+ lf.l_type = F_RDLCK;
+ type = F_FLOCK;
+ if ((fmode & FNONBLOCK) == 0)
+ type |= F_WAIT;
+ if ((error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob, F_SETLK, &lf, type, &context))) {
+ vn_close(vp, fp->f_fglob->fg_flag, fp->f_fglob->fg_cred, p);
+ fp_free(p, indx, fp);
+ return (error);
+ }
+ fp->f_fglob->fg_flag |= FHASLOCK;
+ }
+
+ vnode_put(vp);
+
+ proc_fdlock(p);
+ *fdflags(p, indx) &= ~UF_RESERVED;
+ fp_drop(p, indx, fp, 1);
+ proc_fdunlock(p);
+
+ *retval = indx;
+ return (0);
+
+bad:
+ vnode_put(vp);
+ return (error);
+}
+