#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
#include <sys/proc_internal.h>
#include <sys/kauth.h>
#include <sys/mount_internal.h>
#include <sys/malloc.h>
#include <sys/kpi_mbuf.h>
#include <sys/conf.h>
#include <sys/vnode_internal.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/ubc_internal.h>
#include <sys/attr.h>
#include <sys/signalvar.h>
#include <sys/uio_internal.h>
#include <vfs/vfs_support.h>
#include <sys/vm.h>
#include <sys/time.h>
#include <kern/clock.h>
#include <libkern/OSAtomic.h>
#include <miscfs/fifofs/fifo.h>
#include <miscfs/specfs/specdev.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs_gss.h>
#include <nfs/nfsmount.h>
#include <nfs/nfs_lock.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <vm/vm_kern.h>
#include <kern/task.h>
#include <kern/sched_prim.h>
int
nfs4_access_rpc(nfsnode_t np, u_long *mode, vfs_context_t ctx)
{
int error = 0, status, numops, slot;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
struct timeval now;
uint32_t access, supported = 0, missing;
struct nfsmount *nmp = NFSTONMP(np);
int nfsvers = nmp->nm_vers;
uid_t uid;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 17 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "access", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_ACCESS);
nfsm_chain_add_32(error, &nmreq, *mode);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_ACCESS);
nfsm_chain_get_32(error, &nmrep, supported);
nfsm_chain_get_32(error, &nmrep, access);
nfsmout_if(error);
if ((missing = (*mode & ~supported))) {
if (missing & NFS_ACCESS_DELETE) {
access |= NFS_ACCESS_DELETE;
}
}
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, np, nfsvers, NULL, &xid);
nfsmout_if(error);
uid = kauth_cred_getuid(vfs_context_ucred(ctx));
slot = nfs_node_mode_slot(np, uid, 1);
np->n_modeuid[slot] = uid;
microuptime(&now);
np->n_modestamp[slot] = now.tv_sec;
np->n_mode[slot] = access;
*mode = np->n_mode[slot];
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_getattr_rpc(
nfsnode_t np,
mount_t mp,
u_char *fhp,
size_t fhsize,
vfs_context_t ctx,
struct nfs_vattr *nvap,
u_int64_t *xidp)
{
struct nfsmount *nmp = mp ? VFSTONFS(mp) : NFSTONMP(np);
int error = 0, status, nfsvers, numops;
struct nfsm_chain nmreq, nmrep;
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 2; nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "getattr", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, fhp, fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(np, mp, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, xidp, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvap->nva_bitmap);
error = nfs4_parsefattr(&nmrep, NULL, nvap, NULL, NULL);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_readlink_rpc(nfsnode_t np, char *buf, uint32_t *buflenp, vfs_context_t ctx)
{
struct nfsmount *nmp;
int error = 0, lockerror = ENOENT, status, numops;
uint32_t len = 0;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
nmp = NFSTONMP(np);
if (!nmp)
return (ENXIO);
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "readlink", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_READLINK);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, np, NFS_VER4, NULL, &xid);
nfsm_chain_op_check(error, &nmrep, NFS_OP_READLINK);
nfsm_chain_get_32(error, &nmrep, len);
nfsmout_if(error);
if (len >= *buflenp) {
if (np->n_size && (np->n_size < *buflenp))
len = np->n_size;
else
len = *buflenp - 1;
}
nfsm_chain_get_opaque(error, &nmrep, len, buf);
if (!error)
*buflenp = len;
nfsmout:
if (!lockerror)
nfs_unlock(np);
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_read_rpc_async(
nfsnode_t np,
off_t offset,
size_t len,
thread_t thd,
kauth_cred_t cred,
struct nfsreq_cbinfo *cb,
struct nfsreq **reqp)
{
struct nfsmount *nmp;
int error = 0, nfsvers, numops;
struct nfsm_chain nmreq;
nmp = NFSTONMP(np);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmreq);
numops = 3;
nfsm_chain_build_alloc_init(error, &nmreq, 22 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "read", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_READ);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_64(error, &nmreq, offset);
nfsm_chain_add_32(error, &nmreq, len);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request_async(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, cb, reqp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
return (error);
}
int
nfs4_read_rpc_async_finish(
nfsnode_t np,
struct nfsreq *req,
struct uio *uiop,
size_t *lenp,
int *eofp)
{
struct nfsmount *nmp;
int error = 0, lockerror, nfsvers, numops, status, eof = 0;
size_t retlen = 0;
u_int64_t xid;
struct nfsm_chain nmrep;
nmp = NFSTONMP(np);
if (!nmp) {
nfs_request_async_cancel(req);
return (ENXIO);
}
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmrep);
error = nfs_request_async_finish(req, &nmrep, &xid, &status);
if (error == EINPROGRESS)
return (error);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_READ);
nfsm_chain_get_32(error, &nmrep, eof);
nfsm_chain_get_32(error, &nmrep, retlen);
if (!error) {
*lenp = MIN(retlen, *lenp);
error = nfsm_chain_get_uio(&nmrep, *lenp, uiop);
}
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, np, nfsvers, NULL, &xid);
if (!lockerror)
nfs_unlock(np);
if (eofp) {
if (!eof && !retlen)
eof = 1;
*eofp = eof;
}
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_write_rpc_async(
nfsnode_t np,
struct uio *uiop,
size_t len,
thread_t thd,
kauth_cred_t cred,
int iomode,
struct nfsreq_cbinfo *cb,
struct nfsreq **reqp)
{
struct nfsmount *nmp;
int error = 0, nfsvers, numops;
off_t offset;
struct nfsm_chain nmreq;
nmp = NFSTONMP(np);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
offset = uiop->uio_offset;
nfsm_chain_null(&nmreq);
numops = 3;
nfsm_chain_build_alloc_init(error, &nmreq, 25 * NFSX_UNSIGNED + len);
nfsm_chain_add_compound_header(error, &nmreq, "write", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_WRITE);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_32(error, &nmreq, 0xffffffff);
nfsm_chain_add_64(error, &nmreq, uiop->uio_offset);
nfsm_chain_add_32(error, &nmreq, iomode);
nfsm_chain_add_32(error, &nmreq, len);
if (!error)
error = nfsm_chain_add_uio(&nmreq, uiop, len);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request_async(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, cb, reqp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
return (error);
}
int
nfs4_write_rpc_async_finish(
nfsnode_t np,
struct nfsreq *req,
int *iomodep,
size_t *rlenp,
uint64_t *wverfp)
{
struct nfsmount *nmp;
int error = 0, lockerror = ENOENT, nfsvers, numops, status;
int committed = NFS_WRITE_FILESYNC;
size_t rlen = 0;
u_int64_t xid, wverf;
mount_t mp;
struct nfsm_chain nmrep;
nmp = NFSTONMP(np);
if (!nmp) {
nfs_request_async_cancel(req);
return (ENXIO);
}
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmrep);
error = nfs_request_async_finish(req, &nmrep, &xid, &status);
if (error == EINPROGRESS)
return (error);
nmp = NFSTONMP(np);
if (!nmp)
error = ENXIO;
if (!error && (lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_WRITE);
nfsm_chain_get_32(error, &nmrep, rlen);
nfsmout_if(error);
*rlenp = rlen;
if (rlen <= 0)
error = NFSERR_IO;
nfsm_chain_get_32(error, &nmrep, committed);
nfsm_chain_get_64(error, &nmrep, wverf);
nfsmout_if(error);
if (wverfp)
*wverfp = wverf;
lck_mtx_lock(&nmp->nm_lock);
if (!(nmp->nm_state & NFSSTA_HASWRITEVERF)) {
nmp->nm_verf = wverf;
nmp->nm_state |= NFSSTA_HASWRITEVERF;
} else if (nmp->nm_verf != wverf) {
nmp->nm_verf = wverf;
}
lck_mtx_unlock(&nmp->nm_lock);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, np, nfsvers, NULL, &xid);
nfsmout:
if (!lockerror)
nfs_unlock(np);
nfsm_chain_cleanup(&nmrep);
if ((committed != NFS_WRITE_FILESYNC) && nfs_allow_async &&
((mp = NFSTOMP(np))) && (vfs_flags(mp) & MNT_ASYNC))
committed = NFS_WRITE_FILESYNC;
*iomodep = committed;
return (error);
}
int
nfs4_remove_rpc(
nfsnode_t dnp,
char *name,
int namelen,
thread_t thd,
kauth_cred_t cred)
{
int error = 0, remove_error = 0, status;
struct nfsmount *nmp;
int nfsvers, numops;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
nmp = NFSTONMP(dnp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 3;
nfsm_chain_build_alloc_init(error, &nmreq, 17 * NFSX_UNSIGNED + namelen);
nfsm_chain_add_compound_header(error, &nmreq, "remove", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_REMOVE);
nfsm_chain_add_string(error, &nmreq, name, namelen);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request2(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, 0, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_REMOVE);
remove_error = error;
nfsm_chain_check_change_info(error, &nmrep, dnp);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(dnp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
dnp->n_flag |= NMODIFIED;
return (remove_error);
}
int
nfs4_rename_rpc(
nfsnode_t fdnp,
char *fnameptr,
int fnamelen,
nfsnode_t tdnp,
char *tnameptr,
int tnamelen,
vfs_context_t ctx)
{
int error = 0, status, nfsvers, numops;
struct nfsmount *nmp;
u_int64_t xid, savedxid;
struct nfsm_chain nmreq, nmrep;
nmp = NFSTONMP(fdnp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 7;
nfsm_chain_build_alloc_init(error, &nmreq, 30 * NFSX_UNSIGNED + fnamelen + tnamelen);
nfsm_chain_add_compound_header(error, &nmreq, "rename", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, fdnp->n_fhp, fdnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, tdnp->n_fhp, tdnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_RENAME);
nfsm_chain_add_string(error, &nmreq, fnameptr, fnamelen);
nfsm_chain_add_string(error, &nmreq, tnameptr, tnamelen);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(fdnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_RENAME);
nfsm_chain_check_change_info(error, &nmrep, fdnp);
nfsm_chain_check_change_info(error, &nmrep, tdnp);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
savedxid = xid;
nfsm_chain_loadattr(error, &nmrep, tdnp, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(tdnp);
nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
xid = savedxid;
nfsm_chain_loadattr(error, &nmrep, fdnp, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(fdnp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
fdnp->n_flag |= NMODIFIED;
tdnp->n_flag |= NMODIFIED;
if (error == EEXIST)
error = 0;
return (error);
}
#define DIRHDSIZ ((int)(sizeof(struct dirent) - (MAXNAMLEN + 1)))
int
nfs4_readdir_rpc(nfsnode_t dnp, struct uio *uiop, vfs_context_t ctx)
{
size_t len, tlen, skiplen, left;
struct dirent *dp = NULL;
vnode_t newvp;
nfsuint64 *cookiep;
struct componentname cn, *cnp = &cn;
nfsuint64 cookie;
struct nfsmount *nmp;
nfsnode_t np;
int error = 0, lockerror, status, more_entries = 1, blksiz = 0, bigenough = 1;
int nfsvers, rdirplus, nmreaddirsize, nmrsize, eof, i, numops;
u_int64_t xid, savexid;
struct nfs_vattr nvattr;
struct nfsm_chain nmreq, nmrep;
char *cp;
const char *tag;
uint32_t entry_attrs[NFS_ATTR_BITMAP_LEN];
fhandle_t fh;
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
(uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
panic("nfs4_readdir_rpc: bad uio");
#endif
nmp = NFSTONMP(dnp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nmreaddirsize = nmp->nm_readdirsize;
nmrsize = nmp->nm_rsize;
rdirplus = (nmp->nm_flag & NFSMNT_RDIRPLUS) ? 1 : 0;
bzero(cnp, sizeof(*cnp));
newvp = NULLVP;
if (rdirplus) {
tag = "READDIRPLUS";
for (i=0; i < NFS_ATTR_BITMAP_LEN; i++)
entry_attrs[i] =
nfs_getattr_bitmap[i] &
nmp->nm_fsattr.nfsa_supp_attr[i];
NFS_BITMAP_SET(entry_attrs, NFS_FATTR_FILEHANDLE);
} else {
tag = "READDIR";
NFS_CLEAR_ATTRIBUTES(entry_attrs);
NFS_BITMAP_SET(entry_attrs, NFS_FATTR_TYPE);
NFS_BITMAP_SET(entry_attrs, NFS_FATTR_FILEID);
}
NFS_BITMAP_SET(entry_attrs, NFS_FATTR_RDATTR_ERROR);
if ((lockerror = nfs_lock(dnp, NFS_NODE_LOCK_SHARED)))
return (lockerror);
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep)
cookie = *cookiep;
else {
nfs_unlock(dnp);
return (NFSERR_BAD_COOKIE);
}
if ((uiop->uio_offset == 0) &&
((2*(4 + DIRHDSIZ)) <= uio_uio_resid(uiop))) {
len = 2;
tlen = nfsm_rndup(len);
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_fileno = dnp->n_vattr.nva_fileid;
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ;
dp->d_type = DT_DIR;
strlcpy(dp->d_name, ".", len);
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += DIRHDSIZ + tlen;
uio_iov_base_add(uiop, DIRHDSIZ + tlen);
uio_uio_resid_add(uiop, -(DIRHDSIZ + tlen));
uio_iov_len_add(uiop, -(DIRHDSIZ + tlen));
len = 3;
tlen = nfsm_rndup(len);
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
if (dnp->n_parent)
dp->d_fileno = VTONFS(dnp->n_parent)->n_vattr.nva_fileid;
else
dp->d_fileno = dnp->n_vattr.nva_fileid;
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ;
dp->d_type = DT_DIR;
strlcpy(dp->d_name, "..", len);
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += DIRHDSIZ + tlen;
uio_iov_base_add(uiop, DIRHDSIZ + tlen);
uio_uio_resid_add(uiop, -(DIRHDSIZ + tlen));
uio_iov_len_add(uiop, -(DIRHDSIZ + tlen));
cookie.nfsuquad[0] = 0;
cookie.nfsuquad[1] = 2;
}
while (more_entries && bigenough) {
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
nfsm_assert(error, NFSTONMP(dnp), ENXIO);
numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, tag, numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_READDIR);
nfsm_chain_add_32(error, &nmreq, cookie.nfsuquad[0]);
if ((cookie.nfsuquad[0] == 0) && (cookie.nfsuquad[1] <= 2))
nfsm_chain_add_32(error, &nmreq, 0);
else
nfsm_chain_add_32(error, &nmreq, cookie.nfsuquad[1]);
nfsm_chain_add_32(error, &nmreq, dnp->n_cookieverf.nfsuquad[0]);
nfsm_chain_add_32(error, &nmreq, dnp->n_cookieverf.nfsuquad[1]);
nfsm_chain_add_32(error, &nmreq, nmreaddirsize);
nfsm_chain_add_32(error, &nmreq, nmrsize);
nfsm_chain_add_bitmap(error, &nmreq, entry_attrs, NFS_ATTR_BITMAP_LEN);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfs_unlock(dnp);
nfsmout_if(error);
error = nfs_request(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
if ((lockerror = nfs_lock(dnp, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
savexid = xid;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, NULL, &xid);
nfsm_chain_op_check(error, &nmrep, NFS_OP_READDIR);
nfsm_chain_get_32(error, &nmrep, dnp->n_cookieverf.nfsuquad[0]);
nfsm_chain_get_32(error, &nmrep, dnp->n_cookieverf.nfsuquad[1]);
nfsm_chain_get_32(error, &nmrep, more_entries);
nfs_unlock(dnp);
nfsmout_if(error);
while (more_entries && bigenough) {
nfsm_chain_get_32(error, &nmrep, cookie.nfsuquad[0]);
nfsm_chain_get_32(error, &nmrep, cookie.nfsuquad[1]);
nfsm_chain_get_32(error, &nmrep, len);
nfsmout_if(error);
if (len <= 0) {
error = EBADRPC;
goto nfsmout;
}
if (len > MAXNAMLEN) {
skiplen = len - MAXNAMLEN;
len = MAXNAMLEN;
} else {
skiplen = 0;
}
tlen = nfsm_rndup(len);
if (tlen == len)
tlen += 4;
left = DIRBLKSIZ - blksiz;
if ((tlen + DIRHDSIZ) > left) {
dp->d_reclen += left;
uio_iov_base_add(uiop, left);
uio_iov_len_add(uiop, -left);
uiop->uio_offset += left;
uio_uio_resid_add(uiop, -left);
blksiz = 0;
}
if ((tlen + DIRHDSIZ) > uio_uio_resid(uiop)) {
bigenough = 0;
break;
}
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_fileno = 0;
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ;
dp->d_type = DT_UNKNOWN;
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += DIRHDSIZ;
#if LP64KERN
uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
#else
uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
uio_iov_len_add(uiop, -((int)DIRHDSIZ));
#endif
uio_iov_base_add(uiop, DIRHDSIZ);
cnp->cn_nameptr = CAST_DOWN(caddr_t, uio_iov_base(uiop));
cnp->cn_namelen = len;
error = nfsm_chain_get_uio(&nmrep, len, uiop);
if (skiplen)
nfsm_chain_adv(error, &nmrep,
nfsm_rndup(len + skiplen) - nfsm_rndup(len));
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL);
if (error && NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_RDATTR_ERROR)) {
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = 0;
}
nfsm_chain_get_32(error, &nmrep, more_entries);
nfsmout_if(error);
cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
tlen -= len;
*cp = '\0';
uio_iov_base_add(uiop, tlen);
uio_iov_len_add(uiop, -tlen);
uiop->uio_offset += tlen;
uio_uio_resid_add(uiop, -tlen);
if ((cnp->cn_nameptr[0] == '.') &&
((len == 1) || ((len == 2) && (cnp->cn_nameptr[1] == '.')))) {
dp->d_namlen = 0;
dp->d_name[0] = '\0';
continue;
}
if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_TYPE))
dp->d_type = IFTODT(VTTOIF(nvattr.nva_type));
if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEID))
dp->d_fileno = (int)nvattr.nva_fileid;
if (rdirplus && NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE) &&
!NFS_CMPFH(dnp, fh.fh_data, fh.fh_len)) {
cnp->cn_hash = 0;
error = nfs_nget(NFSTOMP(dnp), dnp, cnp,
fh.fh_data, fh.fh_len, &nvattr, &xid, NG_MAKEENTRY, &np);
if (!error) {
nfs_unlock(np);
vnode_put(NFSTOV(np));
}
}
nfsmout_if(error);
}
if (!more_entries) {
nfsm_chain_get_32(error, &nmrep, eof);
if (!error)
more_entries = (eof == 0);
}
if ((lockerror = nfs_lock(dnp, NFS_NODE_LOCK_SHARED)))
error = lockerror;
nfsmout_if(error);
nfsm_chain_cleanup(&nmrep);
}
nfs_unlock(dnp);
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->d_reclen += left;
uio_iov_base_add(uiop, left);
uio_iov_len_add(uiop, -left);
uiop->uio_offset += left;
uio_uio_resid_add(uiop, -left);
}
if ((lockerror = nfs_lock(dnp, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsmout_if(error);
if (bigenough)
dnp->n_direofoffset = uiop->uio_offset;
else {
if (uio_uio_resid(uiop) > 0)
printf("EEK! nfs4_readdir_rpc resid > 0\n");
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
if (cookiep)
*cookiep = cookie;
}
nfs_unlock(dnp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_lookup_rpc_async(
nfsnode_t dnp,
char *name,
int namelen,
vfs_context_t ctx,
struct nfsreq **reqp)
{
int error = 0, isdotdot = 0, getattrs = 1, nfsvers, numops;
struct nfsm_chain nmreq;
uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
struct nfsmount *nmp;
nmp = NFSTONMP(dnp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
if ((name[0] == '.') && (name[1] == '.') && (namelen == 2))
isdotdot = 1;
nfsm_chain_null(&nmreq);
numops = getattrs ? 4 : 3;
nfsm_chain_build_alloc_init(error, &nmreq, 20 * NFSX_UNSIGNED + namelen);
nfsm_chain_add_compound_header(error, &nmreq, "lookup", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
if (isdotdot) {
nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUPP);
} else {
nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
nfsm_chain_add_string(error, &nmreq, name, namelen);
}
if (getattrs) {
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
}
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND,
vfs_context_thread(ctx), vfs_context_ucred(ctx), NULL, reqp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
return (error);
}
int
nfs4_lookup_rpc_async_finish(
nfsnode_t dnp,
__unused vfs_context_t ctx,
struct nfsreq *req,
u_int64_t *xidp,
fhandle_t *fhp,
struct nfs_vattr *nvap)
{
int error = 0, status, nfsvers, numops;
uint32_t val = 0;
u_int64_t xid;
struct nfsmount *nmp;
struct nfsm_chain nmrep;
nmp = NFSTONMP(dnp);
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmrep);
error = nfs_request_async_finish(req, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
if (xidp)
*xidp = xid;
nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, NULL, &xid);
nfsm_chain_get_32(error, &nmrep, val);
nfsm_assert(error, (val == NFS_OP_LOOKUPP) || (val == NFS_OP_LOOKUP), EBADRPC);
nfsm_chain_get_32(error, &nmrep, val);
nfsm_assert(error, (val == NFS_OK), val);
nfsmout_if(error || !fhp || !nvap);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvap->nva_bitmap);
error = nfs4_parsefattr(&nmrep, NULL, nvap, fhp, NULL);
if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_FILEHANDLE)) {
error = EBADRPC;
goto nfsmout;
}
nfsmout:
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_commit_rpc(
nfsnode_t np,
u_int64_t offset,
u_int64_t count,
kauth_cred_t cred)
{
struct nfsmount *nmp;
int error = 0, lockerror, status, nfsvers, numops;
u_int64_t xid, wverf;
uint32_t count32;
struct nfsm_chain nmreq, nmrep;
nmp = NFSTONMP(np);
FSDBG(521, np, offset, count, nmp ? nmp->nm_state : 0);
if (!nmp)
return (ENXIO);
if (!(nmp->nm_state & NFSSTA_HASWRITEVERF))
return (0);
nfsvers = nmp->nm_vers;
if (count > UINT32_MAX)
count32 = 0;
else
count32 = count;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 3;
nfsm_chain_build_alloc_init(error, &nmreq, 19 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "commit", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_COMMIT);
nfsm_chain_add_64(error, &nmreq, offset);
nfsm_chain_add_32(error, &nmreq, count32);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND,
current_thread(), cred, 0, &nmrep, &xid, &status);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_COMMIT);
nfsm_chain_get_64(error, &nmrep, wverf);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, np, nfsvers, NULL, &xid);
if (!lockerror)
nfs_unlock(np);
nfsmout_if(error);
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_verf != wverf) {
nmp->nm_verf = wverf;
error = NFSERR_STALEWRITEVERF;
}
lck_mtx_unlock(&nmp->nm_lock);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_pathconf_rpc(
nfsnode_t np,
struct nfs_fsattr *nfsap,
vfs_context_t ctx)
{
u_int64_t xid;
int error = 0, lockerror, status, nfsvers, numops;
struct nfsm_chain nmreq, nmrep;
struct nfsmount *nmp = NFSTONMP(np);
uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
struct nfs_vattr nvattr;
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 2; nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "pathconf", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
NFS_BITMAP_SET(bitmap, NFS_FATTR_MAXLINK);
NFS_BITMAP_SET(bitmap, NFS_FATTR_MAXNAME);
NFS_BITMAP_SET(bitmap, NFS_FATTR_NO_TRUNC);
NFS_BITMAP_SET(bitmap, NFS_FATTR_CHOWN_RESTRICTED);
NFS_BITMAP_SET(bitmap, NFS_FATTR_CASE_INSENSITIVE);
NFS_BITMAP_SET(bitmap, NFS_FATTR_CASE_PRESERVING);
nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, nfsap, &nvattr, NULL, NULL);
nfsmout_if(error);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfs_loadattrcache(np, &nvattr, &xid, 0);
if (!lockerror)
nfs_unlock(np);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_vnop_getattr(
struct vnop_getattr_args *ap)
{
struct vnode_attr *vap = ap->a_vap;
struct nfs_vattr nva;
int error;
error = nfs_getattr(VTONFS(ap->a_vp), &nva, ap->a_context, 0);
if (error)
return (error);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_RAWDEV)) {
dev_t rdev = makedev(nva.nva_rawdev.specdata1, nva.nva_rawdev.specdata2);
VATTR_RETURN(vap, va_rdev, rdev);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_NUMLINKS))
VATTR_RETURN(vap, va_nlink, nva.nva_nlink);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_SIZE))
VATTR_RETURN(vap, va_data_size, nva.nva_size);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_SPACE_USED))
VATTR_RETURN(vap, va_total_alloc, nva.nva_bytes);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_OWNER))
VATTR_RETURN(vap, va_uid, nva.nva_uid);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_OWNER_GROUP))
VATTR_RETURN(vap, va_gid, nva.nva_gid);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_MODE))
VATTR_RETURN(vap, va_mode, nva.nva_mode);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_ARCHIVE) ||
NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_HIDDEN)) {
uint32_t flags = 0;
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_ARCHIVE))
flags |= SF_ARCHIVED;
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_HIDDEN))
flags |= UF_HIDDEN;
VATTR_RETURN(vap, va_flags, flags);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_CREATE)) {
vap->va_create_time.tv_sec = nva.nva_timesec[NFSTIME_CREATE];
vap->va_create_time.tv_nsec = nva.nva_timensec[NFSTIME_CREATE];
VATTR_SET_SUPPORTED(vap, va_create_time);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_ACCESS)) {
vap->va_access_time.tv_sec = nva.nva_timesec[NFSTIME_ACCESS];
vap->va_access_time.tv_nsec = nva.nva_timensec[NFSTIME_ACCESS];
VATTR_SET_SUPPORTED(vap, va_access_time);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_MODIFY)) {
vap->va_modify_time.tv_sec = nva.nva_timesec[NFSTIME_MODIFY];
vap->va_modify_time.tv_nsec = nva.nva_timensec[NFSTIME_MODIFY];
VATTR_SET_SUPPORTED(vap, va_modify_time);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_METADATA)) {
vap->va_change_time.tv_sec = nva.nva_timesec[NFSTIME_CHANGE];
vap->va_change_time.tv_nsec = nva.nva_timensec[NFSTIME_CHANGE];
VATTR_SET_SUPPORTED(vap, va_change_time);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_BACKUP)) {
vap->va_backup_time.tv_sec = nva.nva_timesec[NFSTIME_BACKUP];
vap->va_backup_time.tv_nsec = nva.nva_timensec[NFSTIME_BACKUP];
VATTR_SET_SUPPORTED(vap, va_backup_time);
}
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_FILEID))
VATTR_RETURN(vap, va_fileid, nva.nva_fileid);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TYPE))
VATTR_RETURN(vap, va_type, nva.nva_type);
if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_CHANGE))
VATTR_RETURN(vap, va_filerev, nva.nva_change);
return (error);
}
int
nfs4_setattr_rpc(
nfsnode_t np,
struct vnode_attr *vap,
vfs_context_t ctx,
int alreadylocked)
{
struct nfsmount *nmp = NFSTONMP(np);
int error = 0, lockerror = ENOENT, status, nfsvers, numops;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen, stateid;
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
if (VATTR_IS_ACTIVE(vap, va_flags) && (vap->va_flags & ~(SF_ARCHIVED|UF_HIDDEN))) {
if (vap->va_active & ~VNODE_ATTR_va_flags)
return (EINVAL);
else
return (ENOTSUP);
}
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 3;
nfsm_chain_build_alloc_init(error, &nmreq, 40 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "setattr", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_SETATTR);
if (VATTR_IS_ACTIVE(vap, va_data_size))
stateid = 0xffffffff;
else
stateid = 0;
nfsm_chain_add_32(error, &nmreq, stateid);
nfsm_chain_add_32(error, &nmreq, stateid);
nfsm_chain_add_32(error, &nmreq, stateid);
nfsm_chain_add_32(error, &nmreq, stateid);
nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
if (!alreadylocked && ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE))))
error = lockerror;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_SETATTR);
bmlen = NFS_ATTR_BITMAP_LEN;
nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
nfsmout_if(error);
nfs_vattr_set_supported(bitmap, vap);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, np, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(np);
nfsmout:
if (!alreadylocked && !lockerror)
nfs_unlock(np);
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs4_vnop_open(struct vnop_open_args *ap)
{
return nfs3_vnop_open(ap);
}
int
nfs4_vnop_close(struct vnop_close_args *ap)
{
return nfs3_vnop_close(ap);
}
int
nfs4_vnop_advlock(__unused struct vnop_advlock_args *ap)
{
return (ENOSYS);
}
int
nfs4_vnop_create(
struct vnop_create_args *ap)
{
vfs_context_t ctx = ap->a_context;
struct componentname *cnp = ap->a_cnp;
struct vnode_attr *vap = ap->a_vap;
vnode_t dvp = ap->a_dvp;
vnode_t *vpp = ap->a_vpp;
struct nfsmount *nmp;
struct nfs_vattr nvattr, dnvattr;
int error = 0, create_error = EIO, lockerror = ENOENT, status;
int nfsvers, numops;
u_int64_t xid, savedxid = 0;
nfsnode_t dnp = VTONFS(dvp);
nfsnode_t np = NULL;
vnode_t newvp = NULL;
struct nfsm_chain nmreq, nmrep;
uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
uint32_t seqid, stateid[4], rflags, delegation, val;
fhandle_t fh;
struct nfsreq *req = NULL;
struct nfs_dulookup dul;
static uint32_t nfs4_open_owner_hack = 0;
nmp = VTONMP(dvp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
seqid = stateid[0] = stateid[1] = stateid[2] = stateid[3] = 0;
rflags = 0;
nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen);
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 6;
nfsm_chain_build_alloc_init(error, &nmreq, 53 * NFSX_UNSIGNED + cnp->cn_namelen);
nfsm_chain_add_compound_header(error, &nmreq, "create", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN);
nfsm_chain_add_32(error, &nmreq, seqid);
seqid++;
nfsm_chain_add_32(error, &nmreq, NFS_OPEN_SHARE_ACCESS_BOTH);
nfsm_chain_add_32(error, &nmreq, NFS_OPEN_SHARE_DENY_NONE);
nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid); OSAddAtomic(1, (SInt32*)&nfs4_open_owner_hack);
nfsm_chain_add_32(error, &nmreq, sizeof(nfs4_open_owner_hack));
nfsm_chain_add_opaque(error, &nmreq, &nfs4_open_owner_hack, sizeof(nfs4_open_owner_hack)); nfsm_chain_add_32(error, &nmreq, NFS_OPEN_CREATE);
nfsm_chain_add_32(error, &nmreq, NFS_CREATE_UNCHECKED); nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
nfsm_chain_add_32(error, &nmreq, NFS_CLAIM_NULL);
nfsm_chain_add_string(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
if ((lockerror = nfs_lock(dnp, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsmout_if(error);
error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND,
vfs_context_thread(ctx), vfs_context_ucred(ctx), NULL, &req);
if (!error) {
nfs_dulookup_start(&dul, dnp, ctx);
error = nfs_request_async_finish(req, &nmrep, &xid, &status);
}
savedxid = xid;
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN);
nfsm_chain_get_32(error, &nmrep, stateid[0]);
nfsm_chain_get_32(error, &nmrep, stateid[1]);
nfsm_chain_get_32(error, &nmrep, stateid[2]);
nfsm_chain_get_32(error, &nmrep, stateid[3]);
nfsm_chain_check_change_info(error, &nmrep, dnp);
nfsm_chain_get_32(error, &nmrep, rflags);
bmlen = NFS_ATTR_BITMAP_LEN;
nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
nfsm_chain_get_32(error, &nmrep, delegation);
if (!error)
switch (delegation) {
case NFS_OPEN_DELEGATE_NONE:
break;
case NFS_OPEN_DELEGATE_READ:
printf("nfs4_vnop_create: read delegation?\n");
nfsm_chain_adv(error, &nmrep, 5*NFSX_UNSIGNED);
nfsm_chain_adv(error, &nmrep, 3 * NFSX_UNSIGNED);
nfsm_chain_get_32(error, &nmrep, val);
nfsm_chain_adv(error, &nmrep, nfsm_rndup(val));
break;
case NFS_OPEN_DELEGATE_WRITE:
printf("nfs4_vnop_create: write delegation?\n");
nfsm_chain_adv(error, &nmrep, 5*NFSX_UNSIGNED);
nfsm_chain_adv(error, &nmrep, 3*NFSX_UNSIGNED);
nfsm_chain_adv(error, &nmrep, 3 * NFSX_UNSIGNED);
nfsm_chain_get_32(error, &nmrep, val);
nfsm_chain_adv(error, &nmrep, nfsm_rndup(val));
break;
default:
error = EBADRPC;
break;
}
create_error = error;
nfsmout_if(error);
nfs_vattr_set_supported(bitmap, vap);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL);
nfsmout_if(error);
if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
printf("nfs: open/create didn't return filehandle?\n");
error = EBADRPC;
goto nfsmout;
}
nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(dnp);
if (rflags & NFS_OPEN_RESULT_CONFIRM) {
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
numops = 3;
nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "create_confirm", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, fh.fh_data, fh.fh_len);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN_CONFIRM);
nfsm_chain_add_32(error, &nmreq, stateid[0]);
nfsm_chain_add_32(error, &nmreq, stateid[1]);
nfsm_chain_add_32(error, &nmreq, stateid[2]);
nfsm_chain_add_32(error, &nmreq, stateid[3]);
nfsm_chain_add_32(error, &nmreq, seqid);
seqid++;
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN_CONFIRM);
nfsm_chain_get_32(error, &nmrep, stateid[0]);
nfsm_chain_get_32(error, &nmrep, stateid[1]);
nfsm_chain_get_32(error, &nmrep, stateid[2]);
nfsm_chain_get_32(error, &nmrep, stateid[3]);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, NULL, &nvattr, NULL, NULL);
nfsmout_if(error);
savedxid = xid;
}
nfsmout_if(error);
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
numops = 2;
nfsm_chain_build_alloc_init(error, &nmreq, 19 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "create_close", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, fh.fh_data, fh.fh_len);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_CLOSE);
nfsm_chain_add_32(error, &nmreq, seqid);
seqid++;
nfsm_chain_add_32(error, &nmreq, stateid[0]);
nfsm_chain_add_32(error, &nmreq, stateid[1]);
nfsm_chain_add_32(error, &nmreq, stateid[2]);
nfsm_chain_add_32(error, &nmreq, stateid[3]);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_CLOSE);
nfsm_chain_get_32(error, &nmrep, stateid[0]);
nfsm_chain_get_32(error, &nmrep, stateid[1]);
nfsm_chain_get_32(error, &nmrep, stateid[2]);
nfsm_chain_get_32(error, &nmrep, stateid[3]);
if (error)
printf("nfs4_vnop_create: close error %d\n", error);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
if (!lockerror) {
if (!create_error && (dnp->n_flag & NNEGNCENTRIES)) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(dvp);
}
dnp->n_flag |= NMODIFIED;
if (!nfs_getattr(dnp, &dnvattr, ctx, 1)) {
if (NFS_CHANGED_NC(nfsvers, dnp, &dnvattr)) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge(dvp);
NFS_CHANGED_UPDATE_NC(nfsvers, dnp, &dnvattr);
}
}
}
if (!error && fh.fh_len) {
xid = savedxid;
error = nfs_nget(NFSTOMP(dnp), dnp, cnp, fh.fh_data, fh.fh_len, &nvattr, &xid, NG_MAKEENTRY, &np);
if (!error)
newvp = NFSTOV(np);
}
nfs_dulookup_finish(&dul, dnp, ctx);
if ((create_error == EEXIST) || (!create_error && !newvp)) {
error = nfs_lookitup(dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx, &np);
if (!error) {
newvp = NFSTOV(np);
if (vnode_vtype(newvp) != VLNK)
error = EEXIST;
}
}
if (!lockerror)
nfs_unlock(dnp);
if (error) {
if (newvp) {
nfs_unlock(np);
vnode_put(newvp);
}
} else {
nfs_unlock(np);
*vpp = newvp;
}
return (error);
}
static int
nfs4_create_rpc(
vfs_context_t ctx,
nfsnode_t dnp,
struct componentname *cnp,
struct vnode_attr *vap,
int type,
char *link,
nfsnode_t *npp)
{
struct nfsmount *nmp;
struct nfs_vattr nvattr, dnvattr;
int error = 0, create_error = EIO, lockerror = ENOENT, status;
int nfsvers, numops;
u_int64_t xid, savedxid = 0;
nfsnode_t np = NULL;
vnode_t newvp = NULL;
struct nfsm_chain nmreq, nmrep;
uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
const char *tag;
nfs_specdata sd;
fhandle_t fh;
struct nfsreq *req = NULL;
struct nfs_dulookup dul;
nmp = NFSTONMP(dnp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
sd.specdata1 = sd.specdata2 = 0;
switch (type) {
case NFLNK:
tag = "symlink";
break;
case NFBLK:
case NFCHR:
tag = "mknod";
if (!VATTR_IS_ACTIVE(vap, va_rdev))
return (EINVAL);
sd.specdata1 = major(vap->va_rdev);
sd.specdata2 = minor(vap->va_rdev);
break;
case NFSOCK:
case NFFIFO:
tag = "mknod";
break;
case NFDIR:
tag = "mkdir";
break;
default:
return (EINVAL);
}
nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen);
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 6;
nfsm_chain_build_alloc_init(error, &nmreq, 66 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, tag, numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_CREATE);
nfsm_chain_add_32(error, &nmreq, type);
if (type == NFLNK) {
nfsm_chain_add_string(error, &nmreq, link, strlen(link));
} else if ((type == NFBLK) || (type == NFCHR)) {
nfsm_chain_add_32(error, &nmreq, sd.specdata1);
nfsm_chain_add_32(error, &nmreq, sd.specdata2);
}
nfsm_chain_add_string(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen);
nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
if ((lockerror = nfs_lock(dnp, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsmout_if(error);
error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND,
vfs_context_thread(ctx), vfs_context_ucred(ctx), NULL, &req);
if (!error) {
nfs_dulookup_start(&dul, dnp, ctx);
error = nfs_request_async_finish(req, &nmrep, &xid, &status);
}
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
nfsmout_if(error);
nfsm_chain_op_check(error, &nmrep, NFS_OP_CREATE);
nfsm_chain_check_change_info(error, &nmrep, dnp);
bmlen = NFS_ATTR_BITMAP_LEN;
nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
create_error = error;
nfsmout_if(error);
nfs_vattr_set_supported(bitmap, vap);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL);
nfsmout_if(error);
if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
printf("nfs: create/%s didn't return filehandle?\n", tag);
error = EBADRPC;
goto nfsmout;
}
nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
savedxid = xid;
nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(dnp);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
if (!lockerror) {
if (!create_error && (dnp->n_flag & NNEGNCENTRIES)) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(NFSTOV(dnp));
}
dnp->n_flag |= NMODIFIED;
if (!nfs_getattr(dnp, &dnvattr, ctx, 1)) {
if (NFS_CHANGED_NC(nfsvers, dnp, &dnvattr)) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge(NFSTOV(dnp));
NFS_CHANGED_UPDATE_NC(nfsvers, dnp, &dnvattr);
}
}
}
if (!error && fh.fh_len) {
xid = savedxid;
error = nfs_nget(NFSTOMP(dnp), dnp, cnp, fh.fh_data, fh.fh_len, &nvattr, &xid, NG_MAKEENTRY, &np);
if (!error)
newvp = NFSTOV(np);
}
nfs_dulookup_finish(&dul, dnp, ctx);
if ((create_error == EEXIST) || (!create_error && !newvp)) {
error = nfs_lookitup(dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx, &np);
if (!error) {
newvp = NFSTOV(np);
if (vnode_vtype(newvp) != VLNK)
error = EEXIST;
}
}
if (!lockerror)
nfs_unlock(dnp);
if (error) {
if (newvp) {
nfs_unlock(np);
vnode_put(newvp);
}
} else {
nfs_unlock(np);
*npp = np;
}
return (error);
}
int
nfs4_vnop_mknod(
struct vnop_mknod_args *ap)
{
nfsnode_t np = NULL;
struct nfsmount *nmp;
int error;
nmp = VTONMP(ap->a_dvp);
if (!nmp)
return (ENXIO);
if (!VATTR_IS_ACTIVE(ap->a_vap, va_type))
return (EINVAL);
switch (ap->a_vap->va_type) {
case VBLK:
case VCHR:
case VFIFO:
case VSOCK:
break;
default:
return (ENOTSUP);
}
error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
vtonfs_type(ap->a_vap->va_type, nmp->nm_vers), NULL, &np);
if (!error)
*ap->a_vpp = NFSTOV(np);
return (error);
}
int
nfs4_vnop_mkdir(
struct vnop_mkdir_args *ap)
{
nfsnode_t np = NULL;
int error;
error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
NFDIR, NULL, &np);
if (!error)
*ap->a_vpp = NFSTOV(np);
return (error);
}
int
nfs4_vnop_symlink(
struct vnop_symlink_args *ap)
{
nfsnode_t np = NULL;
int error;
error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
NFLNK, ap->a_target, &np);
if (!error)
*ap->a_vpp = NFSTOV(np);
return (error);
}
int
nfs4_vnop_link(
struct vnop_link_args *ap)
{
vfs_context_t ctx = ap->a_context;
vnode_t vp = ap->a_vp;
vnode_t tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
int error = 0, status;
struct nfsmount *nmp;
nfsnode_t np = VTONFS(vp);
nfsnode_t tdnp = VTONFS(tdvp);
int nfsvers, numops;
u_int64_t xid, savedxid;
struct nfsm_chain nmreq, nmrep;
if (vnode_mount(vp) != vnode_mount(tdvp))
return (EXDEV);
nmp = VTONMP(vp);
if (!nmp)
return (ENXIO);
nfsvers = nmp->nm_vers;
nfs_flush(np, MNT_WAIT, vfs_context_thread(ctx), V_IGNORE_WRITEERR);
error = nfs_lock2(tdnp, np, NFS_NODE_LOCK_EXCLUSIVE);
if (error)
return (error);
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 7;
nfsm_chain_build_alloc_init(error, &nmreq, 29 * NFSX_UNSIGNED + cnp->cn_namelen);
nfsm_chain_add_compound_header(error, &nmreq, "link", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, tdnp->n_fhp, tdnp->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_LINK);
nfsm_chain_add_string(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(tdnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_LINK);
nfsm_chain_check_change_info(error, &nmrep, tdnp);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
savedxid = xid;
nfsm_chain_loadattr(error, &nmrep, tdnp, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(tdnp);
nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
xid = savedxid;
nfsm_chain_loadattr(error, &nmrep, np, nfsvers, NULL, &xid);
if (error)
NATTRINVALIDATE(np);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
tdnp->n_flag |= NMODIFIED;
if (error == EEXIST)
error = 0;
if (!error && (tdnp->n_flag & NNEGNCENTRIES)) {
tdnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(tdvp);
}
nfs_unlock2(tdnp, np);
return (error);
}
int
nfs4_vnop_rmdir(
struct vnop_rmdir_args *ap)
{
vfs_context_t ctx = ap->a_context;
vnode_t vp = ap->a_vp;
vnode_t dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
int error = 0;
nfsnode_t np = VTONFS(vp);
nfsnode_t dnp = VTONFS(dvp);
struct nfs_vattr dnvattr;
struct nfs_dulookup dul;
if (vnode_vtype(vp) != VDIR)
return (EINVAL);
nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen);
if ((error = nfs_lock2(dnp, np, NFS_NODE_LOCK_EXCLUSIVE)))
return (error);
nfs_dulookup_start(&dul, dnp, ctx);
error = nfs4_remove_rpc(dnp, cnp->cn_nameptr, cnp->cn_namelen,
vfs_context_thread(ctx), vfs_context_ucred(ctx));
cache_purge(vp);
if (!nfs_getattr(dnp, &dnvattr, ctx, 1)) {
if (NFS_CHANGED_NC(NFS_VER4, dnp, &dnvattr)) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge(dvp);
NFS_CHANGED_UPDATE_NC(NFS_VER4, dnp, &dnvattr);
}
}
nfs_dulookup_finish(&dul, dnp, ctx);
nfs_unlock2(dnp, np);
if (error == ENOENT)
error = 0;
if (!error) {
lck_mtx_lock(nfs_node_hash_mutex);
if (np->n_hflag & NHHASHED) {
LIST_REMOVE(np, n_hash);
np->n_hflag &= ~NHHASHED;
FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
}
lck_mtx_unlock(nfs_node_hash_mutex);
}
return (error);
}