* 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.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* From: @(#)nfs_vnops.c 7.60 (Berkeley) 5/24/91
* $Id: nfs_vnops.c,v 1.6 1993/12/19 00:54:18 wollman Exp $
* vnode op calls for sun nfs version 2
#include "../ufs/quota.h"
#include "../ufs/inode.h"
* Global vfs data structures for nfs
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 */
nfs_advlock
, /* advlock */
* Special device vnode ops
struct vnodeops spec_nfsv2nodeops
= {
spec_lookup
, /* lookup */
spec_create
, /* create */
nfs_getattr
, /* getattr */
nfs_setattr
, /* setattr */
spec_select
, /* select */
spec_remove
, /* remove */
spec_rename
, /* rename */
spec_symlink
, /* symlink */
spec_readdir
, /* readdir */
spec_readlink
, /* readlink */
spec_abortop
, /* abortop */
nfs_inactive
, /* inactive */
nfs_reclaim
, /* reclaim */
spec_strategy
, /* strategy */
nfs_islocked
, /* islocked */
spec_advlock
, /* advlock */
struct vnodeops fifo_nfsv2nodeops
= {
fifo_lookup
, /* lookup */
fifo_create
, /* create */
nfs_getattr
, /* getattr */
nfs_setattr
, /* setattr */
fifo_select
, /* select */
fifo_remove
, /* remove */
fifo_rename
, /* rename */
fifo_symlink
, /* symlink */
fifo_readdir
, /* readdir */
fifo_readlink
, /* readlink */
fifo_abortop
, /* abortop */
nfs_inactive
, /* inactive */
nfs_reclaim
, /* reclaim */
fifo_badop
, /* strategy */
nfs_islocked
, /* islocked */
fifo_advlock
, /* advlock */
extern u_long nfs_procids
[NFS_NPROCS
];
extern u_long nfs_prog
, nfs_vers
;
extern char nfsiobuf
[MAXPHYS
+NBPG
];
struct buf nfs_bqueue
; /* Queue head for nfsiod's */
struct proc
*nfs_iodwant
[NFS_MAXASYNCDAEMON
];
* nfs null call from vfs.
struct mbuf
*mreq
, *mrep
, *md
, *mb
;
nfsm_reqhead(nfs_procids
[NFSPROC_NULL
], cred
, 0);
nfsm_request(vp
, NFSPROC_NULL
, p
, 0);
* Essentially just get vattr and then imitate iaccess()
nfs_access(vp
, mode
, cred
, p
)
register struct ucred
*cred
;
register struct vattr
*vap
;
* If you're the super-user,
if (error
= nfs_dogetattr(vp
, vap
, cred
, 0, p
))
* 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
) == mode
)
* Just check to see if the type is ok
nfs_open(vp
, mode
, cred
, p
)
register enum vtype vtyp
;
if (vtyp
== VREG
|| vtyp
== VDIR
|| vtyp
== VLNK
)
* For reg files, invalidate any buffer cache entries.
nfs_close(vp
, fflags
, cred
, p
)
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
, p
)
register struct vnode
*vp
;
return (nfs_dogetattr(vp
, vap
, cred
, 0, p
));
nfs_dogetattr(vp
, vap
, cred
, tryhard
, p
)
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
, p
, tryhard
);
nfs_setattr(vp
, vap
, cred
, p
)
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
);
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
);
if (vap
->va_size
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
||
vap
->va_atime
.tv_sec
!= VNOVAL
) {
if (np
->n_flag
& NMODIFIED
) {
np
->n_flag
&= ~NMODIFIED
;
nfsm_request(vp
, NFSPROC_SETATTR
, p
, 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
& OPMASK
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
|WANTPARENT
);
if ((error
= cache_lookup(ndp
)) && error
!= ENOENT
) {
* See the comment starting `Step through' in ufs/ufs_lookup.c
* for an explanation of the locking protocol
} else if (ndp
->ni_isdotdot
) {
if (!error
&& lockparent
&& *ndp
->ni_next
== '\0')
if (!lockparent
|| error
|| *ndp
->ni_next
!= '\0')
if (!nfs_dogetattr(vdp
, &vattr
, ndp
->ni_cred
, 0, p
)&&
vattr
.va_ctime
.tv_sec
== VTONFS(vdp
)->n_ctime
) {
nfsstats
.lookupcache_hits
++;
if (flag
!= LOOKUP
&& *ndp
->ni_next
== 0)
ndp
->ni_nameiop
|= SAVENAME
;
if (lockparent
&& vdp
!= vp
&& *ndp
->ni_next
== '\0')
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
, p
, 0);
if (lockparent
|| (flag
!= CREATE
&& flag
!= RENAME
) ||
if (flag
!= LOOKUP
&& *ndp
->ni_next
== 0)
ndp
->ni_nameiop
|= SAVENAME
;
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
)
ndp
->ni_nameiop
|= SAVENAME
;
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)) {
ndp
->ni_nameiop
|= SAVENAME
;
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 (flag
!= LOOKUP
&& *ndp
->ni_next
== 0)
ndp
->ni_nameiop
|= SAVENAME
;
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
)
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
, uiop
->uio_procp
, 0);
nfsm_strsiz(len
, NFS_MAXPATHLEN
);
nfs_readrpc(vp
, uiop
, cred
)
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(tl
, u_long
*, NFSX_UNSIGNED
*3);
*tl
++ = txdr_unsigned(uiop
->uio_offset
);
*tl
++ = txdr_unsigned(len
);
nfsm_request(vp
, NFSPROC_READ
, uiop
->uio_procp
, 1);
nfsm_loadattr(vp
, (struct vattr
*)0);
nfsm_strsiz(retlen
, nmp
->nm_rsize
);
nfsm_mtouio(uiop
, retlen
);
nfs_writerpc(vp
, uiop
, cred
)
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(tl
, u_long
*, NFSX_UNSIGNED
*4);
*(tl
+1) = txdr_unsigned(uiop
->uio_offset
);
*(tl
+3) = txdr_unsigned(len
);
nfsm_request(vp
, NFSPROC_WRITE
, uiop
->uio_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
, p
)
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_namelen
)+NFSX_SATTR
);
nfsm_strtom(ndp
->ni_ptr
, ndp
->ni_namelen
, 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
, p
, 1);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
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_namelen
)+NFSX_SATTR
);
nfsm_strtom(ndp
->ni_ptr
, ndp
->ni_namelen
, 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
);
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
, p
, 1);
nfsm_mtofh(ndp
->ni_dvp
, ndp
->ni_vp
);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
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
, p
);
nfsstats
.rpccnt
[NFSPROC_REMOVE
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_REMOVE
], ndp
->ni_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(ndp
->ni_namelen
));
nfsm_strtom(ndp
->ni_ptr
, ndp
->ni_namelen
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_REMOVE
, p
, 1);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
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 sillyrename
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_REMOVE
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_REMOVE
], sp
->s_cred
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(sp
->s_namlen
));
nfsm_strtom(sp
->s_name
, sp
->s_namlen
, NFS_MAXNAMLEN
);
nfsm_request(sp
->s_dvp
, NFSPROC_REMOVE
, p
, 1);
VTONFS(sp
->s_dvp
)->n_flag
|= NMODIFIED
;
nfs_rename(sndp
, tndp
, p
)
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_namelen
) +
nfsm_rndup(tndp
->ni_namelen
)); /* or sndp->ni_cred?*/
nfsm_fhtom(sndp
->ni_dvp
);
nfsm_strtom(sndp
->ni_ptr
, sndp
->ni_namelen
, NFS_MAXNAMLEN
);
nfsm_fhtom(tndp
->ni_dvp
);
nfsm_strtom(tndp
->ni_ptr
, tndp
->ni_namelen
, NFS_MAXNAMLEN
);
nfsm_request(sndp
->ni_dvp
, NFSPROC_RENAME
, p
, 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
nfs_renameit(sndp
, sp
, p
)
register struct nameidata
*sndp
;
register struct sillyrename
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_RENAME
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_RENAME
], sp
->s_cred
,
(NFSX_FH
+NFSX_UNSIGNED
)*2+nfsm_rndup(sndp
->ni_namelen
) +
nfsm_rndup(sp
->s_namlen
)); /* or sndp->ni_cred?*/
nfsm_fhtom(sndp
->ni_dvp
);
nfsm_strtom(sndp
->ni_ptr
, sndp
->ni_namelen
, NFS_MAXNAMLEN
);
nfsm_strtom(sp
->s_name
, sp
->s_namlen
, NFS_MAXNAMLEN
);
nfsm_request(sndp
->ni_dvp
, NFSPROC_RENAME
, p
, 1);
FREE(sndp
->ni_pnbuf
, M_NAMEI
);
VTONFS(sndp
->ni_dvp
)->n_flag
|= NMODIFIED
;
VTONFS(sp
->s_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_namelen
));
nfsm_strtom(ndp
->ni_ptr
, ndp
->ni_namelen
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LINK
, p
, 1);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
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
, p
)
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_namelen
)+NFSX_UNSIGNED
);
nfsm_strtom(ndp
->ni_ptr
, ndp
->ni_namelen
, 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
, p
, 1);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
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
;
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_ptr
, 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
, p
, 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_ptr
, len
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_LOOKUP
, p
, 1);
nfsm_mtofh(ndp
->ni_dvp
, ndp
->ni_vp
);
if (ndp
->ni_vp
->v_type
!= VDIR
) {
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
* 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_namelen
));
nfsm_strtom(ndp
->ni_ptr
, ndp
->ni_namelen
, NFS_MAXNAMLEN
);
nfsm_request(ndp
->ni_dvp
, NFSPROC_RMDIR
, p
, 1);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
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, uiop
->uio_procp
) == 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
)
register struct vnode
*vp
;
register struct direct
*dp
= 0;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
struct direct
*savdp
= 0;
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 NFS_DIRBLKSIZ.
* The stopping criteria is EOF or buffer full.
while (more_dirs
&& uiop
->uio_resid
>= NFS_DIRBLKSIZ
) {
nfsstats
.rpccnt
[NFSPROC_READDIR
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_READDIR
], cred
, xid
);
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(uiop
->uio_offset
);
*tl
= txdr_unsigned(((uiop
->uio_resid
> nmp
->nm_rsize
) ?
nmp
->nm_rsize
: uiop
->uio_resid
) & ~(NFS_DIRBLKSIZ
-1));
nfsm_request(vp
, NFSPROC_READDIR
, uiop
->uio_procp
, 0);
nfsm_disect(tl
, u_long
*, NFSX_UNSIGNED
);
more_dirs
= fxdr_unsigned(int, *tl
);
/* 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(tl
, u_long
*, 2*NFSX_UNSIGNED
);
dp
= (struct direct
*)tl
;
dp
->d_ino
= fxdr_unsigned(u_long
, *tl
++);
len
= fxdr_unsigned(int, *tl
);
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(tl
, u_long
*, 2*NFSX_UNSIGNED
);
off
= fxdr_unsigned(off_t
, *tl
);
*tl
++ = 0; /* Ensures null termination of name */
more_dirs
= fxdr_unsigned(int, *tl
);
dp
->d_reclen
= len
+4*NFSX_UNSIGNED
;
* If at end of rpc data, get the eof boolean
nfsm_disecton(tl
, u_long
*, NFSX_UNSIGNED
);
more_dirs
= (fxdr_unsigned(int, *tl
) == 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 NFS_DIRBLKSIZ
* by increasing d_reclen for the last record.
if (uiop
->uio_resid
< tresid
) {
len
= uiop
->uio_resid
& (NFS_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...
register struct nameidata
*ndp
;
register struct nfsnode
*np
;
register struct sillyrename
*sp
;
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
);
sp
->s_cred
= crdup(ndp
->ni_cred
);
/* Fudge together a funny name */
bcopy(".nfsAxxxx4.4", sp
->s_name
, 13);
sp
->s_name
[8] = hextoasc
[pid
& 0xf];
sp
->s_name
[7] = hextoasc
[(pid
>> 4) & 0xf];
sp
->s_name
[6] = hextoasc
[(pid
>> 8) & 0xf];
sp
->s_name
[5] = hextoasc
[(pid
>> 12) & 0xf];
/* Try lookitups until we get one that isn't there */
while (nfs_lookitup(sp
, (nfsv2fh_t
*)0, p
) == 0) {
if (sp
->s_name
[4] > 'z') {
if (error
= nfs_renameit(ndp
, sp
, p
))
nfs_lookitup(sp
, &np
->n_fh
, p
);
free((caddr_t
)sp
, M_NFSREQ
);
* 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
register struct sillyrename
*sp
;
register struct vnode
*vp
= sp
->s_dvp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_LOOKUP
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_LOOKUP
], sp
->s_cred
, NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
nfsm_strtom(sp
->s_name
, len
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LOOKUP
, p
, 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 ops, since these may still
* be hanging about after the process terminates.
if ((bp
->b_flags
& B_PHYS
) == 0) {
if (bp
->b_flags
& B_ASYNC
)
bp
->b_proc
= (struct proc
*)0;
* If the op is asynchronous and an i/o daemon is waiting
* queue the request, wake it up and wait for completion
* otherwise just do it ourselves.
if ((bp
->b_flags
& B_ASYNC
) == 0 || nfs_numasync
== 0)
for (i
= 0; i
< NFS_MAXASYNCDAEMON
; i
++) {
if (dp
->b_actf
== NULL
) {
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
;
uiop
->uio_procp
= (struct proc
*)0;
* 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
) {
if (bp
->b_flags
& B_DIRTY
)
uiop
->uio_procp
= pageproc
;
cr
= crcopy(uiop
->uio_procp
->p_ucred
);
/* mapping was already done by vmapbuf */
io
.iov_base
= bp
->b_un
.b_addr
;
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
, cr
);
(void) vnode_pager_uncache(vp
);
uiop
->uio_rw
= UIO_WRITE
;
nfsstats
.write_physios
++;
bp
->b_error
= error
= nfs_writerpc(vp
, uiop
, cr
);
* Finally, release pte's used by physical i/o
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
;
panic("nfs_doio bad type");
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_dirtyoff
= bp
->b_dirtyend
= 0;
bp
->b_resid
= uiop
->uio_resid
;
* NB Currently unsupported.
nfs_mmap(vp
, fflags
, cred
, p
)
* 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
, p
)
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
))
* NFS advisory byte-level locks.
nfs_advlock(vp
, id
, op
, fl
, flags
)
register struct nfsnode
*np
= VTONFS(vp
);
return (lf_advlock(&(np
->n_lockf
), np
->n_size
, id
, op
, fl
, flags
));
* 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
);