* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* %sccs.include.redist.c%
* @(#)nfs_vnops.c 7.45 (Berkeley) %G%
* vnode op calls for sun nfs version 2
#include "machine/mtpr.h"
#include "../ufs/quota.h"
#include "../ufs/inode.h"
struct vnodeops nfsv2_vnodeops
= {
nfs_getattr
, /* getattr */
nfs_setattr
, /* setattr */
nfs_symlink
, /* symlink */
nfs_readdir
, /* readdir */
nfs_readlink
, /* readlink */
nfs_abortop
, /* abortop */
nfs_inactive
, /* inactive */
nfs_reclaim
, /* reclaim */
nfs_strategy
, /* strategy */
nfs_islocked
, /* islocked */
/* Special device vnode ops */
struct vnodeops spec_nfsv2nodeops
= {
spec_lookup
, /* lookup */
nfs_getattr
, /* getattr */
nfs_setattr
, /* setattr */
spec_select
, /* select */
spec_badop
, /* symlink */
spec_badop
, /* readdir */
spec_badop
, /* readlink */
spec_badop
, /* abortop */
nfs_inactive
, /* inactive */
nfs_reclaim
, /* reclaim */
spec_strategy
, /* strategy */
nfs_islocked
, /* islocked */
struct vnodeops fifo_nfsv2nodeops
= {
fifo_lookup
, /* lookup */
nfs_getattr
, /* getattr */
nfs_setattr
, /* setattr */
fifo_select
, /* select */
fifo_badop
, /* symlink */
fifo_badop
, /* readdir */
fifo_badop
, /* readlink */
fifo_badop
, /* abortop */
nfs_inactive
, /* inactive */
nfs_reclaim
, /* reclaim */
fifo_badop
, /* strategy */
nfs_islocked
, /* islocked */
extern u_long nfs_procids
[NFS_NPROCS
];
extern u_long nfs_prog
, nfs_vers
;
extern char nfsiobuf
[MAXPHYS
+NBPG
];
struct map nfsmap
[NFS_MSIZ
];
struct buf nfs_bqueue
; /* Queue head for nfsiod's */
int nfs_asyncdaemons
= 0;
struct proc
*nfs_iodwant
[NFS_MAXASYNCDAEMON
];
static int nfsmap_want
= 0;
* nfs null call from vfs.
struct mbuf
*mreq
, *mrep
, *md
, *mb
;
nfsm_reqhead(nfs_procids
[NFSPROC_NULL
], cred
, 0);
nfsm_request(vp
, NFSPROC_NULL
, u
.u_procp
, 0);
* Essentially just get vattr and then imitate iaccess()
nfs_access(vp
, mode
, cred
)
register struct ucred
*cred
;
register struct vattr
*vap
;
* If you're the super-user,
if (error
= nfs_dogetattr(vp
, vap
, cred
, 0))
* Access check is based on only one of owner, group, public.
* If not owner, then check group. If not a member of the
* group, then check public access.
if (cred
->cr_uid
!= vap
->va_uid
) {
for (i
= 0; i
< cred
->cr_ngroups
; i
++, gp
++)
if ((vap
->va_mode
& mode
) != 0)
* Just check to see if the type is ok
register enum vtype vtyp
;
if (vtyp
== VREG
|| vtyp
== VDIR
|| vtyp
== VLNK
)
* For reg files, invalidate any buffer cache entries.
nfs_close(vp
, fflags
, cred
)
register struct vnode
*vp
;
register struct nfsnode
*np
= VTONFS(vp
);
if (vp
->v_type
== VREG
&& (np
->n_flag
& NMODIFIED
)) {
np
->n_flag
&= ~NMODIFIED
;
if (np
->n_flag
& NWRITEERR
) {
np
->n_flag
&= ~NWRITEERR
;
* nfs getattr call from vfs.
nfs_getattr(vp
, vap
, cred
)
register struct vnode
*vp
;
return (nfs_dogetattr(vp
, vap
, cred
, 0));
nfs_dogetattr(vp
, vap
, cred
, tryhard
)
register struct vnode
*vp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
/* First look in the cache.. */
if (nfs_getattrcache(vp
, vap
) == 0)
nfsstats
.rpccnt
[NFSPROC_GETATTR
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_GETATTR
], cred
, NFSX_FH
);
nfsm_request(vp
, NFSPROC_GETATTR
, u
.u_procp
, tryhard
);
nfs_setattr(vp
, vap
, cred
)
register struct vnode
*vp
;
register struct vattr
*vap
;
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_SETATTR
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_SETATTR
], cred
, NFSX_FH
+NFSX_SATTR
);
nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
if (vap
->va_mode
== 0xffff)
sp
->sa_mode
= vtonfs_mode(vp
->v_type
, vap
->va_mode
);
if (vap
->va_uid
== 0xffff)
sp
->sa_uid
= txdr_unsigned(vap
->va_uid
);
if (vap
->va_gid
== 0xffff)
sp
->sa_gid
= txdr_unsigned(vap
->va_gid
);
sp
->sa_size
= txdr_unsigned(vap
->va_size
);
if (vap
->va_size
!= VNOVAL
) {
if (np
->n_flag
& NMODIFIED
) {
np
->n_flag
&= ~NMODIFIED
;
sp
->sa_atime
.tv_sec
= txdr_unsigned(vap
->va_atime
.tv_sec
);
sp
->sa_atime
.tv_usec
= txdr_unsigned(vap
->va_flags
);
txdr_time(&vap
->va_mtime
, &sp
->sa_mtime
);
nfsm_request(vp
, NFSPROC_SETATTR
, u
.u_procp
, 1);
nfsm_loadattr(vp
, (struct vattr
*)0);
/* should we fill in any vap fields ?? */
* nfs lookup call, one step at a time...
* If not found, unlock the directory nfsnode and do the rpc
register struct vnode
*vp
;
register struct nameidata
*ndp
;
register struct vnode
*vdp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
int lockparent
, wantparent
, flag
, error
= 0;
lockparent
= ndp
->ni_nameiop
& LOCKPARENT
;
flag
= ndp
->ni_nameiop
& OPFLAG
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
|WANTPARENT
);
if ((error
= cache_lookup(ndp
)) && error
!= ENOENT
) {
if (vp
== ndp
->ni_rdir
&& ndp
->ni_isdotdot
)
panic("nfs_lookup: .. through root");
* See the comment starting `Step through' in ufs/ufs_lookup.c
* for an explanation of the locking protocol
} else if (ndp
->ni_isdotdot
) {
if (!nfs_dogetattr(vdp
, &vattr
, ndp
->ni_cred
, 0) &&
vattr
.va_ctime
.tv_sec
== VTONFS(vdp
)->n_ctime
) {
nfsstats
.lookupcache_hits
++;
nfsstats
.lookupcache_misses
++;
nfsstats
.rpccnt
[NFSPROC_LOOKUP
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_LOOKUP
], ndp
->ni_cred
, NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
nfsm_strtom(ndp
->ni_ptr
, len
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LOOKUP
, u
.u_procp
, 0);
if (lockparent
|| (flag
!= CREATE
&& flag
!= RENAME
) ||
nfsm_disect(fhp
,nfsv2fh_t
*,NFSX_FH
);
* Handle DELETE and RENAME cases...
if (flag
== DELETE
&& *ndp
->ni_next
== 0) {
if (!bcmp(VTONFS(vp
)->n_fh
.fh_bytes
, (caddr_t
)fhp
, NFSX_FH
)) {
if (error
= nfs_nget(vp
->v_mount
, fhp
, &np
)) {
nfs_loadattrcache(&newvp
, &md
, &dpos
, (struct vattr
*)0)) {
if (lockparent
|| vp
== newvp
)
if (flag
== RENAME
&& wantparent
&& *ndp
->ni_next
== 0) {
if (!bcmp(VTONFS(vp
)->n_fh
.fh_bytes
, (caddr_t
)fhp
, NFSX_FH
)) {
if (error
= nfs_nget(vp
->v_mount
, fhp
, &np
)) {
nfs_loadattrcache(&newvp
, &md
, &dpos
, (struct vattr
*)0)) {
if (!bcmp(VTONFS(vp
)->n_fh
.fh_bytes
, (caddr_t
)fhp
, NFSX_FH
)) {
} else if (ndp
->ni_isdotdot
) {
if (error
= nfs_nget(vp
->v_mount
, fhp
, &np
)) {
if (error
= nfs_nget(vp
->v_mount
, fhp
, &np
)) {
if (error
= nfs_loadattrcache(&newvp
, &md
, &dpos
, (struct vattr
*)0)) {
if (vp
== newvp
|| (lockparent
&& *ndp
->ni_next
== '\0'))
if (error
== 0 && ndp
->ni_makeentry
) {
np
->n_ctime
= np
->n_vattr
.va_ctime
.tv_sec
;
* Just call nfs_bioread() to do the work.
nfs_read(vp
, uiop
, ioflag
, cred
)
register struct vnode
*vp
;
return (nfs_bioread(vp
, uiop
, ioflag
, cred
));
nfs_readlink(vp
, uiop
, cred
)
return (nfs_bioread(vp
, uiop
, 0, cred
));
* Called by nfs_doio() from below the buffer cache.
nfs_readlinkrpc(vp
, uiop
, cred
, procp
)
register struct vnode
*vp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_READLINK
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_READLINK
], cred
, NFSX_FH
);
nfsm_request(vp
, NFSPROC_READLINK
, procp
, 0);
nfsm_strsiz(len
, NFS_MAXPATHLEN
);
nfs_readrpc(vp
, uiop
, cred
, procp
)
register struct vnode
*vp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nmp
= VFSTONFS(vp
->v_mount
);
nfsstats
.rpccnt
[NFSPROC_READ
]++;
len
= (tsiz
> nmp
->nm_rsize
) ? nmp
->nm_rsize
: tsiz
;
nfsm_reqhead(nfs_procids
[NFSPROC_READ
], cred
, NFSX_FH
+NFSX_UNSIGNED
*3);
nfsm_build(p
, u_long
*, NFSX_UNSIGNED
*3);
*p
++ = txdr_unsigned(uiop
->uio_offset
);
*p
++ = txdr_unsigned(len
);
nfsm_request(vp
, NFSPROC_READ
, procp
, 1);
nfsm_loadattr(vp
, (struct vattr
*)0);
nfsm_strsiz(retlen
, nmp
->nm_rsize
);
nfsm_mtouio(uiop
, retlen
);
nfs_writerpc(vp
, uiop
, cred
, procp
)
register struct vnode
*vp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nmp
= VFSTONFS(vp
->v_mount
);
nfsstats
.rpccnt
[NFSPROC_WRITE
]++;
len
= (tsiz
> nmp
->nm_wsize
) ? nmp
->nm_wsize
: tsiz
;
nfsm_reqhead(nfs_procids
[NFSPROC_WRITE
], cred
,
NFSX_FH
+NFSX_UNSIGNED
*4);
nfsm_build(p
, u_long
*, NFSX_UNSIGNED
*4);
*(p
+1) = txdr_unsigned(uiop
->uio_offset
);
*(p
+3) = txdr_unsigned(len
);
nfsm_request(vp
, NFSPROC_WRITE
, procp
, 1);
nfsm_loadattr(vp
, (struct vattr
*)0);
* This is a kludge. Use a create rpc but with the IFMT bits of the mode
* set to specify the file type and the size field for rdev.
nfs_mknod(ndp
, vap
, cred
)
register struct vattr
*vap
;
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
)
rdev
= txdr_unsigned(vap
->va_rdev
);
else if (vap
->va_type
== VFIFO
)
nfsstats
.rpccnt
[NFSPROC_CREATE
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_CREATE
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
)+NFSX_SATTR
);
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
sp
->sa_mode
= vtonfs_mode(vap
->va_type
, vap
->va_mode
);
sp
->sa_uid
= txdr_unsigned(ndp
->ni_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(ndp
->ni_cred
->cr_gid
);
/* or should these be VNOVAL ?? */
txdr_time(&vap
->va_atime
, &sp
->sa_atime
);
txdr_time(&vap
->va_mtime
, &sp
->sa_mtime
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_CREATE
, u
.u_procp
, 1);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
register struct nameidata
*ndp
;
register struct vattr
*vap
;
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_CREATE
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_CREATE
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
)+NFSX_SATTR
);
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
sp
->sa_mode
= vtonfs_mode(VREG
, vap
->va_mode
);
sp
->sa_uid
= txdr_unsigned(ndp
->ni_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(ndp
->ni_cred
->cr_gid
);
sp
->sa_size
= txdr_unsigned(0);
/* or should these be VNOVAL ?? */
txdr_time(&vap
->va_atime
, &sp
->sa_atime
);
txdr_time(&vap
->va_mtime
, &sp
->sa_mtime
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_CREATE
, u
.u_procp
, 1);
nfsm_mtofh(ndp
->ni_dvp
, ndp
->ni_vp
);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
* To try and make nfs semantics closer to ufs semantics, a file that has
* other processes using the vnode is renamed instead of removed and then
* removed later on the last close.
* If a rename is not already in the works
* call nfs_sillyrename() to set it up
register struct nameidata
*ndp
;
register struct vnode
*vp
= ndp
->ni_vp
;
register struct nfsnode
*np
= VTONFS(ndp
->ni_vp
);
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
if (vp
->v_usecount
> 1) {
error
= nfs_sillyrename(ndp
, REMOVE
);
nfsstats
.rpccnt
[NFSPROC_REMOVE
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_REMOVE
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
));
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_REMOVE
, u
.u_procp
, 1);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
* Kludge City: If the first reply to the remove rpc is lost..
* the reply to the retransmitted request will be ENOENT
* since the file was in fact removed
* Therefore, we cheat and return success.
* nfs file remove rpc called from nfs_inactive
register struct nameidata
*ndp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_REMOVE
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_REMOVE
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
));
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_REMOVE
, u
.u_procp
, 1);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
register struct nameidata
*sndp
, *tndp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_RENAME
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_RENAME
], tndp
->ni_cred
,
(NFSX_FH
+NFSX_UNSIGNED
)*2+nfsm_rndup(sndp
->ni_dent
.d_namlen
)+
nfsm_rndup(tndp
->ni_dent
.d_namlen
)); /* or sndp->ni_cred?*/
nfsm_fhtom(sndp
->ni_dvp
);
nfsm_strtom(sndp
->ni_dent
.d_name
,sndp
->ni_dent
.d_namlen
,NFS_MAXNAMLEN
);
nfsm_fhtom(tndp
->ni_dvp
);
nfsm_strtom(tndp
->ni_dent
.d_name
,tndp
->ni_dent
.d_namlen
,NFS_MAXNAMLEN
);
nfsm_request(sndp
->ni_dvp
, NFSPROC_RENAME
, u
.u_procp
, 1);
VTONFS(sndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
VTONFS(tndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
if (sndp
->ni_vp
->v_type
== VDIR
) {
if (tndp
->ni_vp
!= NULL
&& tndp
->ni_vp
->v_type
== VDIR
)
cache_purge(tndp
->ni_dvp
);
cache_purge(sndp
->ni_dvp
);
if (tndp
->ni_dvp
== tndp
->ni_vp
)
* Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
* nfs file rename rpc called from nfs_remove() above
register struct nameidata
*sndp
, *tndp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_RENAME
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_RENAME
], tndp
->ni_cred
,
(NFSX_FH
+NFSX_UNSIGNED
)*2+nfsm_rndup(sndp
->ni_dent
.d_namlen
)+
nfsm_rndup(tndp
->ni_dent
.d_namlen
)); /* or sndp->ni_cred?*/
nfsm_fhtom(sndp
->ni_dvp
);
nfsm_strtom(sndp
->ni_dent
.d_name
,sndp
->ni_dent
.d_namlen
,NFS_MAXNAMLEN
);
nfsm_fhtom(tndp
->ni_dvp
);
nfsm_strtom(tndp
->ni_dent
.d_name
,tndp
->ni_dent
.d_namlen
,NFS_MAXNAMLEN
);
nfsm_request(sndp
->ni_dvp
, NFSPROC_RENAME
, u
.u_procp
, 1);
VTONFS(sndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
VTONFS(tndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
* nfs hard link create call
register struct vnode
*vp
;
register struct nameidata
*ndp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_LINK
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_LINK
], ndp
->ni_cred
,
NFSX_FH
*2+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
));
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LINK
, u
.u_procp
, 1);
VTONFS(vp
)->n_attrstamp
= 0;
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
* Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
* nfs symbolic link create call
nfs_symlink(ndp
, vap
, nm
)
char *nm
; /* is this the path ?? */
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_SYMLINK
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_SYMLINK
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
)+NFSX_UNSIGNED
);
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_strtom(nm
, strlen(nm
), NFS_MAXPATHLEN
);
nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
sp
->sa_mode
= vtonfs_mode(VLNK
, vap
->va_mode
);
sp
->sa_uid
= txdr_unsigned(ndp
->ni_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(ndp
->ni_cred
->cr_gid
);
sp
->sa_size
= txdr_unsigned(VNOVAL
);
txdr_time(&vap
->va_atime
, &sp
->sa_atime
); /* or VNOVAL ?? */
txdr_time(&vap
->va_mtime
, &sp
->sa_mtime
); /* or VNOVAL ?? */
nfsm_request(ndp
->ni_dvp
, NFSPROC_SYMLINK
, u
.u_procp
, 1);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
* Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
register struct nameidata
*ndp
;
register struct nfsv2_sattr
*sp
;
int error
= 0, firsttry
= 1;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
len
= ndp
->ni_dent
.d_namlen
;
nfsstats
.rpccnt
[NFSPROC_MKDIR
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_MKDIR
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
)+NFSX_SATTR
);
nfsm_strtom(ndp
->ni_dent
.d_name
, len
, NFS_MAXNAMLEN
);
nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
sp
->sa_mode
= vtonfs_mode(VDIR
, vap
->va_mode
);
sp
->sa_uid
= txdr_unsigned(ndp
->ni_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(ndp
->ni_cred
->cr_gid
);
sp
->sa_size
= txdr_unsigned(VNOVAL
);
txdr_time(&vap
->va_atime
, &sp
->sa_atime
); /* or VNOVAL ?? */
txdr_time(&vap
->va_mtime
, &sp
->sa_mtime
); /* or VNOVAL ?? */
nfsm_request(ndp
->ni_dvp
, NFSPROC_MKDIR
, u
.u_procp
, 1);
nfsm_mtofh(ndp
->ni_dvp
, ndp
->ni_vp
);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
* Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
* if we can succeed in looking up the directory.
* "firsttry" is necessary since the macros may "goto nfsmout" which
* is above the if on errors. (Ugh)
if (error
== EEXIST
&& firsttry
) {
nfsstats
.rpccnt
[NFSPROC_LOOKUP
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_LOOKUP
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
nfsm_strtom(ndp
->ni_dent
.d_name
, len
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_LOOKUP
, u
.u_procp
, 1);
nfsm_mtofh(ndp
->ni_dvp
, ndp
->ni_vp
);
if (ndp
->ni_vp
->v_type
!= VDIR
) {
* nfs remove directory call
register struct nameidata
*ndp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
if (ndp
->ni_dvp
== ndp
->ni_vp
) {
nfsstats
.rpccnt
[NFSPROC_RMDIR
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_RMDIR
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_dent
.d_namlen
));
nfsm_strtom(ndp
->ni_dent
.d_name
, ndp
->ni_dent
.d_namlen
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_RMDIR
, u
.u_procp
, 1);
VTONFS(ndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
cache_purge(ndp
->ni_dvp
);
* Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
* Although cookie is defined as opaque, I translate it to/from net byte
* order so that it looks more sensible. This appears consistent with the
* Ultrix implementation of NFS.
nfs_readdir(vp
, uiop
, cred
, eofflagp
)
register struct vnode
*vp
;
register struct nfsnode
*np
= VTONFS(vp
);
* First, check for hit on the EOF offset cache
if (uiop
->uio_offset
!= 0 && uiop
->uio_offset
== np
->n_direofoffset
&&
(np
->n_flag
& NMODIFIED
) == 0 &&
nfs_dogetattr(vp
, &vattr
, cred
, 0) == 0 &&
np
->n_mtime
== vattr
.va_mtime
.tv_sec
) {
nfsstats
.direofcache_hits
++;
* Call nfs_bioread() to do the real work.
tresid
= uiop
->uio_resid
;
error
= nfs_bioread(vp
, uiop
, 0, cred
);
if (!error
&& uiop
->uio_resid
== tresid
) {
nfsstats
.direofcache_misses
++;
* Called from below the buffer cache by nfs_doio().
nfs_readdirrpc(vp
, uiop
, cred
, procp
)
register struct vnode
*vp
;
register struct direct
*dp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
struct nfsnode
*np
= VTONFS(vp
);
nmp
= VFSTONFS(vp
->v_mount
);
tresid
= uiop
->uio_resid
;
* Loop around doing readdir rpc's of size uio_resid or nm_rsize,
* whichever is smaller, truncated to a multiple of DIRBLKSIZ.
* The stopping criteria is EOF or buffer full.
while (more_dirs
&& uiop
->uio_resid
>= DIRBLKSIZ
) {
nfsstats
.rpccnt
[NFSPROC_READDIR
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_READDIR
], cred
, xid
);
nfsm_build(p
, u_long
*, 2*NFSX_UNSIGNED
);
*p
++ = txdr_unsigned(uiop
->uio_offset
);
*p
= txdr_unsigned(((uiop
->uio_resid
> nmp
->nm_rsize
) ?
nmp
->nm_rsize
: uiop
->uio_resid
) & ~(DIRBLKSIZ
-1));
nfsm_request(vp
, NFSPROC_READDIR
, procp
, 0);
nfsm_disect(p
, u_long
*, NFSX_UNSIGNED
);
more_dirs
= fxdr_unsigned(int, *p
);
/* Save the position so that we can do nfsm_mtouio() later */
/* loop thru the dir entries, doctoring them to 4bsd form */
while (more_dirs
&& siz
< uiop
->uio_resid
) {
savoff
= off
; /* Hold onto offset and dp */
nfsm_disecton(p
, u_long
*, 2*NFSX_UNSIGNED
);
dp
->d_ino
= fxdr_unsigned(u_long
, *p
++);
len
= fxdr_unsigned(int, *p
);
if (len
<= 0 || len
> NFS_MAXNAMLEN
) {
dp
->d_namlen
= (u_short
)len
;
nfsm_adv(len
); /* Point past name */
* This should not be necessary, but some servers have
* broken XDR such that these bytes are not null filled.
*dpos
= '\0'; /* Null-terminate */
nfsm_disecton(p
, u_long
*, 2*NFSX_UNSIGNED
);
off
= fxdr_unsigned(off_t
, *p
);
*p
++ = 0; /* Ensures null termination of name */
more_dirs
= fxdr_unsigned(int, *p
);
dp
->d_reclen
= len
+4*NFSX_UNSIGNED
;
* If at end of rpc data, get the eof boolean
nfsm_disecton(p
, u_long
*, NFSX_UNSIGNED
);
more_dirs
= (fxdr_unsigned(int, *p
) == 0);
* If at EOF, cache directory offset
np
->n_direofoffset
= off
;
* If there is too much to fit in the data buffer, use savoff and
* savdp to trim off the last record.
if (siz
> uiop
->uio_resid
) {
more_dirs
= 0; /* Paranoia */
more_dirs
= 0; /* Ugh, never happens, but in case.. */
* Fill last record, iff any, out to a multiple of DIRBLKSIZ
* by increasing d_reclen for the last record.
if (uiop
->uio_resid
< tresid
) {
len
= uiop
->uio_resid
& (DIRBLKSIZ
- 1);
(uiop
->uio_iov
->iov_base
- lastlen
);
uiop
->uio_iov
->iov_base
+= len
;
uiop
->uio_iov
->iov_len
-= len
;
static char hextoasc
[] = "0123456789abcdef";
* Silly rename. To make the NFS filesystem that is stateless look a little
* more like the "ufs" a remove of an active vnode is translated to a rename
* to a funny looking filename that is removed by nfs_inactive on the
* nfsnode. There is the potential for another process on a different client
* to create the same funny name between the nfs_lookitup() fails and the
* nfs_rename() completes, but...
nfs_sillyrename(ndp
, flag
)
register struct nameidata
*ndp
;
register struct nfsnode
*np
;
register struct sillyrename
*sp
;
register struct nameidata
*tndp
;
np
= VTONFS(ndp
->ni_dvp
);
cache_purge(ndp
->ni_dvp
);
MALLOC(sp
, struct sillyrename
*, sizeof (struct sillyrename
),
bcopy((caddr_t
)&np
->n_fh
, (caddr_t
)&sp
->s_fh
, NFSX_FH
);
tndp
->ni_cred
= crdup(ndp
->ni_cred
);
/* Fudge together a funny name */
bcopy(".nfsAxxxx4.4", tndp
->ni_dent
.d_name
, 13);
tndp
->ni_dent
.d_namlen
= 12;
tndp
->ni_dent
.d_name
[8] = hextoasc
[pid
& 0xf];
tndp
->ni_dent
.d_name
[7] = hextoasc
[(pid
>> 4) & 0xf];
tndp
->ni_dent
.d_name
[6] = hextoasc
[(pid
>> 8) & 0xf];
tndp
->ni_dent
.d_name
[5] = hextoasc
[(pid
>> 12) & 0xf];
/* Try lookitups until we get one that isn't there */
while (nfs_lookitup(ndp
->ni_dvp
, tndp
, (nfsv2fh_t
*)0) == 0) {
tndp
->ni_dent
.d_name
[4]++;
if (tndp
->ni_dent
.d_name
[4] > 'z') {
if (error
= nfs_renameit(ndp
, tndp
))
nfs_lookitup(ndp
->ni_dvp
, tndp
, &np
->n_fh
);
free((caddr_t
)sp
, M_TEMP
);
* Look up a file name for silly rename stuff.
* Just like nfs_lookup() except that it doesn't load returned values
* into the nfsnode table.
* If fhp != NULL it copies the returned file handle out
nfs_lookitup(vp
, ndp
, fhp
)
register struct vnode
*vp
;
register struct nameidata
*ndp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_LOOKUP
]++;
len
= ndp
->ni_dent
.d_namlen
;
nfsm_reqhead(nfs_procids
[NFSPROC_LOOKUP
], ndp
->ni_cred
, NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
nfsm_strtom(ndp
->ni_dent
.d_name
, len
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LOOKUP
, u
.u_procp
, 1);
nfsm_disect(cp
, caddr_t
, NFSX_FH
);
bcopy(cp
, (caddr_t
)fhp
, NFSX_FH
);
* - make nfs_bmap() essentially a no-op that does no translation
* - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc
* after mapping the physical addresses into Kernel Virtual space in the
* (Maybe I could use the process's page mapping, but I was concerned that
* Kernel Write might not be enabled and also figured copyout() would do
* a lot more work than bcopy() and also it currently happens in the
* context of the swapper process (2).
nfs_bmap(vp
, bn
, vpp
, bnp
)
*bnp
= bn
* btodb(vp
->v_mount
->mnt_stat
.f_bsize
);
* Strategy routine for phys. i/o
* If the biod's are running, queue a request
* otherwise just call nfs_doio() to get it done
* Set b_proc. It seems a bit silly to do it here, but since bread()
* doesn't set it, I will.
* Set b_proc == NULL for asynchronous reads, since these may still
* be hanging about after the process terminates.
if ((bp
->b_flags
& (B_READ
| B_ASYNC
)) == (B_READ
| B_ASYNC
))
bp
->b_proc
= (struct proc
*)0;
* If an i/o daemon is waiting
* queue the request, wake it up and wait for completion
* otherwise just do it ourselves
for (i
= 0; i
< nfs_asyncdaemons
; i
++) {
if (rp
= nfs_iodwant
[i
]) {
* Ensure that the async_daemon is still waiting here
if (rp
->p_stat
!= SSLEEP
||
rp
->p_wchan
!= ((caddr_t
)&nfs_iodwant
[i
])) {
nfs_iodwant
[i
] = (struct proc
*)0;
if (dp
->b_actf
== NULL
) {
nfs_iodwant
[i
] = (struct proc
*)0;
wakeup((caddr_t
)&nfs_iodwant
[i
]);
* Essentially play ubasetup() and disk interrupt service routine by
* mapping the data buffer into kernel virtual space and doing the
* nfs read or write rpc's from it.
* If the nfsiod's are not running, this is just called from nfs_strategy(),
* otherwise it is called by the nfsiods to do what would normally be
* partially disk interrupt driven.
register struct uio
*uiop
;
register struct vnode
*vp
;
#if !defined(hp300) && !defined(i386)
register struct pte
*pte
, *ppte
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
* For phys i/o, map the b_addr into kernel virtual space using
* Also, add a temporary b_rcred for reading using the process's uid
if (bp
->b_flags
& B_PHYS
) {
bp
->b_rcred
= cr
= crget();
rp
= (bp
->b_flags
& B_DIRTY
) ? &proc
[2] : bp
->b_proc
;
cr
->cr_gid
= 0; /* Anything ?? */
#if defined(hp300) || defined(i386)
/* mapping was already done by vmapbuf */
io
.iov_base
= bp
->b_un
.b_addr
;
o
= (int)bp
->b_un
.b_addr
& PGOFSET
;
npf2
= npf
= btoc(bp
->b_bcount
+ o
);
* Get some mapping page table entries
while ((reg
= rmalloc(nfsmap
, (long)npf
)) == 0) {
(void) tsleep((caddr_t
)&nfsmap_want
, PZERO
-1, "nfsmap",
if (bp
->b_flags
& B_PAGET
)
pte
= &Usrptmap
[btokmx((struct pte
*)bp
->b_un
.b_addr
)];
v
= btop(bp
->b_un
.b_addr
);
if (bp
->b_flags
& B_UAREA
)
* Play vmaccess() but with the Nfsiomap page table
vbase
= vaddr
= &nfsiobuf
[reg
*NBPG
];
mapin(ppte
, (u_int
)vaddr
, pte
->pg_pfnum
, (int)(PG_V
|PG_KW
));
#endif /* !defined(hp300) */
io
.iov_len
= uiop
->uio_resid
= bp
->b_bcount
;
uiop
->uio_offset
= bp
->b_blkno
* DEV_BSIZE
;
if (bp
->b_flags
& B_READ
) {
bp
->b_error
= error
= nfs_readrpc(vp
, uiop
,
bp
->b_rcred
, bp
->b_proc
);
* If a text file has been modified since it was exec'd
* blow the process' out of the water. This is the
* closest we can come to "Text File Busy" in good old
if ((vp
->v_flag
& VTEXT
) &&
(vp
->v_text
->x_mtime
!= np
->n_vattr
.va_mtime
.tv_sec
))
uiop
->uio_rw
= UIO_WRITE
;
nfsstats
.write_physios
++;
bp
->b_error
= error
= nfs_writerpc(vp
, uiop
,
bp
->b_wcred
, bp
->b_proc
);
* Finally, release pte's used by physical i/o
#if !defined(hp300) && !defined(i386)
rmfree(nfsmap
, (long)npf2
, (long)++reg
);
wakeup((caddr_t
)&nfsmap_want
);
if (bp
->b_flags
& B_READ
) {
io
.iov_len
= uiop
->uio_resid
= bp
->b_bcount
;
io
.iov_base
= bp
->b_un
.b_addr
;
uiop
->uio_offset
= bp
->b_blkno
* DEV_BSIZE
;
error
= nfs_readrpc(vp
, uiop
, bp
->b_rcred
,
nfsstats
.readlink_bios
++;
error
= nfs_readlinkrpc(vp
, uiop
, bp
->b_rcred
,
uiop
->uio_offset
= bp
->b_lblkno
;
error
= nfs_readdirrpc(vp
, uiop
, bp
->b_rcred
,
* Save offset cookie in b_blkno.
bp
->b_blkno
= uiop
->uio_offset
;
io
.iov_len
= uiop
->uio_resid
= bp
->b_dirtyend
uiop
->uio_offset
= (bp
->b_blkno
* DEV_BSIZE
)
io
.iov_base
= bp
->b_un
.b_addr
+ bp
->b_dirtyoff
;
uiop
->uio_rw
= UIO_WRITE
;
bp
->b_error
= error
= nfs_writerpc(vp
, uiop
,
bp
->b_wcred
, bp
->b_proc
);
bp
->b_dirtyoff
= bp
->b_dirtyend
= 0;
bp
->b_resid
= uiop
->uio_resid
;
* Flush all the blocks associated with a vnode.
* Walk through the buffer pool and push any dirty pages
* associated with the vnode.
nfs_fsync(vp
, fflags
, cred
, waitfor
)
register struct vnode
*vp
;
register struct nfsnode
*np
= VTONFS(vp
);
if (np
->n_flag
& NMODIFIED
) {
np
->n_flag
&= ~NMODIFIED
;
vflushbuf(vp
, waitfor
== MNT_WAIT
? B_SYNC
: 0);
if (!error
&& (np
->n_flag
& NWRITEERR
))
* Print out the contents of an nfsnode.
register struct nfsnode
*np
= VTONFS(vp
);
printf("tag VT_NFS, fileid %d fsid 0x%x",
np
->n_vattr
.va_fileid
, np
->n_vattr
.va_fsid
);
printf("%s\n", (np
->n_flag
& NLOCKED
) ? " (LOCKED)" : "");
if (np
->n_lockholder
== 0)
printf("\towner pid %d", np
->n_lockholder
);
printf(" waiting pid %d", np
->n_lockwaiter
);