#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/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>
#include <sys/kdebug.h>
#define FSDBG(A, B, C, D, E) \
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_NONE, \
(int)(B), (int)(C), (int)(D), (int)(E), 0)
#define FSDBG_TOP(A, B, C, D, E) \
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_START, \
(int)(B), (int)(C), (int)(D), (int)(E), 0)
#define FSDBG_BOT(A, B, C, D, E) \
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_END, \
(int)(B), (int)(C), (int)(D), (int)(E), 0)
static int nfsspec_read(struct vnop_read_args *);
static int nfsspec_write(struct vnop_write_args *);
static int nfsfifo_read(struct vnop_read_args *);
static int nfsfifo_write(struct vnop_write_args *);
static int nfsspec_close(struct vnop_close_args *);
static int nfsfifo_close(struct vnop_close_args *);
static int nfs_ioctl(struct vnop_ioctl_args *);
static int nfs_select(struct vnop_select_args *);
static int nfs_setattrrpc(vnode_t,struct vnode_attr *,kauth_cred_t,proc_t);
static int nfs_lookup(struct vnop_lookup_args *);
static int nfs_create(struct vnop_create_args *);
static int nfs_mknod(struct vnop_mknod_args *);
static int nfs_open(struct vnop_open_args *);
static int nfs_close(struct vnop_close_args *);
static int nfs_access(struct vnop_access_args *);
static int nfs_vnop_getattr(struct vnop_getattr_args *);
static int nfs_setattr(struct vnop_setattr_args *);
static int nfs_read(struct vnop_read_args *);
static int nfs_mmap(struct vnop_mmap_args *);
static int nfs_fsync(struct vnop_fsync_args *);
static int nfs_remove(struct vnop_remove_args *);
static int nfs_link(struct vnop_link_args *);
static int nfs_rename(struct vnop_rename_args *);
static int nfs_mkdir(struct vnop_mkdir_args *);
static int nfs_rmdir(struct vnop_rmdir_args *);
static int nfs_symlink(struct vnop_symlink_args *);
static int nfs_readdir(struct vnop_readdir_args *);
static int nfs_lookitup(vnode_t,char *,int,kauth_cred_t,proc_t,struct nfsnode **);
static int nfs_sillyrename(vnode_t,vnode_t,struct componentname *,kauth_cred_t,proc_t);
static int nfs_readlink(struct vnop_readlink_args *);
static int nfs_pathconf(struct vnop_pathconf_args *);
static int nfs_advlock(struct vnop_advlock_args *);
static int nfs_pagein(struct vnop_pagein_args *);
static int nfs_pageout(struct vnop_pageout_args *);
static int nfs_blktooff(struct vnop_blktooff_args *);
static int nfs_offtoblk(struct vnop_offtoblk_args *);
static int nfs_blockmap(struct vnop_blockmap_args *);
vnop_t **nfsv2_vnodeop_p;
static struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
{ &vnop_default_desc, (vnop_t *)vn_default_error },
{ &vnop_lookup_desc, (vnop_t *)nfs_lookup },
{ &vnop_create_desc, (vnop_t *)nfs_create },
{ &vnop_mknod_desc, (vnop_t *)nfs_mknod },
{ &vnop_open_desc, (vnop_t *)nfs_open },
{ &vnop_close_desc, (vnop_t *)nfs_close },
{ &vnop_access_desc, (vnop_t *)nfs_access },
{ &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr },
{ &vnop_setattr_desc, (vnop_t *)nfs_setattr },
{ &vnop_read_desc, (vnop_t *)nfs_read },
{ &vnop_write_desc, (vnop_t *)nfs_write },
{ &vnop_ioctl_desc, (vnop_t *)nfs_ioctl },
{ &vnop_select_desc, (vnop_t *)nfs_select },
{ &vnop_revoke_desc, (vnop_t *)nfs_revoke },
{ &vnop_mmap_desc, (vnop_t *)nfs_mmap },
{ &vnop_fsync_desc, (vnop_t *)nfs_fsync },
{ &vnop_remove_desc, (vnop_t *)nfs_remove },
{ &vnop_link_desc, (vnop_t *)nfs_link },
{ &vnop_rename_desc, (vnop_t *)nfs_rename },
{ &vnop_mkdir_desc, (vnop_t *)nfs_mkdir },
{ &vnop_rmdir_desc, (vnop_t *)nfs_rmdir },
{ &vnop_symlink_desc, (vnop_t *)nfs_symlink },
{ &vnop_readdir_desc, (vnop_t *)nfs_readdir },
{ &vnop_readlink_desc, (vnop_t *)nfs_readlink },
{ &vnop_inactive_desc, (vnop_t *)nfs_inactive },
{ &vnop_reclaim_desc, (vnop_t *)nfs_reclaim },
{ &vnop_strategy_desc, (vnop_t *)err_strategy },
{ &vnop_pathconf_desc, (vnop_t *)nfs_pathconf },
{ &vnop_advlock_desc, (vnop_t *)nfs_advlock },
{ &vnop_bwrite_desc, (vnop_t *)err_bwrite },
{ &vnop_pagein_desc, (vnop_t *)nfs_pagein },
{ &vnop_pageout_desc, (vnop_t *)nfs_pageout },
{ &vnop_copyfile_desc, (vnop_t *)err_copyfile },
{ &vnop_blktooff_desc, (vnop_t *)nfs_blktooff },
{ &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk },
{ &vnop_blockmap_desc, (vnop_t *)nfs_blockmap },
{ NULL, NULL }
};
struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
{ &nfsv2_vnodeop_p, nfsv2_vnodeop_entries };
#ifdef __FreeBSD__
VNODEOP_SET(nfsv2_vnodeop_opv_desc);
#endif
vnop_t **spec_nfsv2nodeop_p;
static struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
{ &vnop_default_desc, (vnop_t *)vn_default_error },
{ &vnop_lookup_desc, (vnop_t *)spec_lookup },
{ &vnop_create_desc, (vnop_t *)spec_create },
{ &vnop_mknod_desc, (vnop_t *)spec_mknod },
{ &vnop_open_desc, (vnop_t *)spec_open },
{ &vnop_close_desc, (vnop_t *)nfsspec_close },
{ &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr },
{ &vnop_setattr_desc, (vnop_t *)nfs_setattr },
{ &vnop_read_desc, (vnop_t *)nfsspec_read },
{ &vnop_write_desc, (vnop_t *)nfsspec_write },
{ &vnop_ioctl_desc, (vnop_t *)spec_ioctl },
{ &vnop_select_desc, (vnop_t *)spec_select },
{ &vnop_revoke_desc, (vnop_t *)spec_revoke },
{ &vnop_mmap_desc, (vnop_t *)spec_mmap },
{ &vnop_fsync_desc, (vnop_t *)nfs_fsync },
{ &vnop_remove_desc, (vnop_t *)spec_remove },
{ &vnop_link_desc, (vnop_t *)spec_link },
{ &vnop_rename_desc, (vnop_t *)spec_rename },
{ &vnop_mkdir_desc, (vnop_t *)spec_mkdir },
{ &vnop_rmdir_desc, (vnop_t *)spec_rmdir },
{ &vnop_symlink_desc, (vnop_t *)spec_symlink },
{ &vnop_readdir_desc, (vnop_t *)spec_readdir },
{ &vnop_readlink_desc, (vnop_t *)spec_readlink },
{ &vnop_inactive_desc, (vnop_t *)nfs_inactive },
{ &vnop_reclaim_desc, (vnop_t *)nfs_reclaim },
{ &vnop_strategy_desc, (vnop_t *)spec_strategy },
{ &vnop_pathconf_desc, (vnop_t *)spec_pathconf },
{ &vnop_advlock_desc, (vnop_t *)spec_advlock },
{ &vnop_bwrite_desc, (vnop_t *)vn_bwrite },
{ &vnop_pagein_desc, (vnop_t *)nfs_pagein },
{ &vnop_pageout_desc, (vnop_t *)nfs_pageout },
{ &vnop_blktooff_desc, (vnop_t *)nfs_blktooff },
{ &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk },
{ &vnop_blockmap_desc, (vnop_t *)nfs_blockmap },
{ NULL, NULL }
};
struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
{ &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
#ifdef __FreeBSD__
VNODEOP_SET(spec_nfsv2nodeop_opv_desc);
#endif
vnop_t **fifo_nfsv2nodeop_p;
static struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
{ &vnop_default_desc, (vnop_t *)vn_default_error },
{ &vnop_lookup_desc, (vnop_t *)fifo_lookup },
{ &vnop_create_desc, (vnop_t *)fifo_create },
{ &vnop_mknod_desc, (vnop_t *)fifo_mknod },
{ &vnop_open_desc, (vnop_t *)fifo_open },
{ &vnop_close_desc, (vnop_t *)nfsfifo_close },
{ &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr },
{ &vnop_setattr_desc, (vnop_t *)nfs_setattr },
{ &vnop_read_desc, (vnop_t *)nfsfifo_read },
{ &vnop_write_desc, (vnop_t *)nfsfifo_write },
{ &vnop_ioctl_desc, (vnop_t *)fifo_ioctl },
{ &vnop_select_desc, (vnop_t *)fifo_select },
{ &vnop_revoke_desc, (vnop_t *)fifo_revoke },
{ &vnop_mmap_desc, (vnop_t *)fifo_mmap },
{ &vnop_fsync_desc, (vnop_t *)nfs_fsync },
{ &vnop_remove_desc, (vnop_t *)fifo_remove },
{ &vnop_link_desc, (vnop_t *)fifo_link },
{ &vnop_rename_desc, (vnop_t *)fifo_rename },
{ &vnop_mkdir_desc, (vnop_t *)fifo_mkdir },
{ &vnop_rmdir_desc, (vnop_t *)fifo_rmdir },
{ &vnop_symlink_desc, (vnop_t *)fifo_symlink },
{ &vnop_readdir_desc, (vnop_t *)fifo_readdir },
{ &vnop_readlink_desc, (vnop_t *)fifo_readlink },
{ &vnop_inactive_desc, (vnop_t *)nfs_inactive },
{ &vnop_reclaim_desc, (vnop_t *)nfs_reclaim },
{ &vnop_strategy_desc, (vnop_t *)fifo_strategy },
{ &vnop_pathconf_desc, (vnop_t *)fifo_pathconf },
{ &vnop_advlock_desc, (vnop_t *)fifo_advlock },
{ &vnop_bwrite_desc, (vnop_t *)vn_bwrite },
{ &vnop_pagein_desc, (vnop_t *)nfs_pagein },
{ &vnop_pageout_desc, (vnop_t *)nfs_pageout },
{ &vnop_blktooff_desc, (vnop_t *)nfs_blktooff },
{ &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk },
{ &vnop_blockmap_desc, (vnop_t *)nfs_blockmap },
{ NULL, NULL }
};
struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
{ &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
#ifdef __FreeBSD__
VNODEOP_SET(fifo_nfsv2nodeop_opv_desc);
#endif
static int nfs_mknodrpc(vnode_t dvp, vnode_t *vpp,
struct componentname *cnp,
struct vnode_attr *vap,
kauth_cred_t cred, proc_t p);
static int nfs_removerpc(vnode_t dvp, char *name, int namelen,
kauth_cred_t cred, proc_t proc);
static int nfs_renamerpc(vnode_t fdvp, char *fnameptr,
int fnamelen, vnode_t tdvp,
char *tnameptr, int tnamelen,
kauth_cred_t cred, proc_t proc);
extern u_long nfs_xdrneg1;
extern u_long nfs_true, nfs_false;
extern struct nfsstats nfsstats;
extern nfstype nfsv3_type[9];
proc_t nfs_iodwant[NFS_MAXASYNCDAEMON];
struct nfsmount *nfs_iodmount[NFS_MAXASYNCDAEMON];
lck_grp_t *nfs_iod_lck_grp;
lck_grp_attr_t *nfs_iod_lck_grp_attr;
lck_attr_t *nfs_iod_lck_attr;
lck_mtx_t *nfs_iod_mutex;
int nfs_numasync = 0;
int nfs_ioddelwri = 0;
#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
static int nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
#define NFSV3ACCESS_ALL (NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY \
| NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE \
| NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP)
enum actiontype {NOACTION, DUMP, DUMPANDLOG, RETRY, RETRYWITHSLEEP, SEVER};
static int errorcount[ELAST+1];
static const short errortooutcome[ELAST+1] = {
NOACTION,
DUMP,
DUMP,
DUMPANDLOG,
RETRY,
DUMP,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
DUMP,
DUMP,
DUMP,
DUMP,
DUMP,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMP,
DUMP,
DUMPANDLOG,
DUMP,
DUMP,
RETRY,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
DUMPANDLOG,
DUMP,
RETRY,
RETRY,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMP,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
};
static short
nfs_pageouterrorhandler(int error)
{
if (error > ELAST)
return(DUMP);
else
return(errortooutcome[error]);
}
static int
nfs3_access_otw(vnode_t vp,
int wmode,
proc_t p,
kauth_cred_t cred)
{
const int v3 = 1;
u_long *tl;
int error = 0, attrflag;
mbuf_t mreq, mrep, md, mb, mb2;
caddr_t bpos, dpos, cp2;
register long t1, t2;
register caddr_t cp;
u_int32_t rmode;
struct nfsnode *np = VTONFS(vp);
u_int64_t xid;
struct timeval now;
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED);
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_ACCESS]);
nfsm_fhtom(vp, v3);
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(wmode);
nfsm_request(vp, NFSPROC_ACCESS, p, cred, &xid);
if (mrep) {
nfsm_postop_attr_update(vp, 1, attrflag, &xid);
}
if (!error) {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
rmode = fxdr_unsigned(u_int32_t, *tl);
np->n_mode = rmode;
np->n_modeuid = kauth_cred_getuid(cred);
microuptime(&now);
np->n_modestamp = now.tv_sec;
}
nfsm_reqdone;
return error;
}
static int
nfs_access(ap)
struct vnop_access_args *ap;
{
vnode_t vp = ap->a_vp;
int error = 0, dorpc;
u_long mode, wmode;
int v3 = NFS_ISV3(vp);
struct nfsnode *np = VTONFS(vp);
struct timeval now;
kauth_cred_t cred;
if (v3) {
mode = 0;
if (vnode_isdir(vp)) {
if (ap->a_action &
(KAUTH_VNODE_LIST_DIRECTORY |
KAUTH_VNODE_READ_EXTATTRIBUTES))
mode |= NFSV3ACCESS_READ;
if (ap->a_action & KAUTH_VNODE_SEARCH)
mode |= NFSV3ACCESS_LOOKUP;
if (ap->a_action &
(KAUTH_VNODE_ADD_FILE |
KAUTH_VNODE_ADD_SUBDIRECTORY))
mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
if (ap->a_action & KAUTH_VNODE_DELETE_CHILD)
mode |= NFSV3ACCESS_MODIFY;
} else {
if (ap->a_action &
(KAUTH_VNODE_READ_DATA |
KAUTH_VNODE_READ_EXTATTRIBUTES))
mode |= NFSV3ACCESS_READ;
if (ap->a_action & KAUTH_VNODE_WRITE_DATA)
mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
if (ap->a_action & KAUTH_VNODE_APPEND_DATA)
mode |= NFSV3ACCESS_EXTEND;
if (ap->a_action & KAUTH_VNODE_EXECUTE)
mode |= NFSV3ACCESS_EXECUTE;
}
if (ap->a_action & KAUTH_VNODE_DELETE)
mode |= NFSV3ACCESS_DELETE;
if (ap->a_action &
(KAUTH_VNODE_WRITE_ATTRIBUTES |
KAUTH_VNODE_WRITE_EXTATTRIBUTES |
KAUTH_VNODE_WRITE_SECURITY))
mode |= NFSV3ACCESS_MODIFY;
if (ap->a_action & KAUTH_VNODE_CHANGE_OWNER)
mode |= NFSV3ACCESS_MODIFY;
if (nfsaccess_cache_timeout > 0) {
wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY |
NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE |
NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
} else
wmode = mode;
cred = vfs_context_ucred(ap->a_context);
dorpc = 1;
if (NMODEVALID(np)) {
microuptime(&now);
if ((now.tv_sec < (np->n_modestamp + nfsaccess_cache_timeout)) &&
(kauth_cred_getuid(cred) == np->n_modeuid) &&
((np->n_mode & mode) == mode)) {
dorpc = 0;
}
}
if (dorpc) {
error = nfs3_access_otw(vp, wmode, vfs_context_proc(ap->a_context), cred);
}
if (!error) {
if ((mode & NFSV3ACCESS_DELETE) &&
!(np->n_mode & NFSV3ACCESS_DELETE))
np->n_mode |= NFSV3ACCESS_DELETE;
if ((np->n_mode & mode) != mode)
error = EACCES;
}
} else {
if ((ap->a_action & KAUTH_VNODE_WRITE_RIGHTS) && vfs_isrdonly(vnode_mount(vp))) {
error = EROFS;
} else {
error = 0;
}
}
return (error);
}
static int
nfs_open(ap)
struct vnop_open_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfs_vattr nvattr;
kauth_cred_t cred;
proc_t p;
enum vtype vtype;
int error;
vtype = vnode_vtype(vp);
if (vtype != VREG && vtype != VDIR && vtype != VLNK) {
return (EACCES);
}
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
if (np->n_flag & NNEEDINVALIDATE) {
np->n_flag &= ~NNEEDINVALIDATE;
nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
}
if (np->n_flag & NMODIFIED) {
if ((error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1)) == EINTR)
return (error);
NATTRINVALIDATE(np);
if (vtype == VDIR)
np->n_direofoffset = 0;
error = nfs_getattr(vp, &nvattr, cred, p);
if (error)
return (error);
if (vtype == VDIR) {
if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=))
cache_purge(vp);
np->n_ncmtime = nvattr.nva_mtime;
}
np->n_mtime = nvattr.nva_mtime;
} else {
error = nfs_getattr(vp, &nvattr, cred, p);
if (error)
return (error);
if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, !=)) {
if (vtype == VDIR) {
np->n_direofoffset = 0;
nfs_invaldir(vp);
if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=))
cache_purge(vp);
}
if ((error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1)) == EINTR)
return (error);
if (vtype == VDIR)
np->n_ncmtime = nvattr.nva_mtime;
np->n_mtime = nvattr.nva_mtime;
}
}
NATTRINVALIDATE(np);
return (0);
}
static int
nfs_close(ap)
struct vnop_close_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp;
kauth_cred_t cred;
proc_t p;
int error = 0;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
if (vnode_vtype(vp) == VREG) {
#if DIAGNOSTIC
register struct sillyrename *sp = np->n_sillyrename;
if (sp)
kprintf("nfs_close: %s, dvp=%x, vp=%x, ap=%x, np=%x, sp=%x\n",
&sp->s_name[0], (unsigned)(sp->s_dvp), (unsigned)vp,
(unsigned)ap, (unsigned)np, (unsigned)sp);
#endif
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
if (np->n_flag & NNEEDINVALIDATE) {
np->n_flag &= ~NNEEDINVALIDATE;
nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
}
if (np->n_flag & NMODIFIED) {
if (NFS_ISV3(vp)) {
error = nfs_flush(vp, MNT_WAIT, cred, p, 0);
} else {
error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
}
NATTRINVALIDATE(np);
}
if (np->n_flag & NWRITEERR) {
np->n_flag &= ~NWRITEERR;
error = np->n_error;
}
}
return (error);
}
int
nfs_getattr_no_vnode(
mount_t mp,
u_char *fhp,
int fhsize,
kauth_cred_t cred,
proc_t p,
struct nfs_vattr *nvap,
u_int64_t *xidp)
{
mbuf_t mreq, mrep, md, mb, mb2;
caddr_t bpos, dpos;
int t2;
u_long *tl;
caddr_t cp;
struct nfsmount *nmp = VFSTONFS(mp);
int v3 = (nmp->nm_flag & NFSMNT_NFSV3);
int hsiz;
int error = 0;
hsiz = NFSX_FH(v3);
mb = NULL;
if (hsiz >= nfs_mbuf_minclsize)
error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
else
error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
if (error)
return (error);
bpos = mbuf_data(mb);
mreq = mb;
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_GETATTR]);
if (v3) {
t2 = nfsm_rndup(fhsize) + NFSX_UNSIGNED;
if (t2 <= mbuf_trailingspace(mb)) {
nfsm_build(tl, u_long *, t2);
*tl++ = txdr_unsigned(fhsize);
*(tl + ((t2>>2) - 2)) = 0;
bcopy((caddr_t)fhp,(caddr_t)tl, fhsize);
} else if ((t2 = nfsm_strtmbuf(&mb, &bpos, (caddr_t)fhp, fhsize))) {
error = t2;
mbuf_freem(mreq);
goto nfsmout;
}
} else {
nfsm_build(cp, caddr_t, NFSX_V2FH);
bcopy((caddr_t)fhp, cp, NFSX_V2FH);
}
if ((error = nfs_request(NULL, mp, mreq, NFSPROC_GETATTR, p, cred, &mrep, &md, &dpos, xidp))) {
if (error & NFSERR_RETERR)
error &= ~NFSERR_RETERR;
else
goto nfsmout;
}
if (!error) {
error = nfs_parsefattr(&md, &dpos, v3, nvap);
if (error) {
mbuf_freem(mrep);
goto nfsmout;
}
}
nfsm_reqdone;
return (error);
}
int
nfs_getattr(
vnode_t vp,
struct nfs_vattr *nvap,
kauth_cred_t cred,
proc_t p)
{
struct nfsnode *np = VTONFS(vp);
caddr_t cp;
u_long *tl;
int t1, t2;
caddr_t bpos, dpos;
int error = 0;
mbuf_t mreq, mrep, md, mb, mb2;
int v3;
u_int64_t xid;
int avoidfloods;
FSDBG_TOP(513, np->n_size, np, np->n_vattr.nva_size, np->n_flag);
if (np->n_flag & (NACC | NUPD))
np->n_flag |= NCHG;
if ((error = nfs_getattrcache(vp, nvap)) == 0) {
FSDBG_BOT(513, np->n_size, 0, np->n_vattr.nva_size, np->n_flag);
return (0);
}
if (error != ENOENT) {
FSDBG_BOT(513, np->n_size, error, np->n_vattr.nva_size,
np->n_flag);
return (error);
}
if (!VFSTONFS(vnode_mount(vp))) {
FSDBG_BOT(513, np->n_size, ENXIO, np->n_vattr.nva_size, np->n_flag);
return (ENXIO);
}
v3 = NFS_ISV3(vp);
error = 0;
if (v3 && (nfsaccess_cache_timeout > 0) &&
(nfs_attrcachetimeout(vp) > 0)) {
if ((error = nfs3_access_otw(vp, NFSV3ACCESS_ALL, p, cred)))
return (error);
if ((error = nfs_getattrcache(vp, nvap)) == 0)
return (0);
if (error != ENOENT)
return (error);
error = 0;
}
avoidfloods = 0;
tryagain:
nfsm_reqhead(NFSX_FH(v3));
if (error) {
FSDBG_BOT(513, np->n_size, error, np->n_vattr.nva_size, np->n_flag);
return (error);
}
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_GETATTR]);
nfsm_fhtom(vp, v3);
nfsm_request(vp, NFSPROC_GETATTR, p, cred, &xid);
if (!error) {
nfsm_loadattr(vp, v3, nvap, &xid);
if (!xid) {
mbuf_freem(mrep);
mrep = NULL;
FSDBG(513, -1, np, np->n_xid << 32, np->n_xid);
if (avoidfloods++ < 100)
goto tryagain;
panic("nfs_getattr: getattr flood\n");
}
if (nfstimespeccmp(&np->n_mtime, &nvap->nva_mtime, !=)) {
enum vtype vtype = vnode_vtype(vp);
FSDBG(513, -1, np, -1, vp);
if (vtype == VDIR) {
nfs_invaldir(vp);
if (nfstimespeccmp(&np->n_ncmtime, &nvap->nva_mtime, !=))
cache_purge(vp);
}
error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
FSDBG(513, -1, np, -2, error);
if (!error) {
if (vtype == VDIR)
np->n_ncmtime = nvap->nva_mtime;
np->n_mtime = nvap->nva_mtime;
}
}
}
nfsm_reqdone;
FSDBG_BOT(513, np->n_size, -1, np->n_vattr.nva_size, error);
return (error);
}
static int
nfs_vnop_getattr(
struct vnop_getattr_args *ap)
{
int error;
struct nfs_vattr nva;
struct vnode_attr *vap = ap->a_vap;
error = nfs_getattr(ap->a_vp, &nva,
vfs_context_ucred(ap->a_context),
vfs_context_proc(ap->a_context));
if (error)
return (error);
VATTR_RETURN(vap, va_type, nva.nva_type);
VATTR_RETURN(vap, va_mode, nva.nva_mode);
VATTR_RETURN(vap, va_rdev, nva.nva_rdev);
VATTR_RETURN(vap, va_uid, nva.nva_uid);
VATTR_RETURN(vap, va_gid, nva.nva_gid);
VATTR_RETURN(vap, va_nlink, nva.nva_nlink);
VATTR_RETURN(vap, va_fileid, nva.nva_fileid);
VATTR_RETURN(vap, va_data_size, nva.nva_size);
VATTR_RETURN(vap, va_data_alloc, nva.nva_bytes);
VATTR_RETURN(vap, va_iosize, nva.nva_blocksize);
VATTR_RETURN(vap, va_fsid, nva.nva_fsid);
vap->va_access_time.tv_sec = nva.nva_atime.tv_sec;
vap->va_access_time.tv_nsec = nva.nva_atime.tv_nsec;
VATTR_SET_SUPPORTED(vap, va_access_time);
vap->va_modify_time.tv_sec = nva.nva_mtime.tv_sec;
vap->va_modify_time.tv_nsec = nva.nva_mtime.tv_nsec;
VATTR_SET_SUPPORTED(vap, va_modify_time);
vap->va_change_time.tv_sec = nva.nva_ctime.tv_sec;
vap->va_change_time.tv_nsec = nva.nva_ctime.tv_nsec;
VATTR_SET_SUPPORTED(vap, va_change_time);
return (error);
}
static int
nfs_setattr(ap)
struct vnop_setattr_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct vnode_attr *vap = ap->a_vap;
int error = 0;
u_quad_t tsize;
kauth_cred_t cred;
proc_t p;
#ifndef nolint
tsize = (u_quad_t)0;
#endif
if (VATTR_IS_ACTIVE(vap, va_flags))
return (ENOTSUP);
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_SET_SUPPORTED(vap, va_data_size);
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
if ((VATTR_IS_ACTIVE(vap, va_flags) || VATTR_IS_ACTIVE(vap, va_mode) ||
VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid) ||
VATTR_IS_ACTIVE(vap, va_access_time) ||
VATTR_IS_ACTIVE(vap, va_modify_time)) &&
vnode_vfsisrdonly(vp))
return (EROFS);
if (VATTR_IS_ACTIVE(vap, va_data_size)) {
switch (vnode_vtype(vp)) {
case VDIR:
return (EISDIR);
case VCHR:
case VBLK:
case VSOCK:
case VFIFO:
if (!VATTR_IS_ACTIVE(vap, va_modify_time) &&
!VATTR_IS_ACTIVE(vap, va_access_time) &&
!VATTR_IS_ACTIVE(vap, va_mode) &&
!VATTR_IS_ACTIVE(vap, va_uid) &&
!VATTR_IS_ACTIVE(vap, va_gid))
return (0);
VATTR_CLEAR_ACTIVE(vap, va_data_size);
break;
default:
if (vnode_vfsisrdonly(vp))
return (EROFS);
FSDBG_TOP(512, np->n_size, vap->va_data_size,
np->n_vattr.nva_size, np->n_flag);
if (np->n_flag & NMODIFIED) {
if (vap->va_data_size == 0)
error = nfs_vinvalbuf(vp, 0, cred, p, 1);
else
error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
if (error) {
printf("nfs_setattr: nfs_vinvalbuf %d\n", error);
FSDBG_BOT(512, np->n_size, vap->va_data_size,
np->n_vattr.nva_size, -1);
return (error);
}
} else if (np->n_size > vap->va_data_size) {
daddr64_t obn, bn;
int biosize, neweofoff, mustwrite;
struct nfsbuf *bp;
biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
obn = (np->n_size - 1) / biosize;
bn = vap->va_data_size / biosize;
for ( ; obn >= bn; obn--) {
if (!nfs_buf_is_incore(vp, obn))
continue;
error = nfs_buf_get(vp, obn, biosize, 0, NBLK_READ, &bp);
if (error)
continue;
if (obn != bn) {
FSDBG(512, bp, bp->nb_flags, 0, obn);
SET(bp->nb_flags, NB_INVAL);
nfs_buf_release(bp, 1);
continue;
}
mustwrite = 0;
neweofoff = vap->va_data_size - NBOFF(bp);
if (bp->nb_dirtyend && bp->nb_dirtyoff < neweofoff) {
if (bp->nb_dirtyend > neweofoff)
bp->nb_dirtyend = neweofoff;
mustwrite++;
}
bp->nb_dirty &= (1 << round_page_32(neweofoff)/PAGE_SIZE) - 1;
if (bp->nb_dirty)
mustwrite++;
if (!mustwrite) {
FSDBG(512, bp, bp->nb_flags, 0, obn);
SET(bp->nb_flags, NB_INVAL);
nfs_buf_release(bp, 1);
continue;
}
CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL | NB_ASYNC | NB_READ));
SET(bp->nb_flags, NB_STABLE | NB_NOCACHE);
if (bp->nb_wcred == NOCRED) {
kauth_cred_ref(cred);
bp->nb_wcred = cred;
}
error = nfs_buf_write(bp);
if (error) {
FSDBG(512, bp, 0xd00dee, 0xbad, error);
np->n_error = error;
np->n_flag |= NWRITEERR;
NATTRINVALIDATE(np);
nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
error = 0;
}
}
}
tsize = np->n_size;
np->n_size = np->n_vattr.nva_size = vap->va_data_size;
ubc_setsize(vp, (off_t)vap->va_data_size);
}
} else if ((VATTR_IS_ACTIVE(vap, va_modify_time) ||
VATTR_IS_ACTIVE(vap, va_access_time)) &&
(np->n_flag & NMODIFIED) && (vnode_vtype(vp) == VREG)) {
error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
if (error == EINTR)
return (error);
}
if (VATTR_IS_ACTIVE(vap, va_mode)) {
NMODEINVALIDATE(np);
}
error = nfs_setattrrpc(vp, vap, cred, p);
FSDBG_BOT(512, np->n_size, vap->va_data_size, np->n_vattr.nva_size, error);
if (error && VATTR_IS_ACTIVE(vap, va_data_size)) {
int err;
np->n_size = np->n_vattr.nva_size = tsize;
ubc_setsize(vp, (off_t)np->n_size);
vap->va_data_size = tsize;
err = nfs_setattrrpc(vp, vap, cred, p);
printf("nfs_setattr: nfs_setattrrpc %d %d\n", error, err);
}
return (error);
}
static int
nfs_setattrrpc(vp, vap, cred, procp)
vnode_t vp;
struct vnode_attr *vap;
kauth_cred_t cred;
proc_t procp;
{
register struct nfsv2_sattr *sp;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
u_long *tl;
int error = 0, wccpostattr = 0;
mbuf_t mreq, mrep, md, mb, mb2;
int v3;
u_int64_t xid;
struct timeval now;
if (!VFSTONFS(vnode_mount(vp)))
return (ENXIO);
v3 = NFS_ISV3(vp);
nfsm_reqhead(NFSX_FH(v3) + NFSX_SATTR(v3));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SETATTR]);
nfsm_fhtom(vp, v3);
if (v3) {
if (VATTR_IS_ACTIVE(vap, va_mode)) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_mode);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (VATTR_IS_ACTIVE(vap, va_uid)) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_uid);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (VATTR_IS_ACTIVE(vap, va_gid)) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_gid);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (VATTR_IS_ACTIVE(vap, va_data_size)) {
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
*tl++ = nfs_true;
txdr_hyper(&vap->va_data_size, tl);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
microtime(&now);
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
if (vap->va_access_time.tv_sec != now.tv_sec) {
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_access_time, tl);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
if (vap->va_modify_time.tv_sec != now.tv_sec) {
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_modify_time, tl);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
struct timespec neg1time = { -1, -1 };
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
if (VATTR_IS_ACTIVE(vap, va_mode))
sp->sa_mode = vtonfsv2_mode(vnode_vtype(vp), vap->va_mode);
else
sp->sa_mode = nfs_xdrneg1;
if (VATTR_IS_ACTIVE(vap, va_uid))
sp->sa_uid = txdr_unsigned(vap->va_uid);
else
sp->sa_uid = nfs_xdrneg1;
if (VATTR_IS_ACTIVE(vap, va_gid))
sp->sa_gid = txdr_unsigned(vap->va_gid);
else
sp->sa_gid = nfs_xdrneg1;
if (VATTR_IS_ACTIVE(vap, va_data_size))
sp->sa_size = txdr_unsigned(vap->va_data_size);
else
sp->sa_size = nfs_xdrneg1;
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_atime);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_mtime);
}
}
nfsm_request(vp, NFSPROC_SETATTR, procp, cred, &xid);
if (v3) {
struct timespec premtime = { 0, 0 };
if (mrep) {
nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
}
if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==)) {
VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
}
if ((vnode_vtype(vp) == VDIR) &&
nfstimespeccmp(&VTONFS(vp)->n_ncmtime, &premtime, ==)) {
VTONFS(vp)->n_ncmtime = VTONFS(vp)->n_vattr.nva_mtime;
}
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(vp));
} else {
if (mrep) {
nfsm_loadattr(vp, v3, NULL, &xid);
}
}
nfsm_reqdone;
return (error);
}
static int
nfs_lookup(ap)
struct vnop_lookup_args *ap;
{
struct componentname *cnp = ap->a_cnp;
vnode_t dvp = ap->a_dvp;
vnode_t *vpp = ap->a_vpp;
int flags = cnp->cn_flags;
vnode_t newvp;
u_long *tl;
caddr_t cp;
long t1, t2;
caddr_t bpos, dpos, cp2;
mbuf_t mreq, mrep, md, mb, mb2;
long len;
u_char *fhp;
struct nfsnode *dnp, *np;
int wantparent, error, attrflag, dattrflag, fhsize, fhisdvp;
int v3 = NFS_ISV3(dvp);
u_int64_t xid, dxid;
struct nfs_vattr nvattr;
kauth_cred_t cred;
proc_t p;
int ngflags;
*vpp = NULLVP;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
wantparent = flags & (LOCKPARENT|WANTPARENT);
dnp = VTONFS(dvp);
error = nfs_getattr(dvp, &nvattr, cred, p);
if (error)
goto error_return;
if (nfstimespeccmp(&dnp->n_ncmtime, &nvattr.nva_mtime, !=)) {
cache_purge(dvp);
dnp->n_ncmtime = nvattr.nva_mtime;
}
error = cache_lookup(dvp, vpp, cnp);
switch (error) {
case ENOENT:
error = 0;
case 0:
break;
case -1:
{
struct vnop_access_args naa;
OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_hits);
naa.a_vp = dvp;
naa.a_action = KAUTH_VNODE_SEARCH;
naa.a_context = ap->a_context;
error = nfs_access(&naa);
}
default:
goto error_return;
}
if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
fhisdvp = 1;
fhp = NULL;
fhsize = 0;
mrep = NULL;
goto found;
}
if (v3) {
struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
if (!nmp) {
error = ENXIO;
goto error_return;
}
if (((nmp->nm_state & (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) ==
(NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) &&
(nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS) &&
(cnp->cn_namelen > (long)nmp->nm_fsinfo.namemax)) {
error = ENAMETOOLONG;
goto error_return;
}
} else if (cnp->cn_namelen > NFS_MAXNAMLEN) {
error = ENAMETOOLONG;
goto error_return;
}
error = 0;
newvp = NULLVP;
OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_misses);
len = cnp->cn_namelen;
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
if (error)
goto error_return;
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
nfsm_request(dvp, NFSPROC_LOOKUP, p, cred, &xid);
if (error) {
if (mrep) {
nfsm_postop_attr_update(dvp, v3, dattrflag, &xid);
mbuf_freem(mrep);
}
goto nfsmout;
}
nfsm_getfh(fhp, fhsize, v3);
fhisdvp = NFS_CMPFH(dnp, fhp, fhsize);
if (v3) {
dxid = xid;
nfsm_postop_attr_get(v3, attrflag, &nvattr);
nfsm_postop_attr_update(dvp, v3, dattrflag, &dxid);
if (!attrflag && (!fhisdvp || !dattrflag)) {
error = nfs_getattr_no_vnode(vnode_mount(dvp),
fhp, fhsize, cred, p, &nvattr, &xid);
if (error) {
mbuf_freem(mrep);
goto error_return;
}
}
} else {
nfsm_attr_get(v3, &nvattr);
}
found:
if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
if (fhisdvp) {
mbuf_freem(mrep);
error = EISDIR;
goto error_return;
}
error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
&nvattr, &xid, 0, &np);
if (error) {
mbuf_freem(mrep);
goto error_return;
}
*vpp = NFSTOV(np);
mbuf_freem(mrep);
goto error_return;
}
if ((cnp->cn_flags & MAKEENTRY) &&
(cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))
ngflags = NG_MAKEENTRY;
else
ngflags = 0;
if (fhisdvp) {
error = vnode_get(dvp);
if (error) {
mbuf_freem(mrep);
goto error_return;
}
newvp = dvp;
if (fhp && (dnp->n_xid <= xid)) {
error = nfs_loadattrcache(dnp, &nvattr, &xid, 0);
if (error) {
vnode_put(dvp);
mbuf_freem(mrep);
goto error_return;
}
}
} else {
error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
&nvattr, &xid, ngflags, &np);
if (error) {
mbuf_freem(mrep);
goto error_return;
}
newvp = NFSTOV(np);
}
*vpp = newvp;
nfsm_reqdone;
if (error) {
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
(flags & ISLASTCN) && error == ENOENT) {
if (vnode_mount(dvp) && vnode_vfsisrdonly(dvp))
error = EROFS;
else
error = EJUSTRETURN;
}
}
error_return:
if (error && *vpp) {
vnode_put(*vpp);
*vpp = NULLVP;
}
return (error);
}
static int
nfs_read(ap)
struct vnop_read_args *ap;
{
if (vnode_vtype(ap->a_vp) != VREG)
return (EPERM);
return (nfs_bioread(ap->a_vp, ap->a_uio, ap->a_ioflag,
vfs_context_ucred(ap->a_context),
vfs_context_proc(ap->a_context)));
}
static int
nfs_readlink(ap)
struct vnop_readlink_args *ap;
{
if (vnode_vtype(ap->a_vp) != VLNK)
return (EPERM);
return (nfs_bioread(ap->a_vp, ap->a_uio, 0,
vfs_context_ucred(ap->a_context),
vfs_context_proc(ap->a_context)));
}
int
nfs_readlinkrpc(
vnode_t vp,
struct uio *uiop,
kauth_cred_t cred,
proc_t p)
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, len, attrflag;
mbuf_t mreq, mrep, md, mb, mb2;
int v3;
u_int64_t xid;
if (!VFSTONFS(vnode_mount(vp)))
return (ENXIO);
v3 = NFS_ISV3(vp);
nfsm_reqhead(NFSX_FH(v3));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READLINK]);
nfsm_fhtom(vp, v3);
nfsm_request(vp, NFSPROC_READLINK, p, cred, &xid);
if (v3 && mrep)
nfsm_postop_attr_update(vp, v3, attrflag, &xid);
if (!error) {
nfsm_strsiz(len, NFS_MAXPATHLEN, v3);
if (len >= NFS_MAXPATHLEN) {
struct nfsnode *np = VTONFS(vp);
#if DIAGNOSTIC
if (!np)
panic("nfs_readlinkrpc: null np");
#endif
if (np->n_size && np->n_size < NFS_MAXPATHLEN)
len = np->n_size;
}
nfsm_mtouio(uiop, len);
}
nfsm_reqdone;
return (error);
}
int
nfs_readrpc(
vnode_t vp,
struct uio *uiop,
kauth_cred_t cred,
proc_t p)
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
mbuf_t mreq, mrep, md, mb, mb2;
struct nfsmount *nmp;
int error = 0, len, retlen, tsiz, eof = 0, attrflag;
int v3, nmrsize;
u_int64_t xid;
FSDBG_TOP(536, vp, uiop->uio_offset, uio_uio_resid(uiop), 0);
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
v3 = NFS_ISV3(vp);
nmrsize = nmp->nm_rsize;
tsiz = uio_uio_resid(uiop);
if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
FSDBG_BOT(536, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
return (EFBIG);
}
while (tsiz > 0) {
len = (tsiz > nmrsize) ? nmrsize : tsiz;
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED * 3);
if (error)
break;
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READ]);
nfsm_fhtom(vp, v3);
nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
if (v3) {
txdr_hyper(&uiop->uio_offset, tl);
*(tl + 2) = txdr_unsigned(len);
} else {
*tl++ = txdr_unsigned(uiop->uio_offset);
*tl++ = txdr_unsigned(len);
*tl = 0;
}
FSDBG(536, vp, uiop->uio_offset, len, 0);
nfsm_request(vp, NFSPROC_READ, p, cred, &xid);
if (v3) {
if (mrep) {
nfsm_postop_attr_update(vp, v3, attrflag, &xid);
}
if (error) {
mbuf_freem(mrep);
goto nfsmout;
}
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
eof = fxdr_unsigned(int, *(tl + 1));
} else {
if (mrep) {
nfsm_loadattr(vp, v3, NULL, &xid);
}
}
if (mrep) {
nfsm_strsiz(retlen, nmrsize, 0);
nfsm_mtouio(uiop, retlen);
mbuf_freem(mrep);
} else {
retlen = 0;
}
tsiz -= retlen;
if (v3) {
if (eof || retlen == 0)
tsiz = 0;
} else if (retlen < len)
tsiz = 0;
}
nfsmout:
FSDBG_BOT(536, vp, eof, uio_uio_resid(uiop), error);
return (error);
}
int
nfs_writerpc(
vnode_t vp,
struct uio *uiop,
kauth_cred_t cred,
proc_t p,
int *iomode,
int *must_commit)
{
register u_long *tl;
register caddr_t cp;
register int t1, t2, backup;
caddr_t bpos, dpos, cp2;
mbuf_t mreq, mrep, md, mb, mb2;
struct nfsmount *nmp;
int error = 0, len, tsiz, updatemtime = 0, wccpostattr = 0, rlen, commit;
int v3, committed = NFSV3WRITE_FILESYNC;
u_int64_t xid;
mount_t mp;
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1)
panic("nfs_writerpc: iovcnt > 1");
#endif
FSDBG_TOP(537, vp, uiop->uio_offset, uio_uio_resid(uiop), *iomode);
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
v3 = NFS_ISV3(vp);
*must_commit = 0;
tsiz = uio_uio_resid(uiop);
if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
FSDBG_BOT(537, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
return (EFBIG);
}
while (tsiz > 0) {
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp) {
error = ENXIO;
break;
}
len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
nfsm_reqhead(NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
if (error)
break;
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_WRITE]);
nfsm_fhtom(vp, v3);
if (v3) {
nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
txdr_hyper(&uiop->uio_offset, tl);
tl += 2;
*tl++ = txdr_unsigned(len);
*tl++ = txdr_unsigned(*iomode);
} else {
nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
*++tl = txdr_unsigned(uiop->uio_offset);
tl += 2;
}
*tl = txdr_unsigned(len);
FSDBG(537, vp, uiop->uio_offset, len, 0);
nfsm_uiotom(uiop, len);
nfsm_request(vp, NFSPROC_WRITE, p, cred, &xid);
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
error = ENXIO;
if (v3) {
if (mrep) {
struct timespec premtime;
nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==))
updatemtime = 1;
}
if (!error) {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
NFSX_V3WRITEVERF);
rlen = fxdr_unsigned(int, *tl++);
if (rlen <= 0) {
error = NFSERR_IO;
break;
} else if (rlen < len) {
backup = len - rlen;
uio_iov_base_add(uiop, -backup);
uio_iov_len_add(uiop, backup);
uiop->uio_offset -= backup;
uio_uio_resid_add(uiop, backup);
len = rlen;
}
commit = fxdr_unsigned(int, *tl++);
if (committed == NFSV3WRITE_FILESYNC)
committed = commit;
else if (committed == NFSV3WRITE_DATASYNC &&
commit == NFSV3WRITE_UNSTABLE)
committed = commit;
if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) {
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
nmp->nm_state |= NFSSTA_HASWRITEVERF;
} else if (bcmp((caddr_t)tl,
(caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) {
*must_commit = 1;
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
}
}
} else {
if (mrep) {
nfsm_loadattr(vp, v3, NULL, &xid);
}
}
if (updatemtime)
VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
mbuf_freem(mrep);
if (error)
break;
tsiz -= len;
}
nfsmout:
if ((mp = vnode_mount(vp)) && (vfs_flags(mp) & MNT_ASYNC))
committed = NFSV3WRITE_FILESYNC;
*iomode = committed;
if (error)
uio_uio_resid_set(uiop, tsiz);
FSDBG_BOT(537, vp, committed, uio_uio_resid(uiop), error);
return (error);
}
static int
nfs_mknodrpc(
vnode_t dvp,
vnode_t *vpp,
struct componentname *cnp,
struct vnode_attr *vap,
kauth_cred_t cred,
proc_t p)
{
register struct nfsv2_sattr *sp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
vnode_t newvp = (vnode_t)0;
struct nfsnode *np = (struct nfsnode *)0;
struct nfs_vattr nvattr;
char *cp2;
caddr_t bpos, dpos;
int error = 0, wccpostattr = 0, gotvp = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
u_long rdev;
u_int64_t xid;
int v3 = NFS_ISV3(dvp);
int gotuid, gotgid;
if (!VATTR_IS_ACTIVE(vap, va_type))
return (EINVAL);
if (vap->va_type == VCHR || vap->va_type == VBLK) {
if (!VATTR_IS_ACTIVE(vap, va_rdev))
return (EINVAL);
rdev = txdr_unsigned(vap->va_rdev);
} else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
rdev = 0xffffffff;
else {
return (ENOTSUP);
}
nfsm_reqhead(NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
if (error)
return (error);
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_SET_SUPPORTED(vap, va_data_size);
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
gotuid = VATTR_IS_ACTIVE(vap, va_uid);
gotgid = VATTR_IS_ACTIVE(vap, va_gid);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKNOD]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
if (v3) {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl++ = vtonfsv3_type(vap->va_type);
nfsm_v3sattr(vap);
if (vap->va_type == VCHR || vap->va_type == VBLK) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(major(vap->va_rdev));
*tl = txdr_unsigned(minor(vap->va_rdev));
}
} else {
struct timespec neg1time = { -1, -1 };
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(vap->va_type,
(VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
sp->sa_size = rdev;
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_atime);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_mtime);
}
}
nfsm_request(dvp, NFSPROC_MKNOD, p, cred, &xid);
if (!error) {
nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
if (!gotvp) {
error = nfs_lookitup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cred, p, &np);
if (!error)
newvp = NFSTOV(np);
}
}
if (v3 && mrep)
nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
if (!error && (gotuid || gotgid) &&
(!newvp || nfs_getattrcache(newvp, &nvattr) ||
(gotuid && (nvattr.nva_uid != vap->va_uid)) ||
(gotgid && (nvattr.nva_gid != vap->va_gid)))) {
VATTR_CLEAR_SUPPORTED(vap, va_uid);
VATTR_CLEAR_SUPPORTED(vap, va_gid);
}
nfsm_reqdone;
if (error) {
if (newvp)
vnode_put(newvp);
} else {
*vpp = newvp;
}
VTONFS(dvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(dvp));
return (error);
}
static int
nfs_mknod(ap)
struct vnop_mknod_args *ap;
{
int error;
error = nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap,
vfs_context_ucred(ap->a_context),
vfs_context_proc(ap->a_context));
return (error);
}
static u_long create_verf;
static int
nfs_create(ap)
struct vnop_create_args *ap;
{
vnode_t dvp = ap->a_dvp;
struct vnode_attr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct nfs_vattr nvattr;
struct nfsv2_sattr *sp;
u_long *tl;
caddr_t cp;
long t1, t2;
struct nfsnode *np = (struct nfsnode *)0;
vnode_t newvp = (vnode_t)0;
caddr_t bpos, dpos, cp2;
int error = 0, wccpostattr = 0, gotvp = 0, fmode = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
int v3 = NFS_ISV3(dvp);
int gotuid, gotgid;
u_int64_t xid;
kauth_cred_t cred;
proc_t p;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
if (!VATTR_IS_ACTIVE(vap, va_type))
return (EINVAL);
if (vap->va_type == VSOCK)
return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap, cred, p));
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_SET_SUPPORTED(vap, va_data_size);
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
gotuid = VATTR_IS_ACTIVE(vap, va_uid);
gotgid = VATTR_IS_ACTIVE(vap, va_gid);
if (vap->va_vaflags & VA_EXCLUSIVE)
fmode |= O_EXCL;
again:
nfsm_reqhead(NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_CREATE]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
if (v3) {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
if (fmode & O_EXCL) {
*tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
nfsm_build(tl, u_long *, NFSX_V3CREATEVERF);
if (!TAILQ_EMPTY(&in_ifaddrhead))
*tl++ = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr.s_addr;
else
*tl++ = create_verf;
*tl = ++create_verf;
} else {
*tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
nfsm_v3sattr(vap);
}
} else {
struct timespec neg1time = { -1, -1 };
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(vap->va_type,
(VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
sp->sa_size = 0;
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_atime);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_mtime);
}
}
nfsm_request(dvp, NFSPROC_CREATE, p, cred, &xid);
if (!error) {
nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
if (!gotvp) {
error = nfs_lookitup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cred, p, &np);
if (!error)
newvp = NFSTOV(np);
}
}
if (v3 && mrep)
nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
nfsm_reqdone;
if (error) {
if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
fmode &= ~O_EXCL;
goto again;
}
if (newvp)
vnode_put(newvp);
} else if (v3 && (fmode & O_EXCL)) {
error = nfs_setattrrpc(newvp, vap, cred, p);
if (error && (gotuid || gotgid)) {
VATTR_CLEAR_ACTIVE(vap, va_uid);
VATTR_CLEAR_ACTIVE(vap, va_gid);
error = nfs_setattrrpc(newvp, vap, cred, p);
}
if (error)
vnode_put(newvp);
}
if (!error) {
*ap->a_vpp = newvp;
}
VTONFS(dvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(dvp));
if (!error && (gotuid || gotgid) &&
(!newvp || nfs_getattrcache(newvp, &nvattr) ||
(gotuid && (nvattr.nva_uid != vap->va_uid)) ||
(gotgid && (nvattr.nva_gid != vap->va_gid)))) {
VATTR_CLEAR_SUPPORTED(vap, va_uid);
VATTR_CLEAR_SUPPORTED(vap, va_gid);
}
return (error);
}
static int
nfs_remove(ap)
struct vnop_remove_args *ap;
{
vnode_t vp = ap->a_vp;
vnode_t dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct nfsnode *np = VTONFS(vp);
int error = 0, gofree = 0;
struct nfs_vattr nvattr;
kauth_cred_t cred;
proc_t p;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
gofree = vnode_isinuse(vp, 0) ? 0 : 1;
if ((ap->a_flags & VNODE_REMOVE_NODELETEBUSY) && !gofree) {
return (EBUSY);
}
if (gofree || (np->n_sillyrename &&
nfs_getattr(vp, &nvattr, cred, p) == 0 &&
nvattr.nva_nlink > 1)) {
cache_purge(vp);
error = nfs_vinvalbuf(vp, 0, cred, p, 1);
np->n_size = 0;
ubc_setsize(vp, (off_t)0);
if (error != EINTR)
error = nfs_removerpc(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cred, p);
if (error == ENOENT)
error = 0;
if (!error) {
lck_mtx_lock(nfs_node_hash_mutex);
LIST_REMOVE(np, n_hash);
np->n_flag &= ~NHASHED;
lck_mtx_unlock(nfs_node_hash_mutex);
}
if (!error && !np->n_sillyrename) {
np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
vnode_recycle(vp);
}
} else if (!np->n_sillyrename) {
error = nfs_sillyrename(dvp, vp, cnp, cred, p);
}
NATTRINVALIDATE(np);
return (error);
}
int
nfs_removeit(struct sillyrename *sp)
{
return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, NULL));
}
static int
nfs_removerpc(dvp, name, namelen, cred, proc)
vnode_t dvp;
char *name;
int namelen;
kauth_cred_t cred;
proc_t proc;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, wccpostattr = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
int v3;
u_int64_t xid;
if (!VFSTONFS(vnode_mount(dvp)))
return (ENXIO);
v3 = NFS_ISV3(dvp);
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_REMOVE]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(name, namelen, NFS_MAXNAMLEN, v3);
nfsm_request(dvp, NFSPROC_REMOVE, proc, cred, &xid);
if (v3 && mrep)
nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
nfsm_reqdone;
VTONFS(dvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(dvp));
return (error);
}
static int
nfs_rename(ap)
struct vnop_rename_args *ap;
{
vnode_t fvp = ap->a_fvp;
vnode_t tvp = ap->a_tvp;
vnode_t fdvp = ap->a_fdvp;
vnode_t tdvp = ap->a_tdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
int error, inuse=0;
mount_t fmp, tdmp, tmp;
struct nfsnode *tnp;
kauth_cred_t cred;
proc_t p;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
tnp = tvp ? VTONFS(tvp) : NULL;
fmp = vnode_mount(fvp);
tmp = tvp ? vnode_mount(tvp) : NULL;
tdmp = vnode_mount(tdvp);
if ((fmp != tdmp) || (tvp && (fmp != tmp))) {
error = EXDEV;
goto out;
}
if (tvp && tvp != fvp) {
inuse = vnode_isinuse(tvp, 0);
}
if (inuse && !tnp->n_sillyrename && vnode_vtype(tvp) != VDIR) {
if ((error = nfs_sillyrename(tdvp, tvp, tcnp, cred, p))) {
goto out;
} else {
tvp = NULL;
}
}
error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, cred, p);
if (error == ENOENT)
error = 0;
if (!error && tvp && tvp != fvp && !tnp->n_sillyrename) {
lck_mtx_lock(nfs_node_hash_mutex);
LIST_REMOVE(tnp, n_hash);
tnp->n_flag &= ~NHASHED;
lck_mtx_unlock(nfs_node_hash_mutex);
}
cache_purge(fvp);
if (tvp) {
cache_purge(tvp);
if (!error && !tnp->n_sillyrename) {
tnp->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
vnode_recycle(tvp);
}
}
if (!error)
cache_enter(tdvp, fvp, tcnp);
out:
if (error == ENOENT)
error = 0;
return (error);
}
static int
nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
vnode_t fdvp;
char *fnameptr;
int fnamelen;
vnode_t tdvp;
char *tnameptr;
int tnamelen;
kauth_cred_t cred;
proc_t proc;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, fwccpostattr = 0, twccpostattr = 0;
struct timespec fpremtime = { 0, 0 }, tpremtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
int v3;
u_int64_t xid;
if (!VFSTONFS(vnode_mount(fdvp)))
return (ENXIO);
v3 = NFS_ISV3(fdvp);
nfsm_reqhead((NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
nfsm_rndup(tnamelen));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RENAME]);
nfsm_fhtom(fdvp, v3);
nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN, v3);
nfsm_fhtom(tdvp, v3);
nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN, v3);
nfsm_request(fdvp, NFSPROC_RENAME, proc, cred, &xid);
if (v3 && mrep) {
u_int64_t txid = xid;
nfsm_wcc_data(fdvp, &fpremtime, fwccpostattr, &xid);
nfsm_wcc_data(tdvp, &tpremtime, twccpostattr, &txid);
}
nfsm_reqdone;
VTONFS(fdvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(fdvp)->n_ncmtime, &fpremtime, ==))
VTONFS(fdvp)->n_ncmtime = VTONFS(fdvp)->n_vattr.nva_mtime;
if (!fwccpostattr)
NATTRINVALIDATE(VTONFS(fdvp));
VTONFS(tdvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &tpremtime, ==))
VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
if (!twccpostattr)
NATTRINVALIDATE(VTONFS(tdvp));
return (error);
}
static int
nfs_link(ap)
struct vnop_link_args *ap;
{
vnode_t vp = ap->a_vp;
vnode_t tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
u_long *tl;
caddr_t cp;
long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, wccpostattr = 0, attrflag = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
int v3;
u_int64_t xid;
kauth_cred_t cred;
proc_t p;
if (vnode_mount(vp) != vnode_mount(tdvp)) {
return (EXDEV);
}
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
v3 = NFS_ISV3(vp);
nfs_flush(vp, MNT_WAIT, cred, p, 0);
nfsm_reqhead(NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LINK]);
nfsm_fhtom(vp, v3);
nfsm_fhtom(tdvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
nfsm_request(vp, NFSPROC_LINK, p, cred, &xid);
if (v3 && mrep) {
u_int64_t txid = xid;
nfsm_postop_attr_update(vp, v3, attrflag, &xid);
nfsm_wcc_data(tdvp, &premtime, wccpostattr, &txid);
}
nfsm_reqdone;
VTONFS(tdvp)->n_flag |= NMODIFIED;
if (!attrflag)
NATTRINVALIDATE(VTONFS(vp));
if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &premtime, ==))
VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(tdvp));
if (error == EEXIST)
error = 0;
return (error);
}
static int
nfs_symlink(ap)
struct vnop_symlink_args *ap;
{
vnode_t dvp = ap->a_dvp;
struct vnode_attr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct nfs_vattr nvattr;
struct nfsv2_sattr *sp;
u_long *tl;
caddr_t cp;
long t1, t2;
caddr_t bpos, dpos, cp2;
int slen, error = 0, wccpostattr = 0, gotvp = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
vnode_t newvp = (vnode_t)0;
int v3 = NFS_ISV3(dvp);
int gotuid, gotgid;
u_int64_t xid;
kauth_cred_t cred;
proc_t p;
struct nfsnode *np = NULL;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
slen = strlen(ap->a_target);
nfsm_reqhead(NFSX_FH(v3) + 2*NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
if (error)
return (error);
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_SET_SUPPORTED(vap, va_data_size);
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
gotuid = VATTR_IS_ACTIVE(vap, va_uid);
gotgid = VATTR_IS_ACTIVE(vap, va_gid);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SYMLINK]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
if (v3) {
nfsm_v3sattr(vap);
}
nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN, v3);
if (!v3) {
struct timespec neg1time = { -1, -1 };
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(VLNK,
(VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
sp->sa_size = nfs_xdrneg1;
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_atime);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_mtime);
}
}
nfsm_request(dvp, NFSPROC_SYMLINK, p, cred, &xid);
if (v3 && mrep) {
u_int64_t dxid = xid;
if (!error)
nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
}
nfsm_reqdone;
VTONFS(dvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(dvp));
if ((error == EEXIST) || (!error && !gotvp)) {
if (newvp) {
vnode_put(newvp);
newvp = NULL;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cred, p, &np);
if (!error) {
newvp = NFSTOV(np);
if (vnode_vtype(newvp) != VLNK)
error = EEXIST;
}
}
if (!error && (gotuid || gotgid) &&
(!newvp || nfs_getattrcache(newvp, &nvattr) ||
(gotuid && (nvattr.nva_uid != vap->va_uid)) ||
(gotgid && (nvattr.nva_gid != vap->va_gid)))) {
VATTR_CLEAR_SUPPORTED(vap, va_uid);
VATTR_CLEAR_SUPPORTED(vap, va_gid);
}
if (error) {
if (newvp)
vnode_put(newvp);
} else {
*ap->a_vpp = newvp;
}
return (error);
}
static int
nfs_mkdir(ap)
struct vnop_mkdir_args *ap;
{
vnode_t dvp = ap->a_dvp;
struct vnode_attr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct nfs_vattr nvattr;
struct nfsv2_sattr *sp;
u_long *tl;
caddr_t cp;
long t1, t2;
int len;
struct nfsnode *np = (struct nfsnode *)0;
vnode_t newvp = (vnode_t)0;
caddr_t bpos, dpos, cp2;
int error = 0, wccpostattr = 0;
struct timespec premtime = { 0, 0 };
int gotvp = 0;
mbuf_t mreq, mrep, md, mb, mb2;
int v3 = NFS_ISV3(dvp);
int gotuid, gotgid;
u_int64_t xid, dxid;
kauth_cred_t cred;
proc_t p;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
len = cnp->cn_namelen;
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
if (error)
return (error);
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_SET_SUPPORTED(vap, va_data_size);
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
gotuid = VATTR_IS_ACTIVE(vap, va_uid);
gotgid = VATTR_IS_ACTIVE(vap, va_gid);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKDIR]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
if (v3) {
nfsm_v3sattr(vap);
} else {
struct timespec neg1time = { -1, -1 };
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(VDIR,
(VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
sp->sa_size = nfs_xdrneg1;
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_atime);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
} else {
txdr_nfsv2time(&neg1time, &sp->sa_mtime);
}
}
nfsm_request(dvp, NFSPROC_MKDIR, p, cred, &xid);
dxid = xid;
if (!error)
nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
if (v3 && mrep)
nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
nfsm_reqdone;
VTONFS(dvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(dvp));
if (error == EEXIST || (!error && !gotvp)) {
if (newvp) {
vnode_put(newvp);
newvp = NULL;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cred, p, &np);
if (!error) {
newvp = NFSTOV(np);
if (vnode_vtype(newvp) != VDIR)
error = EEXIST;
}
}
if (!error && (gotuid || gotgid) &&
(!newvp || nfs_getattrcache(newvp, &nvattr) ||
(gotuid && (nvattr.nva_uid != vap->va_uid)) ||
(gotgid && (nvattr.nva_gid != vap->va_gid)))) {
VATTR_CLEAR_SUPPORTED(vap, va_uid);
VATTR_CLEAR_SUPPORTED(vap, va_gid);
}
if (error) {
if (newvp)
vnode_put(newvp);
} else {
*ap->a_vpp = newvp;
}
return (error);
}
static int
nfs_rmdir(ap)
struct vnop_rmdir_args *ap;
{
vnode_t vp = ap->a_vp;
vnode_t dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
u_long *tl;
caddr_t cp;
long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, wccpostattr = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
int v3 = NFS_ISV3(dvp);
u_int64_t xid;
kauth_cred_t cred;
proc_t p;
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RMDIR]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
nfsm_request(dvp, NFSPROC_RMDIR, p, cred, &xid);
if (v3 && mrep)
nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
nfsm_reqdone;
VTONFS(dvp)->n_flag |= NMODIFIED;
if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
if (!wccpostattr)
NATTRINVALIDATE(VTONFS(dvp));
cache_purge(vp);
if (error == ENOENT)
error = 0;
if (!error) {
lck_mtx_lock(nfs_node_hash_mutex);
LIST_REMOVE(VTONFS(vp), n_hash);
VTONFS(vp)->n_flag &= ~NHASHED;
lck_mtx_unlock(nfs_node_hash_mutex);
}
return (error);
}
static int
nfs_readdir(ap)
struct vnop_readdir_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct uio *uio = ap->a_uio;
int tresid, error;
struct nfs_vattr nvattr;
kauth_cred_t cred;
proc_t p;
if (vnode_vtype(vp) != VDIR)
return (EPERM);
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
(np->n_flag & NMODIFIED) == 0) {
if (!nfs_getattr(vp, &nvattr, cred, p)) {
if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, ==)) {
OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_hits);
return (0);
}
if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=)) {
cache_purge(vp);
}
}
}
tresid = uio_uio_resid(uio);
error = nfs_bioread(vp, uio, 0, cred, p);
if (!error && uio_uio_resid(uio) == tresid)
OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_misses);
return (error);
}
int
nfs_readdirrpc(
vnode_t vp,
struct uio *uiop,
kauth_cred_t cred,
proc_t p)
{
register int len, skiplen, left;
register struct dirent *dp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
register nfsuint64 *cookiep;
caddr_t bpos, dpos, cp2;
mbuf_t mreq, mrep, md, mb, mb2;
nfsuint64 cookie;
struct nfsmount *nmp;
struct nfsnode *dnp = VTONFS(vp);
u_quad_t fileno;
int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
int attrflag;
int v3, nmreaddirsize;
u_int64_t xid;
#ifndef nolint
dp = (struct dirent *)0;
#endif
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) ||
(uio_uio_resid(uiop) & (NFS_DIRBLKSIZ - 1)))
panic("nfs_readdirrpc: bad uio");
#endif
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
v3 = NFS_ISV3(vp);
nmreaddirsize = nmp->nm_readdirsize;
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep)
cookie = *cookiep;
else
return (NFSERR_BAD_COOKIE);
while (more_dirs && bigenough) {
nfsm_reqhead(NFSX_FH(v3) + NFSX_READDIR(v3));
if (error)
goto nfsmout;
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIR]);
nfsm_fhtom(vp, v3);
if (v3) {
nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
*tl++ = cookie.nfsuquad[1];
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
} else {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
}
*tl = txdr_unsigned(nmreaddirsize);
nfsm_request(vp, NFSPROC_READDIR, p, cred, &xid);
if (v3) {
if (mrep) {
nfsm_postop_attr_update(vp, v3, attrflag, &xid);
}
if (!error) {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl;
} else {
mbuf_freem(mrep);
goto nfsmout;
}
} else if (!mrep) {
goto nfsmout;
}
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = fxdr_unsigned(int, *tl);
while (more_dirs && bigenough) {
if (v3) {
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
fxdr_hyper(tl, &fileno);
len = fxdr_unsigned(int, *(tl + 2));
} else {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
fileno = fxdr_unsigned(u_quad_t, *tl++);
len = fxdr_unsigned(int, *tl);
}
if (len <= 0) {
error = EBADRPC;
mbuf_freem(mrep);
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 + (int)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 + (int)DIRHDSIZ) > uio_uio_resid(uiop))
bigenough = 0;
if (bigenough) {
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_fileno = (int)fileno;
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);
nfsm_mtouio(uiop, len);
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);
} else {
nfsm_adv(nfsm_rndup(len));
}
if (skiplen)
nfsm_adv(nfsm_rndup(skiplen));
if (v3) {
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
} else {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
}
if (bigenough) {
cookie.nfsuquad[0] = *tl++;
if (v3)
cookie.nfsuquad[1] = *tl++;
} else if (v3)
tl += 2;
else
tl++;
more_dirs = fxdr_unsigned(int, *tl);
}
if (!more_dirs) {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = (fxdr_unsigned(int, *tl) == 0);
}
mbuf_freem(mrep);
}
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 (bigenough)
dnp->n_direofoffset = uiop->uio_offset;
else {
if (uio_uio_resid(uiop) > 0)
printf("EEK! readdirrpc resid > 0\n");
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
if (cookiep)
*cookiep = cookie;
}
nfsmout:
return (error);
}
int
nfs_readdirplusrpc(
vnode_t vp,
struct uio *uiop,
kauth_cred_t cred,
proc_t p)
{
int len, skiplen, left;
struct dirent *dp;
u_long *tl;
caddr_t cp;
long t1, t2;
vnode_t newvp;
nfsuint64 *cookiep;
caddr_t bpos, dpos, cp2;
mbuf_t mreq, mrep, md, mb, mb2;
struct componentname cn, *cnp = &cn;
nfsuint64 cookie;
struct nfsmount *nmp;
struct nfsnode *dnp = VTONFS(vp), *np;
u_char *fhp;
u_quad_t fileno;
int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
int attrflag, fhsize, nmreaddirsize, nmrsize;
u_int64_t xid, savexid;
struct nfs_vattr nvattr;
#ifndef nolint
dp = (struct dirent *)0;
#endif
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
(uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
panic("nfs_readdirplusrpc: bad uio");
#endif
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
nmreaddirsize = nmp->nm_readdirsize;
nmrsize = nmp->nm_rsize;
bzero(cnp, sizeof(*cnp));
newvp = NULLVP;
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep)
cookie = *cookiep;
else
return (NFSERR_BAD_COOKIE);
while (more_dirs && bigenough) {
nfsm_reqhead(NFSX_FH(1) + 6 * NFSX_UNSIGNED);
if (error)
goto nfsmout;
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIRPLUS]);
nfsm_fhtom(vp, 1);
nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
*tl++ = cookie.nfsuquad[1];
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
*tl++ = txdr_unsigned(nmreaddirsize);
*tl = txdr_unsigned(nmrsize);
nfsm_request(vp, NFSPROC_READDIRPLUS, p, cred, &xid);
savexid = xid;
if (mrep) {
nfsm_postop_attr_update(vp, 1, attrflag, &xid);
}
if (error) {
mbuf_freem(mrep);
goto nfsmout;
}
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl++;
more_dirs = fxdr_unsigned(int, *tl);
while (more_dirs && bigenough) {
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
fxdr_hyper(tl, &fileno);
len = fxdr_unsigned(int, *(tl + 2));
if (len <= 0) {
error = EBADRPC;
mbuf_freem(mrep);
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 + (int)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 + (int)DIRHDSIZ) > uio_uio_resid(uiop))
bigenough = 0;
if (bigenough) {
dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
dp->d_fileno = (int)fileno;
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;
nfsm_mtouio(uiop, len);
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);
} else {
nfsm_adv(nfsm_rndup(len));
}
if (skiplen)
nfsm_adv(nfsm_rndup(skiplen));
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
if (bigenough) {
cookie.nfsuquad[0] = *tl++;
cookie.nfsuquad[1] = *tl++;
} else
tl += 2;
attrflag = fxdr_unsigned(int, *tl);
if (attrflag) {
nfsm_attr_get(1, &nvattr);
dp->d_type = IFTODT(VTTOIF(nvattr.nva_type));
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
doit = fxdr_unsigned(int, *tl);
if (doit) {
nfsm_getfh(fhp, fhsize, 1);
if (NFS_CMPFH(dnp, fhp, fhsize)) {
error = vnode_ref(vp);
if (error) {
doit = 0;
} else {
newvp = vp;
np = dnp;
}
} else if (!bigenough ||
(cnp->cn_namelen == 2 &&
cnp->cn_nameptr[1] == '.' &&
cnp->cn_nameptr[0] == '.')) {
doit = 0;
} else {
cnp->cn_hash = 0;
error = nfs_nget(vnode_mount(vp), vp, cnp,
fhp, fhsize, &nvattr, &xid,
NG_MAKEENTRY, &np);
if (error)
doit = 0;
else
newvp = NFSTOV(np);
}
}
if (doit && bigenough && (np->n_xid <= savexid)) {
xid = savexid;
nfs_loadattrcache(np, &nvattr, &xid, 0);
}
} else {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
i = fxdr_unsigned(int, *tl);
nfsm_adv(nfsm_rndup(i));
}
if (newvp != NULLVP) {
if (newvp == vp)
vnode_rele(newvp);
else
vnode_put(newvp);
newvp = NULLVP;
}
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = fxdr_unsigned(int, *tl);
}
if (!more_dirs) {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = (fxdr_unsigned(int, *tl) == 0);
}
mbuf_freem(mrep);
}
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 (bigenough)
dnp->n_direofoffset = uiop->uio_offset;
else {
if (uio_uio_resid(uiop) > 0)
printf("EEK! readdirplusrpc resid > 0\n");
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
if (cookiep)
*cookiep = cookie;
}
nfsmout:
return (error);
}
static char sillyrename_name[] = ".nfsAAA%04x4.4";
static int
nfs_sillyrename(
vnode_t dvp,
vnode_t vp,
struct componentname *cnp,
kauth_cred_t cred,
proc_t p)
{
register struct sillyrename *sp;
struct nfsnode *np;
int error;
short pid;
kauth_cred_t tmpcred;
int i, j, k;
cache_purge(vp);
np = VTONFS(vp);
#if DIAGNOSTIC
if (vnode_vtype(vp) == VDIR)
panic("nfs_sillyrename: dir");
#endif
MALLOC_ZONE(sp, struct sillyrename *,
sizeof (struct sillyrename), M_NFSREQ, M_WAITOK);
if (!sp)
return (ENOMEM);
kauth_cred_ref(cred);
sp->s_cred = cred;
sp->s_dvp = dvp;
error = vnode_ref(dvp);
if (error)
goto bad_norele;
pid = proc_pid(p);
sp->s_namlen = sprintf(sp->s_name, sillyrename_name, pid);
i = j = k = 0;
while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, NULL) == 0) {
if (sp->s_name[4]++ >= 'z')
sp->s_name[4] = 'A';
if (++i > ('z' - 'A' + 1)) {
i = 0;
if (sp->s_name[5]++ >= 'z')
sp->s_name[5] = 'A';
if (++j > ('z' - 'A' + 1)) {
j = 0;
if (sp->s_name[6]++ >= 'z')
sp->s_name[6] = 'A';
if (++k > ('z' - 'A' + 1)) {
error = EINVAL;
goto bad;
}
}
}
}
if ((sillyrename_name[4] = (sp->s_name[4] + 1)) > 'z') {
sillyrename_name[4] = 'A';
if ((sillyrename_name[5] = (sp->s_name[5] + 1)) > 'z') {
sillyrename_name[5] = 'A';
if ((sillyrename_name[6] = (sp->s_name[6] + 1)) > 'z')
sillyrename_name[6] = 'A';
}
}
error = nfs_renamerpc(dvp, cnp->cn_nameptr, cnp->cn_namelen,
dvp, sp->s_name, sp->s_namlen, sp->s_cred, p);
if (error)
goto bad;
error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, &np);
#if DIAGNOSTIC
kprintf("sillyrename: %s, vp=%x, np=%x, dvp=%x\n",
&sp->s_name[0], (unsigned)vp, (unsigned)np, (unsigned)dvp);
#endif
np->n_sillyrename = sp;
return (0);
bad:
vnode_rele(sp->s_dvp);
bad_norele:
tmpcred = sp->s_cred;
sp->s_cred = NOCRED;
kauth_cred_rele(tmpcred);
FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
return (error);
}
static int
nfs_lookitup(dvp, name, len, cred, procp, npp)
vnode_t dvp;
char *name;
int len;
kauth_cred_t cred;
proc_t procp;
struct nfsnode **npp;
{
u_long *tl;
caddr_t cp;
long t1, t2;
vnode_t newvp = (vnode_t)0;
struct nfsnode *np, *dnp = VTONFS(dvp);
caddr_t bpos, dpos, cp2;
int error = 0, fhlen, attrflag;
mbuf_t mreq, mrep, md, mb, mb2;
u_char *nfhp;
int v3;
u_int64_t xid, dxid, savedxid;
struct nfs_vattr nvattr;
if (!VFSTONFS(vnode_mount(dvp)))
return (ENXIO);
v3 = NFS_ISV3(dvp);
nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
nfsm_fhtom(dvp, v3);
nfsm_strtom(name, len, NFS_MAXNAMLEN, v3);
nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred, &xid);
if (npp && !error) {
savedxid = xid;
nfsm_getfh(nfhp, fhlen, v3);
if (v3) {
nfsm_postop_attr_get(v3, attrflag, &nvattr);
if (!attrflag) {
error = nfs_getattr_no_vnode(vnode_mount(dvp),
nfhp, fhlen, cred, procp, &nvattr, &xid);
if (error) {
mbuf_freem(mrep);
goto nfsmout;
}
}
dxid = savedxid;
nfsm_postop_attr_update(dvp, v3, attrflag, &dxid);
} else {
nfsm_attr_get(v3, &nvattr);
}
if (*npp) {
np = *npp;
if (fhlen != np->n_fhsize) {
u_char *oldbuf = (np->n_fhsize > NFS_SMALLFH) ? np->n_fhp : NULL;
if (fhlen > NFS_SMALLFH) {
MALLOC_ZONE(np->n_fhp, u_char *, fhlen, M_NFSBIGFH, M_WAITOK);
if (!np->n_fhp) {
np->n_fhp = oldbuf;
error = ENOMEM;
mbuf_freem(mrep);
goto nfsmout;
}
} else {
np->n_fhp = &np->n_fh[0];
}
if (oldbuf) {
FREE_ZONE(oldbuf, np->n_fhsize, M_NFSBIGFH);
}
}
bcopy(nfhp, np->n_fhp, fhlen);
np->n_fhsize = fhlen;
newvp = NFSTOV(np);
error = nfs_loadattrcache(np, &nvattr, &xid, 0);
if (error) {
mbuf_freem(mrep);
goto nfsmout;
}
} else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
newvp = dvp;
if (dnp->n_xid <= savedxid) {
dxid = savedxid;
error = nfs_loadattrcache(dnp, &nvattr, &dxid, 0);
if (error) {
mbuf_freem(mrep);
goto nfsmout;
}
}
} else {
struct componentname cn, *cnp = &cn;
bzero(cnp, sizeof(*cnp));
cnp->cn_nameptr = name;
cnp->cn_namelen = len;
error = nfs_nget(vnode_mount(dvp), dvp, cnp, nfhp, fhlen,
&nvattr, &xid, NG_MAKEENTRY, &np);
if (error) {
mbuf_freem(mrep);
return (error);
}
newvp = NFSTOV(np);
}
}
nfsm_reqdone;
if (npp && *npp == NULL) {
if (error) {
if (newvp) {
if (newvp == dvp)
vnode_rele(newvp);
else
vnode_put(newvp);
}
} else
*npp = np;
}
return (error);
}
int
nfs_commit(vp, offset, count, cred, procp)
vnode_t vp;
u_quad_t offset;
u_int32_t count;
kauth_cred_t cred;
proc_t procp;
{
caddr_t cp;
u_long *tl;
int t1, t2;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
caddr_t bpos, dpos, cp2;
int error = 0, wccpostattr = 0;
struct timespec premtime = { 0, 0 };
mbuf_t mreq, mrep, md, mb, mb2;
u_int64_t xid;
FSDBG(521, vp, offset, count, nmp->nm_state);
if (!nmp)
return (ENXIO);
if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0)
return (0);
nfsm_reqhead(NFSX_FH(1));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_COMMIT]);
nfsm_fhtom(vp, 1);
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
txdr_hyper(&offset, tl);
tl += 2;
*tl = txdr_unsigned(count);
nfsm_request(vp, NFSPROC_COMMIT, procp, cred, &xid);
if (mrep) {
nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
}
if (!error) {
nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF);
if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl,
NFSX_V3WRITEVERF)) {
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
error = NFSERR_STALEWRITEVERF;
}
}
nfsm_reqdone;
return (error);
}
static int
nfs_blockmap(
__unused struct vnop_blockmap_args *ap)
{
return (ENOTSUP);
}
static int
nfs_mmap(
__unused struct vnop_mmap_args *ap)
{
return (EINVAL);
}
static int
nfs_fsync(ap)
struct vnop_fsync_args *ap;
{
kauth_cred_t cred = vfs_context_ucred(ap->a_context);
proc_t p = vfs_context_proc(ap->a_context);
struct nfsnode *np = VTONFS(ap->a_vp);
int error;
np->n_flag |= NWRBUSY;
error = nfs_flush(ap->a_vp, ap->a_waitfor, cred, p, 0);
np->n_flag &= ~NWRBUSY;
return (error);
}
int
nfs_flushcommits(vnode_t vp, proc_t p, int nowait)
{
struct nfsnode *np = VTONFS(vp);
struct nfsbuf *bp;
struct nfsbuflists blist, commitlist;
int error = 0, retv, wcred_set, flags;
u_quad_t off, endoff, toff;
u_int32_t count;
kauth_cred_t wcred = NULL;
FSDBG_TOP(557, vp, np, 0, 0);
if (!LIST_EMPTY(&np->n_dirtyblkhd))
np->n_flag |= NMODIFIED;
off = (u_quad_t)-1;
endoff = 0;
wcred_set = 0;
LIST_INIT(&commitlist);
if (!VFSTONFS(vnode_mount(vp))) {
error = ENXIO;
goto done;
}
if (!NFS_ISV3(vp)) {
error = EINVAL;
goto done;
}
flags = NBI_DIRTY;
if (nowait)
flags |= NBI_NOWAIT;
lck_mtx_lock(nfs_buf_mutex);
if (!nfs_buf_iterprepare(np, &blist, flags)) {
while ((bp = LIST_FIRST(&blist))) {
LIST_REMOVE(bp, nb_vnbufs);
LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
error = nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0);
if (error)
continue;
if (((bp->nb_flags & (NB_DELWRI | NB_NEEDCOMMIT))
!= (NB_DELWRI | NB_NEEDCOMMIT))) {
nfs_buf_drop(bp);
continue;
}
nfs_buf_remfree(bp);
lck_mtx_unlock(nfs_buf_mutex);
if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
retv = nfs_buf_upl_setup(bp);
if (retv) {
printf("nfs_flushcommits: upl create failed %d\n", retv);
bp->nb_valid = bp->nb_dirty = 0;
}
}
nfs_buf_upl_check(bp);
lck_mtx_lock(nfs_buf_mutex);
FSDBG(557, bp, bp->nb_flags, bp->nb_valid, bp->nb_dirty);
FSDBG(557, bp->nb_validoff, bp->nb_validend,
bp->nb_dirtyoff, bp->nb_dirtyend);
if (wcred_set == 0) {
wcred = bp->nb_wcred;
if (wcred == NOCRED)
panic("nfs: needcommit w/out wcred");
wcred_set = 1;
} else if ((wcred_set == 1) && wcred != bp->nb_wcred) {
wcred_set = -1;
}
SET(bp->nb_flags, NB_WRITEINPROG);
LIST_REMOVE(bp, nb_vnbufs);
LIST_INSERT_HEAD(&commitlist, bp, nb_vnbufs);
toff = NBOFF(bp) + bp->nb_dirtyoff;
if (toff < off)
off = toff;
toff += (u_quad_t)(bp->nb_dirtyend - bp->nb_dirtyoff);
if (toff > endoff)
endoff = toff;
}
nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
}
lck_mtx_unlock(nfs_buf_mutex);
if (LIST_EMPTY(&commitlist)) {
error = ENOBUFS;
goto done;
}
if (wcred_set == 1) {
if ((endoff - off) > 0xffffffff)
count = 0;
else
count = (endoff - off);
retv = nfs_commit(vp, off, count, wcred, p);
} else {
retv = 0;
LIST_FOREACH(bp, &commitlist, nb_vnbufs) {
toff = NBOFF(bp) + bp->nb_dirtyoff;
count = bp->nb_dirtyend - bp->nb_dirtyoff;
retv = nfs_commit(vp, toff, count, bp->nb_wcred, p);
if (retv)
break;
}
}
if (retv == NFSERR_STALEWRITEVERF)
nfs_clearcommit(vnode_mount(vp));
while ((bp = LIST_FIRST(&commitlist))) {
LIST_REMOVE(bp, nb_vnbufs);
FSDBG(557, bp, retv, bp->nb_flags, bp->nb_dirty);
CLR(bp->nb_flags, (NB_NEEDCOMMIT | NB_WRITEINPROG));
np->n_needcommitcnt--;
CHECK_NEEDCOMMITCNT(np);
if (retv) {
lck_mtx_lock(nfs_buf_mutex);
LIST_INSERT_HEAD(&VTONFS(vp)->n_dirtyblkhd, bp, nb_vnbufs);
lck_mtx_unlock(nfs_buf_mutex);
nfs_buf_release(bp, 1);
continue;
}
vnode_startwrite(vp);
if (ISSET(bp->nb_flags, NB_DELWRI)) {
OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
NFSBUFCNTCHK(0);
wakeup(&nfs_nbdwrite);
}
CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
if (!bp->nb_dirty)
SET(bp->nb_flags, NB_ASYNC);
lck_mtx_lock(nfs_buf_mutex);
LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
lck_mtx_unlock(nfs_buf_mutex);
bp->nb_dirtyoff = bp->nb_dirtyend = 0;
nfs_buf_iodone(bp);
if (bp->nb_dirty) {
CLR(bp->nb_flags, NB_DONE);
nfs_buf_write_delayed(bp, p);
}
}
done:
FSDBG_BOT(557, vp, np, 0, error);
return (error);
}
int
nfs_flush(
vnode_t vp,
int waitfor,
__unused kauth_cred_t cred,
proc_t p,
int ignore_writeerr)
{
struct nfsnode *np = VTONFS(vp);
struct nfsbuf *bp;
struct nfsbuflists blist;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
int error = 0, error2, slptimeo = 0, slpflag = 0;
int flags, passone = 1;
FSDBG_TOP(517, vp, np, waitfor, 0);
if (!nmp) {
error = ENXIO;
goto done;
}
if (nmp->nm_flag & NFSMNT_INT)
slpflag = PCATCH;
again:
lck_mtx_lock(nfs_buf_mutex);
FSDBG(518, LIST_FIRST(&np->n_dirtyblkhd), np->n_flag, 0, 0);
if (!LIST_EMPTY(&np->n_dirtyblkhd))
np->n_flag |= NMODIFIED;
if (!VFSTONFS(vnode_mount(vp))) {
lck_mtx_unlock(nfs_buf_mutex);
error = ENXIO;
goto done;
}
if (!nfs_buf_iterprepare(np, &blist, NBI_DIRTY)) {
while ((bp = LIST_FIRST(&blist))) {
LIST_REMOVE(bp, nb_vnbufs);
LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
flags = (passone || (waitfor != MNT_WAIT)) ? NBAC_NOWAIT : 0;
if (flags != NBAC_NOWAIT)
nfs_buf_refget(bp);
while ((error = nfs_buf_acquire(bp, flags, slpflag, slptimeo))) {
FSDBG(524, bp, flags, bp->nb_lflags, bp->nb_flags);
if (error == EBUSY)
break;
if (error) {
error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
if (error2) {
if (flags != NBAC_NOWAIT)
nfs_buf_refrele(bp);
nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
lck_mtx_unlock(nfs_buf_mutex);
error = error2;
goto done;
}
if (slpflag == PCATCH) {
slpflag = 0;
slptimeo = 2 * hz;
}
}
}
if (flags != NBAC_NOWAIT)
nfs_buf_refrele(bp);
if (error == EBUSY)
continue;
if (!bp->nb_vp) {
nfs_buf_drop(bp);
continue;
}
if (!ISSET(bp->nb_flags, NB_DELWRI))
panic("nfs_flush: not dirty");
FSDBG(525, bp, passone, bp->nb_lflags, bp->nb_flags);
if ((passone || (waitfor != MNT_WAIT)) &&
ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
nfs_buf_drop(bp);
continue;
}
nfs_buf_remfree(bp);
lck_mtx_unlock(nfs_buf_mutex);
if (ISSET(bp->nb_flags, NB_ERROR)) {
np->n_error = bp->nb_error ? bp->nb_error : EIO;
np->n_flag |= NWRITEERR;
nfs_buf_release(bp, 1);
lck_mtx_lock(nfs_buf_mutex);
continue;
}
SET(bp->nb_flags, NB_ASYNC);
if (!passone) {
SET(bp->nb_flags, NB_STABLE);
}
nfs_buf_write(bp);
lck_mtx_lock(nfs_buf_mutex);
}
nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
}
lck_mtx_unlock(nfs_buf_mutex);
if (waitfor == MNT_WAIT) {
while ((error = vnode_waitforwrites(vp, 0, slpflag, slptimeo, "nfsflush"))) {
error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
if (error2) {
error = error2;
goto done;
}
if (slpflag == PCATCH) {
slpflag = 0;
slptimeo = 2 * hz;
}
}
}
if (NFS_ISV3(vp)) {
while (np->n_needcommitcnt)
if (nfs_flushcommits(vp, p, 0))
break;
}
if (passone) {
passone = 0;
goto again;
}
if ((waitfor == MNT_WAIT) && !LIST_EMPTY(&np->n_dirtyblkhd)) {
goto again;
}
if (LIST_EMPTY(&np->n_dirtyblkhd))
np->n_flag &= ~NMODIFIED;
FSDBG(526, np->n_flag, np->n_error, 0, 0);
if (!ignore_writeerr && (np->n_flag & NWRITEERR)) {
error = np->n_error;
np->n_flag &= ~NWRITEERR;
}
done:
FSDBG_BOT(517, vp, np, error, 0);
return (error);
}
int
nfs_pathconfrpc(
vnode_t vp,
struct nfsv3_pathconf *pc,
kauth_cred_t cred,
proc_t procp)
{
mbuf_t mreq, mrep, md, mb, mb2;
caddr_t bpos, dpos, cp, cp2;
int32_t t1, t2;
u_long *tl;
u_int64_t xid;
int attrflag, error = 0;
struct nfsv3_pathconf *mpc;
nfsm_reqhead(NFSX_FH(1));
if (error)
return (error);
nfsm_fhtom(vp, 1);
nfsm_request(vp, NFSPROC_PATHCONF, procp, cred, &xid);
nfsm_postop_attr_update(vp, 1, attrflag, &xid);
if (!error) {
nfsm_dissect(mpc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
pc->pc_linkmax = fxdr_unsigned(long, mpc->pc_linkmax);
pc->pc_namemax = fxdr_unsigned(long, mpc->pc_namemax);
pc->pc_chownrestricted = fxdr_unsigned(long, mpc->pc_chownrestricted);
pc->pc_notrunc = fxdr_unsigned(long, mpc->pc_notrunc);
pc->pc_caseinsensitive = fxdr_unsigned(long, mpc->pc_caseinsensitive);
pc->pc_casepreserving = fxdr_unsigned(long, mpc->pc_casepreserving);
}
nfsm_reqdone;
return (error);
}
void
nfs_pathconf_cache(struct nfsmount *nmp, struct nfsv3_pathconf *pc)
{
nmp->nm_state |= NFSSTA_GOTPATHCONF;
nmp->nm_fsinfo.linkmax = pc->pc_linkmax;
nmp->nm_fsinfo.namemax = pc->pc_namemax;
nmp->nm_fsinfo.pcflags = 0;
if (pc->pc_notrunc)
nmp->nm_fsinfo.pcflags |= NFSPCINFO_NOTRUNC;
if (pc->pc_chownrestricted)
nmp->nm_fsinfo.pcflags |= NFSPCINFO_CHOWN_RESTRICTED;
if (pc->pc_caseinsensitive)
nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_INSENSITIVE;
if (pc->pc_casepreserving)
nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_PRESERVING;
}
static int
nfs_pathconf(ap)
struct vnop_pathconf_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsmount *nmp;
struct nfsv3_pathconf pc;
int error = 0, cached;
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
if (!NFS_ISV3(vp))
return (EINVAL);
switch (ap->a_name) {
case _PC_LINK_MAX:
case _PC_NAME_MAX:
case _PC_CHOWN_RESTRICTED:
case _PC_NO_TRUNC:
case _PC_CASE_SENSITIVE:
case _PC_CASE_PRESERVING:
break;
default:
return (EINVAL);
}
if (!(nmp->nm_state & NFSSTA_GOTPATHCONF)) {
kauth_cred_t cred = vfs_context_ucred(ap->a_context);
proc_t p = vfs_context_proc(ap->a_context);
error = nfs_pathconfrpc(vp, &pc, cred, p);
if (error)
return (error);
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
if (!(nmp->nm_state & NFSSTA_GOTFSINFO)) {
nfs_fsinfo(nmp, vp, cred, p);
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp)
return (ENXIO);
}
if ((nmp->nm_state & NFSSTA_GOTFSINFO) &&
(nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS)) {
nfs_pathconf_cache(nmp, &pc);
}
}
cached = (nmp->nm_state & NFSSTA_GOTPATHCONF);
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = cached ? nmp->nm_fsinfo.linkmax : pc.pc_linkmax;
break;
case _PC_NAME_MAX:
*ap->a_retval = cached ? nmp->nm_fsinfo.namemax : pc.pc_namemax;
break;
case _PC_CHOWN_RESTRICTED:
if (cached)
*ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CHOWN_RESTRICTED) ? 1 : 0;
else
*ap->a_retval = pc.pc_chownrestricted;
break;
case _PC_NO_TRUNC:
if (cached)
*ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_NOTRUNC) ? 1 : 0;
else
*ap->a_retval = pc.pc_notrunc;
break;
case _PC_CASE_SENSITIVE:
if (cached)
*ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_INSENSITIVE) ? 0 : 1;
else
*ap->a_retval = !pc.pc_caseinsensitive;
break;
case _PC_CASE_PRESERVING:
if (cached)
*ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_PRESERVING) ? 1 : 0;
else
*ap->a_retval = pc.pc_casepreserving;
break;
default:
error = EINVAL;
}
return (error);
}
static int
nfs_advlock(ap)
struct vnop_advlock_args *ap;
{
return (nfs_dolock(ap));
}
int
nfs_buf_write(struct nfsbuf *bp)
{
int oldflags = bp->nb_flags, rv = 0;
vnode_t vp = bp->nb_vp;
struct nfsnode *np = VTONFS(vp);
kauth_cred_t cr;
proc_t p = current_proc();
FSDBG_TOP(553, bp, NBOFF(bp), bp->nb_flags, 0);
if (!ISSET(bp->nb_lflags, NBL_BUSY))
panic("nfs_buf_write: buffer is not busy???");
CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
if (ISSET(oldflags, NB_DELWRI)) {
OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
NFSBUFCNTCHK(0);
wakeup(&nfs_nbdwrite);
}
if (ISSET(oldflags, (NB_ASYNC|NB_DELWRI))) {
lck_mtx_lock(nfs_buf_mutex);
if (bp->nb_vnbufs.le_next != NFSNOLIST)
LIST_REMOVE(bp, nb_vnbufs);
LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
lck_mtx_unlock(nfs_buf_mutex);
}
vnode_startwrite(vp);
if (p && p->p_stats)
p->p_stats->p_ru.ru_oublock++;
if (ISSET(bp->nb_flags, NB_ASYNC))
p = NULL;
if (ISSET(bp->nb_flags, NB_READ))
cr = bp->nb_rcred;
else
cr = bp->nb_wcred;
if (!ISSET(bp->nb_flags, NB_ASYNC) || nfs_asyncio(bp, NOCRED))
rv = nfs_doio(bp, cr, p);
if ((oldflags & NB_ASYNC) == 0) {
rv = nfs_buf_iowait(bp);
if (oldflags & NB_DELWRI) {
lck_mtx_lock(nfs_buf_mutex);
if (bp->nb_vnbufs.le_next != NFSNOLIST)
LIST_REMOVE(bp, nb_vnbufs);
LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
lck_mtx_unlock(nfs_buf_mutex);
}
oldflags = bp->nb_flags;
FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
if (cr) {
kauth_cred_ref(cr);
}
nfs_buf_release(bp, 1);
if (ISSET(oldflags, NB_ERROR) && !(np->n_flag & NFLUSHINPROG)) {
nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cr, p, 1);
}
if (cr)
kauth_cred_rele(cr);
return (rv);
}
FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
return (rv);
}
static int
nfsspec_read(ap)
struct vnop_read_args *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
struct timeval now;
np->n_flag |= NACC;
microtime(&now);
np->n_atim.tv_sec = now.tv_sec;
np->n_atim.tv_nsec = now.tv_usec * 1000;
return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap));
}
static int
nfsspec_write(ap)
struct vnop_write_args *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
struct timeval now;
np->n_flag |= NUPD;
microtime(&now);
np->n_mtim.tv_sec = now.tv_sec;
np->n_mtim.tv_nsec = now.tv_usec * 1000;
return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap));
}
static int
nfsspec_close(ap)
struct vnop_close_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct vnode_attr vattr;
mount_t mp;
if (np->n_flag & (NACC | NUPD)) {
np->n_flag |= NCHG;
if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
VATTR_INIT(&vattr);
if (np->n_flag & NACC) {
vattr.va_access_time = np->n_atim;
VATTR_SET_ACTIVE(&vattr, va_access_time);
}
if (np->n_flag & NUPD) {
vattr.va_modify_time = np->n_mtim;
VATTR_SET_ACTIVE(&vattr, va_modify_time);
}
vnode_setattr(vp, &vattr, ap->a_context);
}
}
return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap));
}
extern vnop_t **fifo_vnodeop_p;
static int
nfsfifo_read(ap)
struct vnop_read_args *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
struct timeval now;
np->n_flag |= NACC;
microtime(&now);
np->n_atim.tv_sec = now.tv_sec;
np->n_atim.tv_nsec = now.tv_usec * 1000;
return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_read), ap));
}
static int
nfsfifo_write(ap)
struct vnop_write_args *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
struct timeval now;
np->n_flag |= NUPD;
microtime(&now);
np->n_mtim.tv_sec = now.tv_sec;
np->n_mtim.tv_nsec = now.tv_usec * 1000;
return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_write), ap));
}
static int
nfsfifo_close(ap)
struct vnop_close_args *ap;
{
vnode_t vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct vnode_attr vattr;
struct timeval now;
mount_t mp;
if (np->n_flag & (NACC | NUPD)) {
microtime(&now);
if (np->n_flag & NACC) {
np->n_atim.tv_sec = now.tv_sec;
np->n_atim.tv_nsec = now.tv_usec * 1000;
}
if (np->n_flag & NUPD) {
np->n_mtim.tv_sec = now.tv_sec;
np->n_mtim.tv_nsec = now.tv_usec * 1000;
}
np->n_flag |= NCHG;
if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
VATTR_INIT(&vattr);
if (np->n_flag & NACC) {
vattr.va_access_time = np->n_atim;
VATTR_SET_ACTIVE(&vattr, va_access_time);
}
if (np->n_flag & NUPD) {
vattr.va_modify_time = np->n_mtim;
VATTR_SET_ACTIVE(&vattr, va_modify_time);
}
vnode_setattr(vp, &vattr, ap->a_context);
}
}
return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_close), ap));
}
static int
nfs_ioctl(
__unused struct vnop_ioctl_args *ap)
{
return (ENOTTY);
}
static int
nfs_select(
__unused struct vnop_select_args *ap)
{
return (1);
}
static int
nfs_pagein(ap)
struct vnop_pagein_args *ap;
{
vnode_t vp = ap->a_vp;
upl_t pl = ap->a_pl;
size_t size= ap->a_size;
off_t f_offset = ap->a_f_offset;
vm_offset_t pl_offset = ap->a_pl_offset;
int flags = ap->a_flags;
kauth_cred_t cred;
proc_t p;
struct nfsnode *np = VTONFS(vp);
int biosize, xsize, iosize;
struct nfsmount *nmp;
int error = 0;
vm_offset_t ioaddr;
struct uio auio;
struct iovec_32 aiov;
struct uio * uio = &auio;
int nofreeupl = flags & UPL_NOCOMMIT;
upl_page_info_t *plinfo;
FSDBG(322, vp, f_offset, size, flags);
if (pl == (upl_t)NULL)
panic("nfs_pagein: no upl");
if (UBCINVALID(vp)) {
printf("nfs_pagein: invalid vnode 0x%x", (int)vp);
if (!nofreeupl)
(void) ubc_upl_abort(pl, 0);
return (EPERM);
}
UBCINFOCHECK("nfs_pagein", vp);
if (size <= 0) {
printf("nfs_pagein: invalid size %d", size);
if (!nofreeupl)
(void) ubc_upl_abort(pl, 0);
return (EINVAL);
}
if (f_offset < 0 || f_offset >= (off_t)np->n_size || (f_offset & PAGE_MASK_64)) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
return (EINVAL);
}
cred = ubc_getcred(vp);
if (cred == NOCRED)
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
auio.uio_offset = f_offset;
#if 1
auio.uio_segflg = UIO_SYSSPACE;
#else
auio.uio_segflg = UIO_SYSSPACE32;
#endif
auio.uio_rw = UIO_READ;
auio.uio_procp = p;
nmp = VFSTONFS(vnode_mount(vp));
if (!nmp) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
return (ENXIO);
}
if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
(void)nfs_fsinfo(nmp, vp, cred, p);
biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
plinfo = ubc_upl_pageinfo(pl);
ubc_upl_map(pl, &ioaddr);
ioaddr += pl_offset;
xsize = size;
do {
iosize = min(biosize, xsize);
aiov.iov_len = iosize;
aiov.iov_base = (uintptr_t)ioaddr;
auio.uio_iovs.iov32p = &aiov;
auio.uio_iovcnt = 1;
uio_uio_resid_set(&auio, iosize);
FSDBG(322, uio->uio_offset, uio_uio_resid(uio), ioaddr, xsize);
#ifdef UPL_DEBUG
upl_ubc_alias_set(pl, current_thread(), 2);
#endif
OSAddAtomic(1, (SInt32*)&nfsstats.pageins);
error = nfs_readrpc(vp, uio, cred, p);
if (!error) {
if (uio_uio_resid(uio)) {
int zcnt = uio_uio_resid(uio);
int zoff = iosize - zcnt;
bzero((char *)ioaddr + zoff, zcnt);
FSDBG(324, uio->uio_offset, zoff, zcnt, ioaddr);
uio->uio_offset += zcnt;
}
ioaddr += iosize;
xsize -= iosize;
} else {
FSDBG(322, uio->uio_offset, uio_uio_resid(uio), error, -1);
}
nmp = VFSTONFS(vnode_mount(vp));
} while (error == 0 && xsize > 0);
ubc_upl_unmap(pl);
if (!nofreeupl) {
if (error)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_ERROR |
UPL_ABORT_FREE_ON_EMPTY);
else
ubc_upl_commit_range(pl, pl_offset, size,
UPL_COMMIT_CLEAR_DIRTY |
UPL_COMMIT_FREE_ON_EMPTY);
}
return (error);
}
static int
nfs_pageout(ap)
struct vnop_pageout_args *ap;
{
vnode_t vp = ap->a_vp;
upl_t pl = ap->a_pl;
size_t size= ap->a_size;
off_t f_offset = ap->a_f_offset;
vm_offset_t pl_offset = ap->a_pl_offset;
int flags = ap->a_flags;
struct nfsnode *np = VTONFS(vp);
kauth_cred_t cred;
proc_t p;
struct nfsbuf *bp;
struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
daddr64_t lbn;
int error = 0, iomode, must_commit;
off_t off;
vm_offset_t ioaddr;
struct uio auio;
struct iovec_32 aiov;
int nofreeupl = flags & UPL_NOCOMMIT;
size_t biosize, iosize, pgsize, xsize;
FSDBG(323, f_offset, size, pl, pl_offset);
if (pl == (upl_t)NULL)
panic("nfs_pageout: no upl");
if (UBCINVALID(vp)) {
printf("nfs_pageout: invalid vnode 0x%x", (int)vp);
if (!nofreeupl)
ubc_upl_abort(pl, 0);
return (EIO);
}
UBCINFOCHECK("nfs_pageout", vp);
if (size <= 0) {
printf("nfs_pageout: invalid size %d", size);
if (!nofreeupl)
ubc_upl_abort(pl, 0);
return (EINVAL);
}
if (!nmp) {
if (!nofreeupl)
ubc_upl_abort(pl, UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY);
return (ENXIO);
}
biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
for (iosize = 0; iosize < size; iosize += xsize) {
off = f_offset + iosize;
xsize = biosize - (off % biosize);
if (off + xsize > f_offset + size)
xsize = f_offset + size - off;
lbn = ubc_offtoblk(vp, off);
lck_mtx_lock(nfs_buf_mutex);
if ((bp = nfs_buf_incore(vp, lbn))) {
FSDBG(323, off, bp, bp->nb_lflags, bp->nb_flags);
if (nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0)) {
lck_mtx_unlock(nfs_buf_mutex);
if (!nofreeupl)
ubc_upl_abort(pl, 0);
return (EBUSY);
}
if (bp->nb_dirtyend > 0) {
off_t boff, start, end;
boff = NBOFF(bp);
start = off;
end = off + xsize;
if (end > (off_t)np->n_size)
end = np->n_size;
start -= boff;
end -= boff;
if ((bp->nb_dirtyoff < start) &&
(bp->nb_dirtyend > end)) {
FSDBG(323, vp, bp, 0xd00deebc, EBUSY);
nfs_buf_drop(bp);
lck_mtx_unlock(nfs_buf_mutex);
if (!nofreeupl)
ubc_upl_abort(pl, 0);
return (EBUSY);
}
if ((bp->nb_dirtyoff < start) ||
(bp->nb_dirtyend > end)) {
if (bp->nb_dirtyoff < start)
bp->nb_dirtyend = min(bp->nb_dirtyend, start);
if (bp->nb_dirtyend > end)
bp->nb_dirtyoff = max(bp->nb_dirtyoff, end);
FSDBG(323, bp, bp->nb_dirtyoff, bp->nb_dirtyend, 0xd00dee00);
nfs_buf_drop(bp);
lck_mtx_unlock(nfs_buf_mutex);
continue;
}
}
nfs_buf_remfree(bp);
lck_mtx_unlock(nfs_buf_mutex);
SET(bp->nb_flags, NB_INVAL);
if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
CLR(bp->nb_flags, NB_NEEDCOMMIT);
np->n_needcommitcnt--;
CHECK_NEEDCOMMITCNT(np);
}
nfs_buf_release(bp, 1);
} else {
lck_mtx_unlock(nfs_buf_mutex);
}
}
cred = ubc_getcred(vp);
if (cred == NOCRED)
cred = vfs_context_ucred(ap->a_context);
p = vfs_context_proc(ap->a_context);
if (np->n_flag & NWRITEERR) {
np->n_flag &= ~NWRITEERR;
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_FREE_ON_EMPTY);
return (np->n_error);
}
if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
nfs_fsinfo(nmp, vp, cred, p);
if (f_offset < 0 || f_offset >= (off_t)np->n_size ||
f_offset & PAGE_MASK_64 || size & PAGE_MASK_64) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_FREE_ON_EMPTY);
return (EINVAL);
}
ubc_upl_map(pl, &ioaddr);
ioaddr += pl_offset;
if ((u_quad_t)f_offset + size > np->n_size)
xsize = np->n_size - f_offset;
else
xsize = size;
pgsize = round_page_64(xsize);
if (size > pgsize) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset + pgsize,
size - pgsize,
UPL_ABORT_FREE_ON_EMPTY);
}
if ((u_quad_t)f_offset < np->n_size && (u_quad_t)f_offset + size > np->n_size) {
size_t io = np->n_size - f_offset;
bzero((caddr_t)(ioaddr + io), size - io);
FSDBG(321, np->n_size, f_offset, f_offset + io, size - io);
}
auio.uio_offset = f_offset;
#if 1
auio.uio_segflg = UIO_SYSSPACE;
#else
auio.uio_segflg = UIO_SYSSPACE32;
#endif
auio.uio_rw = UIO_READ;
auio.uio_procp = p;
do {
iosize = min(biosize, xsize);
uio_uio_resid_set(&auio, iosize);
aiov.iov_len = iosize;
aiov.iov_base = (uintptr_t)ioaddr;
auio.uio_iovs.iov32p = &aiov;
auio.uio_iovcnt = 1;
FSDBG(323, auio.uio_offset, uio_uio_resid(&auio), ioaddr, xsize);
OSAddAtomic(1, (SInt32*)&nfsstats.pageouts);
vnode_startwrite(vp);
iomode = NFSV3WRITE_FILESYNC;
error = nfs_writerpc(vp, &auio, cred, p, &iomode, &must_commit);
if (must_commit)
nfs_clearcommit(vnode_mount(vp));
vnode_writedone(vp);
if (error)
goto cleanup;
ioaddr += iosize;
xsize -= iosize;
} while (xsize > 0);
cleanup:
ubc_upl_unmap(pl);
if (!nofreeupl) {
if (error) {
int abortflags = 0;
short action = nfs_pageouterrorhandler(error);
switch (action) {
case DUMP:
abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
break;
case DUMPANDLOG:
abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
if (error <= ELAST &&
(errorcount[error] % 100 == 0))
printf("nfs_pageout: unexpected error %d. dumping vm page\n", error);
errorcount[error]++;
break;
case RETRY:
abortflags = UPL_ABORT_FREE_ON_EMPTY;
break;
case RETRYWITHSLEEP:
abortflags = UPL_ABORT_FREE_ON_EMPTY;
tsleep(&lbolt, PSOCK, "nfspageout", 0);
break;
case SEVER:
default:
printf("nfs_pageout: action %d not expected\n", action);
break;
}
ubc_upl_abort_range(pl, pl_offset, size, abortflags);
} else
ubc_upl_commit_range(pl, pl_offset, pgsize,
UPL_COMMIT_CLEAR_DIRTY |
UPL_COMMIT_FREE_ON_EMPTY);
}
return (error);
}
static int
nfs_blktooff(ap)
struct vnop_blktooff_args *ap;
{
int biosize;
vnode_t vp = ap->a_vp;
mount_t mp = vnode_mount(vp);
if (!mp)
return (ENXIO);
biosize = vfs_statfs(mp)->f_iosize;
*ap->a_offset = (off_t)(ap->a_lblkno * biosize);
return (0);
}
static int
nfs_offtoblk(ap)
struct vnop_offtoblk_args *ap;
{
int biosize;
vnode_t vp = ap->a_vp;
mount_t mp = vnode_mount(vp);
if (!mp)
return (ENXIO);
biosize = vfs_statfs(mp)->f_iosize;
*ap->a_lblkno = (daddr64_t)(ap->a_offset / biosize);
return (0);
}