* 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.75 (Berkeley) %G%
* vnode op calls for sun nfs version 2
#include <nfs/nfsmount.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
* Global vfs data structures for nfs
int (**nfsv2_vnodeop_p
)();
struct vnodeopv_entry_desc nfsv2_vnodeop_entries
[] = {
{ &vop_default_desc
, vn_default_error
},
{ &vop_lookup_desc
, nfs_lookup
}, /* lookup */
{ &vop_create_desc
, nfs_create
}, /* create */
{ &vop_mknod_desc
, nfs_mknod
}, /* mknod */
{ &vop_open_desc
, nfs_open
}, /* open */
{ &vop_close_desc
, nfs_close
}, /* close */
{ &vop_access_desc
, nfs_access
}, /* access */
{ &vop_getattr_desc
, nfs_getattr
}, /* getattr */
{ &vop_setattr_desc
, nfs_setattr
}, /* setattr */
{ &vop_read_desc
, nfs_read
}, /* read */
{ &vop_write_desc
, nfs_write
}, /* write */
{ &vop_ioctl_desc
, nfs_ioctl
}, /* ioctl */
{ &vop_select_desc
, nfs_select
}, /* select */
{ &vop_mmap_desc
, nfs_mmap
}, /* mmap */
{ &vop_fsync_desc
, nfs_fsync
}, /* fsync */
{ &vop_seek_desc
, nfs_seek
}, /* seek */
{ &vop_remove_desc
, nfs_remove
}, /* remove */
{ &vop_link_desc
, nfs_link
}, /* link */
{ &vop_rename_desc
, nfs_rename
}, /* rename */
{ &vop_mkdir_desc
, nfs_mkdir
}, /* mkdir */
{ &vop_rmdir_desc
, nfs_rmdir
}, /* rmdir */
{ &vop_symlink_desc
, nfs_symlink
}, /* symlink */
{ &vop_readdir_desc
, nfs_readdir
}, /* readdir */
{ &vop_readlink_desc
, nfs_readlink
}, /* readlink */
{ &vop_abortop_desc
, nfs_abortop
}, /* abortop */
{ &vop_inactive_desc
, nfs_inactive
}, /* inactive */
{ &vop_reclaim_desc
, nfs_reclaim
}, /* reclaim */
{ &vop_lock_desc
, nfs_lock
}, /* lock */
{ &vop_unlock_desc
, nfs_unlock
}, /* unlock */
{ &vop_bmap_desc
, nfs_bmap
}, /* bmap */
{ &vop_strategy_desc
, nfs_strategy
}, /* strategy */
{ &vop_print_desc
, nfs_print
}, /* print */
{ &vop_islocked_desc
, nfs_islocked
}, /* islocked */
{ &vop_advlock_desc
, nfs_advlock
}, /* advlock */
{ &vop_blkatoff_desc
, nfs_blkatoff
}, /* blkatoff */
{ &vop_vget_desc
, nfs_vget
}, /* vget */
{ &vop_valloc_desc
, nfs_valloc
}, /* valloc */
{ &vop_vfree_desc
, nfs_vfree
}, /* vfree */
{ &vop_truncate_desc
, nfs_truncate
}, /* truncate */
{ &vop_update_desc
, nfs_update
}, /* update */
{ &vop_bwrite_desc
, vn_bwrite
},
{ (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
struct vnodeopv_desc nfsv2_vnodeop_opv_desc
=
{ &nfsv2_vnodeop_p
, nfsv2_vnodeop_entries
};
* Special device vnode ops
int (**spec_nfsv2nodeop_p
)();
struct vnodeopv_entry_desc spec_nfsv2nodeop_entries
[] = {
{ &vop_default_desc
, vn_default_error
},
{ &vop_lookup_desc
, spec_lookup
}, /* lookup */
{ &vop_create_desc
, spec_create
}, /* create */
{ &vop_mknod_desc
, spec_mknod
}, /* mknod */
{ &vop_open_desc
, spec_open
}, /* open */
{ &vop_close_desc
, spec_close
}, /* close */
{ &vop_access_desc
, nfs_access
}, /* access */
{ &vop_getattr_desc
, nfs_getattr
}, /* getattr */
{ &vop_setattr_desc
, nfs_setattr
}, /* setattr */
{ &vop_read_desc
, spec_read
}, /* read */
{ &vop_write_desc
, spec_write
}, /* write */
{ &vop_ioctl_desc
, spec_ioctl
}, /* ioctl */
{ &vop_select_desc
, spec_select
}, /* select */
{ &vop_mmap_desc
, spec_mmap
}, /* mmap */
{ &vop_fsync_desc
, spec_fsync
}, /* fsync */
{ &vop_seek_desc
, spec_seek
}, /* seek */
{ &vop_remove_desc
, spec_remove
}, /* remove */
{ &vop_link_desc
, spec_link
}, /* link */
{ &vop_rename_desc
, spec_rename
}, /* rename */
{ &vop_mkdir_desc
, spec_mkdir
}, /* mkdir */
{ &vop_rmdir_desc
, spec_rmdir
}, /* rmdir */
{ &vop_symlink_desc
, spec_symlink
}, /* symlink */
{ &vop_readdir_desc
, spec_readdir
}, /* readdir */
{ &vop_readlink_desc
, spec_readlink
}, /* readlink */
{ &vop_abortop_desc
, spec_abortop
}, /* abortop */
{ &vop_inactive_desc
, nfs_inactive
}, /* inactive */
{ &vop_reclaim_desc
, nfs_reclaim
}, /* reclaim */
{ &vop_lock_desc
, nfs_lock
}, /* lock */
{ &vop_unlock_desc
, nfs_unlock
}, /* unlock */
{ &vop_bmap_desc
, spec_bmap
}, /* bmap */
{ &vop_strategy_desc
, spec_strategy
}, /* strategy */
{ &vop_print_desc
, nfs_print
}, /* print */
{ &vop_islocked_desc
, nfs_islocked
}, /* islocked */
{ &vop_advlock_desc
, spec_advlock
}, /* advlock */
{ &vop_blkatoff_desc
, spec_blkatoff
}, /* blkatoff */
{ &vop_vget_desc
, spec_vget
}, /* vget */
{ &vop_valloc_desc
, spec_valloc
}, /* valloc */
{ &vop_vfree_desc
, spec_vfree
}, /* vfree */
{ &vop_truncate_desc
, spec_truncate
}, /* truncate */
{ &vop_update_desc
, nfs_update
}, /* update */
{ &vop_bwrite_desc
, vn_bwrite
},
{ (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
struct vnodeopv_desc spec_nfsv2nodeop_opv_desc
=
{ &spec_nfsv2nodeop_p
, spec_nfsv2nodeop_entries
};
int (**fifo_nfsv2nodeop_p
)();
struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries
[] = {
{ &vop_default_desc
, vn_default_error
},
{ &vop_lookup_desc
, fifo_lookup
}, /* lookup */
{ &vop_create_desc
, fifo_create
}, /* create */
{ &vop_mknod_desc
, fifo_mknod
}, /* mknod */
{ &vop_open_desc
, fifo_open
}, /* open */
{ &vop_close_desc
, fifo_close
}, /* close */
{ &vop_access_desc
, nfs_access
}, /* access */
{ &vop_getattr_desc
, nfs_getattr
}, /* getattr */
{ &vop_setattr_desc
, nfs_setattr
}, /* setattr */
{ &vop_read_desc
, fifo_read
}, /* read */
{ &vop_write_desc
, fifo_write
}, /* write */
{ &vop_ioctl_desc
, fifo_ioctl
}, /* ioctl */
{ &vop_select_desc
, fifo_select
}, /* select */
{ &vop_mmap_desc
, fifo_mmap
}, /* mmap */
{ &vop_fsync_desc
, fifo_fsync
}, /* fsync */
{ &vop_seek_desc
, fifo_seek
}, /* seek */
{ &vop_remove_desc
, fifo_remove
}, /* remove */
{ &vop_link_desc
, fifo_link
}, /* link */
{ &vop_rename_desc
, fifo_rename
}, /* rename */
{ &vop_mkdir_desc
, fifo_mkdir
}, /* mkdir */
{ &vop_rmdir_desc
, fifo_rmdir
}, /* rmdir */
{ &vop_symlink_desc
, fifo_symlink
}, /* symlink */
{ &vop_readdir_desc
, fifo_readdir
}, /* readdir */
{ &vop_readlink_desc
, fifo_readlink
}, /* readlink */
{ &vop_abortop_desc
, fifo_abortop
}, /* abortop */
{ &vop_inactive_desc
, nfs_inactive
}, /* inactive */
{ &vop_reclaim_desc
, nfs_reclaim
}, /* reclaim */
{ &vop_lock_desc
, nfs_lock
}, /* lock */
{ &vop_unlock_desc
, nfs_unlock
}, /* unlock */
{ &vop_bmap_desc
, fifo_bmap
}, /* bmap */
{ &vop_strategy_desc
, fifo_badop
}, /* strategy */
{ &vop_print_desc
, nfs_print
}, /* print */
{ &vop_islocked_desc
, nfs_islocked
}, /* islocked */
{ &vop_advlock_desc
, fifo_advlock
}, /* advlock */
{ &vop_blkatoff_desc
, fifo_blkatoff
}, /* blkatoff */
{ &vop_vget_desc
, fifo_vget
}, /* vget */
{ &vop_valloc_desc
, fifo_valloc
}, /* valloc */
{ &vop_vfree_desc
, fifo_vfree
}, /* vfree */
{ &vop_truncate_desc
, fifo_truncate
}, /* truncate */
{ &vop_update_desc
, nfs_update
}, /* update */
{ &vop_bwrite_desc
, vn_bwrite
},
{ (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc
=
{ &fifo_nfsv2nodeop_p
, fifo_nfsv2nodeop_entries
};
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
];
#define DIRHDSIZ (sizeof (struct readdir) - (MAXNAMLEN + 1))
* nfs null call from vfs.
nfs_null(vp
, cred
, procp
)
struct mbuf
*mreq
, *mrep
, *md
, *mb
;
nfsm_reqhead(vp
, NFSPROC_NULL
, 0);
nfsm_request(vp
, NFSPROC_NULL
, procp
, cred
);
* Essentially just get vattr and then imitate iaccess()
struct vop_access_args
*ap
;
#define mode (ap->a_mode)
#define cred (ap->a_cred)
register struct vattr
*vap
;
* If you're the super-user,
if (error
= VOP_GETATTR(vp
, vap
, cred
, procp
))
* 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
* and that deletion is not in progress.
struct vop_open_args
*ap
;
#define mode (ap->a_mode)
#define cred (ap->a_cred)
if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
&& vp
->v_type
!= VLNK
)
if ((VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_NQNFS
) == 0)
VTONFS(vp
)->n_attrstamp
= 0; /* For Open/Close consistency */
* For reg files, invalidate any buffer cache entries.
struct vop_close_args
*ap
;
#define fflags (ap->a_fflag)
#define cred (ap->a_cred)
register struct nfsnode
*np
= VTONFS(vp
);
if ((np
->n_flag
& NMODIFIED
) &&
(VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_NQNFS
) == 0 &&
np
->n_flag
&= ~NMODIFIED
;
if (np
->n_flag
& NWRITEERR
) {
np
->n_flag
&= ~NWRITEERR
;
* nfs getattr call from vfs.
struct vop_getattr_args
*ap
;
#define cred (ap->a_cred)
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
/* First look in the cache.. */
if (nfs_getattrcache(vp
, vap
) == 0)
nfsstats
.rpccnt
[NFSPROC_GETATTR
]++;
nfsm_reqhead(vp
, NFSPROC_GETATTR
, NFSX_FH
);
nfsm_request(vp
, NFSPROC_GETATTR
, procp
, cred
);
struct vop_setattr_args
*ap
;
#define cred (ap->a_cred)
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
struct nfsnode
*np
= VTONFS(vp
);
nfsstats
.rpccnt
[NFSPROC_SETATTR
]++;
nfsm_reqhead(vp
, NFSPROC_SETATTR
, 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
, procp
, cred
);
nfsm_loadattr(vp
, (struct vattr
*)0);
if ((VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_NQNFS
) &&
NQNFS_CKCACHABLE(vp
, NQL_WRITE
)) {
nfsm_dissect(tl
, u_long
*, 2*NFSX_UNSIGNED
);
if (QUADGT(frev
, np
->n_brev
))
* nfs lookup call, one step at a time...
* If not found, unlock the directory nfsnode and do the rpc
struct vop_lookup_args
*ap
;
register struct vnode
*vdp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
int lockparent
, wantparent
, error
= 0;
lockparent
= cnp
->cn_flags
& LOCKPARENT
;
wantparent
= cnp
->cn_flags
& (LOCKPARENT
|WANTPARENT
);
nmp
= VFSTONFS(dvp
->v_mount
);
if ((error
= cache_lookup(dvp
, vpp
, cnp
)) && error
!= ENOENT
) {
* See the comment starting `Step through' in ufs/ufs_lookup.c
* for an explanation of the locking protocol
if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
if (NQNFS_CKCACHABLE(dvp
, NQL_READ
)) {
if (QUADNE(np
->n_lrev
, np
->n_brev
) ||
(np
->n_flag
& NMODIFIED
)) {
np
->n_flag
&= ~NMODIFIED
;
nfsstats
.lookupcache_hits
++;
if (cnp
->cn_nameiop
!= LOOKUP
&&
(cnp
->cn_flags
&ISLASTCN
))
cnp
->cn_flags
|= SAVENAME
;
} else if (!VOP_GETATTR(vdp
, &vattr
, cnp
->cn_cred
, cnp
->cn_proc
) &&
vattr
.va_ctime
.tv_sec
== VTONFS(vdp
)->n_ctime
) {
nfsstats
.lookupcache_hits
++;
if (cnp
->cn_nameiop
!= LOOKUP
&& (cnp
->cn_flags
&ISLASTCN
))
cnp
->cn_flags
|= SAVENAME
;
nfsstats
.lookupcache_misses
++;
nfsstats
.rpccnt
[NFSPROC_LOOKUP
]++;
nfsm_reqhead(dvp
, NFSPROC_LOOKUP
, NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
* For nqnfs optionally piggyback a getlease request for the name
if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
if ((nmp
->nm_flag
& NFSMNT_NQLOOKLEASE
) &&
((cnp
->cn_flags
&MAKEENTRY
) && (cnp
->cn_nameiop
!= DELETE
|| !(cnp
->cn_flags
&ISLASTCN
)))) {
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(NQL_READ
);
*tl
= txdr_unsigned(nmp
->nm_leaseterm
);
nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
nfsm_strtom(cnp
->cn_nameptr
, len
, NFS_MAXNAMLEN
);
nfsm_request(dvp
, NFSPROC_LOOKUP
, cnp
->cn_proc
, cnp
->cn_cred
);
if ((cnp
->cn_nameiop
== CREATE
|| cnp
->cn_nameiop
== RENAME
) &&
(cnp
->cn_flags
& ISLASTCN
) && error
== ENOENT
)
if (cnp
->cn_nameiop
!= LOOKUP
&& (cnp
->cn_flags
&ISLASTCN
))
cnp
->cn_flags
|= SAVENAME
;
if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
nqlflag
= fxdr_unsigned(int, *tl
);
nfsm_dissect(tl
, u_long
*, 4*NFSX_UNSIGNED
);
cachable
= fxdr_unsigned(int, *tl
++);
reqtime
+= fxdr_unsigned(int, *tl
++);
nfsm_dissect(fhp
, nfsv2fh_t
*, NFSX_FH
);
if (cnp
->cn_nameiop
== RENAME
&& wantparent
&& (cnp
->cn_flags
&ISLASTCN
)) {
if (!bcmp(np
->n_fh
.fh_bytes
, (caddr_t
)fhp
, NFSX_FH
)) {
if (error
= nfs_nget(dvp
->v_mount
, fhp
, &np
)) {
nfs_loadattrcache(&newvp
, &md
, &dpos
, (struct vattr
*)0)) {
cnp
->cn_flags
|= SAVENAME
;
if (!bcmp(np
->n_fh
.fh_bytes
, (caddr_t
)fhp
, NFSX_FH
)) {
if (error
= nfs_nget(dvp
->v_mount
, fhp
, &np
)) {
if (error
= nfs_loadattrcache(&newvp
, &md
, &dpos
, (struct vattr
*)0)) {
if (cnp
->cn_nameiop
!= LOOKUP
&& (cnp
->cn_flags
&ISLASTCN
))
cnp
->cn_flags
|= SAVENAME
;
if ((cnp
->cn_flags
&MAKEENTRY
) && (cnp
->cn_nameiop
!= DELETE
|| !(cnp
->cn_flags
&ISLASTCN
))) {
if ((nmp
->nm_flag
& NFSMNT_NQNFS
) == 0)
np
->n_ctime
= np
->n_vattr
.va_ctime
.tv_sec
;
else if (nqlflag
&& reqtime
> time
.tv_sec
) {
if (np
->n_tnext
== (struct nfsnode
*)nmp
)
nmp
->nm_tprev
= np
->n_tprev
;
np
->n_tnext
->n_tprev
= np
->n_tprev
;
if (np
->n_tprev
== (struct nfsnode
*)nmp
)
nmp
->nm_tnext
= np
->n_tnext
;
np
->n_tprev
->n_tnext
= np
->n_tnext
;
if (nqlflag
== NQL_WRITE
)
np
->n_flag
|= NQNFSWRITE
;
} else if (nqlflag
== NQL_READ
)
np
->n_flag
&= ~NQNFSWRITE
;
np
->n_flag
|= NQNFSWRITE
;
np
->n_flag
&= ~NQNFSNONCACHE
;
np
->n_flag
|= NQNFSNONCACHE
;
while (tp
!= (struct nfsnode
*)nmp
&& tp
->n_expiry
> np
->n_expiry
)
if (tp
== (struct nfsnode
*)nmp
) {
np
->n_tnext
= nmp
->nm_tnext
;
np
->n_tnext
= tp
->n_tnext
;
if (np
->n_tnext
== (struct nfsnode
*)nmp
)
np
->n_tnext
->n_tprev
= np
;
cache_enter(dvp
, *vpp
, cnp
);
* Just call nfs_bioread() to do the work.
struct vop_read_args
*ap
;
#define ioflag (ap->a_ioflag)
#define cred (ap->a_cred)
return (nfs_bioread(vp
, uiop
, ioflag
, cred
));
struct vop_readlink_args
*ap
;
#define cred (ap->a_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(vp
, NFSPROC_READLINK
, NFSX_FH
);
nfsm_request(vp
, NFSPROC_READLINK
, uiop
->uio_procp
, cred
);
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(vp
, NFSPROC_READ
, 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
, cred
);
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
;
struct nfsnode
*np
= VTONFS(vp
);
nmp
= VFSTONFS(vp
->v_mount
);
nfsstats
.rpccnt
[NFSPROC_WRITE
]++;
len
= (tsiz
> nmp
->nm_wsize
) ? nmp
->nm_wsize
: tsiz
;
nfsm_reqhead(vp
, NFSPROC_WRITE
,
NFSX_FH
+NFSX_UNSIGNED
*4+nfsm_rndup(len
));
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
, cred
);
nfsm_loadattr(vp
, (struct vattr
*)0);
if (nmp
->nm_flag
& NFSMNT_MYWRITE
)
VTONFS(vp
)->n_mtime
= VTONFS(vp
)->n_vattr
.va_mtime
.tv_sec
;
else if ((nmp
->nm_flag
& NFSMNT_NQNFS
) &&
NQNFS_CKCACHABLE(vp
, NQL_WRITE
)) {
nfsm_dissect(tl
, u_long
*, 2*NFSX_UNSIGNED
);
if (QUADGT(frev
, np
->n_brev
))
* 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.
struct vop_mknod_args
*ap
;
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(dvp
, NFSPROC_CREATE
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(cnp
->cn_namelen
)+NFSX_SATTR
);
nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_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(cnp
->cn_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(cnp
->cn_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(dvp
, NFSPROC_CREATE
, cnp
->cn_proc
, cnp
->cn_cred
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(dvp
)->n_flag
|= NMODIFIED
;
struct vop_create_args
*ap
;
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_CREATE
]++;
nfsm_reqhead(dvp
, NFSPROC_CREATE
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(cnp
->cn_namelen
)+NFSX_SATTR
);
nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_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(cnp
->cn_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(cnp
->cn_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(dvp
, NFSPROC_CREATE
, cnp
->cn_proc
, cnp
->cn_cred
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(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
struct vop_remove_args
*ap
;
register struct nfsnode
*np
= VTONFS(vp
);
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
if (vp
->v_usecount
> 1) {
error
= nfs_sillyrename(dvp
, vp
, cnp
);
* Purge the name cache so that the chance of a lookup for
* the name succeeding while the remove is in progress is
* minimized. Without node locking it can still happen, such
* that an I/O op returns ESTALE, but since you get this if
* another host removes the file..
* Throw away biocache buffers. Mainly to avoid
* unnecessary delayed writes.
nfsstats
.rpccnt
[NFSPROC_REMOVE
]++;
nfsm_reqhead(dvp
, NFSPROC_REMOVE
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(cnp
->cn_namelen
));
nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_request(dvp
, NFSPROC_REMOVE
, cnp
->cn_proc
, cnp
->cn_cred
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(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(sp
->s_dvp
, NFSPROC_REMOVE
,
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
, procp
, sp
->s_cred
);
VTONFS(sp
->s_dvp
)->n_flag
|= NMODIFIED
;
struct vop_rename_args
*ap
;
#define fdvp (ap->a_fdvp)
#define fcnp (ap->a_fcnp)
#define tdvp (ap->a_tdvp)
#define tcnp (ap->a_tcnp)
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_RENAME
]++;
nfsm_reqhead(fdvp
, NFSPROC_RENAME
,
(NFSX_FH
+NFSX_UNSIGNED
)*2+nfsm_rndup(fcnp
->cn_namelen
)+
nfsm_rndup(fcnp
->cn_namelen
)); /* or fcnp->cn_cred?*/
nfsm_strtom(fcnp
->cn_nameptr
, fcnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_strtom(tcnp
->cn_nameptr
, tcnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_request(fdvp
, NFSPROC_RENAME
, tcnp
->cn_proc
, tcnp
->cn_cred
);
VTONFS(fdvp
)->n_flag
|= NMODIFIED
;
VTONFS(tdvp
)->n_flag
|= NMODIFIED
;
if (fvp
->v_type
== VDIR
) {
if (tvp
!= NULL
&& tvp
->v_type
== VDIR
)
* Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
* nfs file rename rpc called from nfs_remove() above
nfs_renameit(sdvp
, scnp
, sp
)
struct componentname
*scnp
;
register struct sillyrename
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_RENAME
]++;
nfsm_reqhead(sdvp
, NFSPROC_RENAME
,
(NFSX_FH
+NFSX_UNSIGNED
)*2+nfsm_rndup(scnp
->cn_namelen
)+
nfsm_rndup(sp
->s_namlen
));
nfsm_strtom(scnp
->cn_nameptr
, scnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_strtom(sp
->s_name
, sp
->s_namlen
, NFS_MAXNAMLEN
);
nfsm_request(sdvp
, NFSPROC_RENAME
, scnp
->cn_proc
, scnp
->cn_cred
);
FREE(scnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(sdvp
)->n_flag
|= NMODIFIED
;
* nfs hard link create call
struct vop_link_args
*ap
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_LINK
]++;
nfsm_reqhead(vp
, NFSPROC_LINK
,
NFSX_FH
*2+NFSX_UNSIGNED
+nfsm_rndup(cnp
->cn_namelen
));
nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LINK
, cnp
->cn_proc
, cnp
->cn_cred
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(vp
)->n_attrstamp
= 0;
VTONFS(tdvp
)->n_flag
|= NMODIFIED
;
* Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
* nfs symbolic link create call
struct vop_symlink_args
*ap
;
#define nm (ap->a_target)
register struct nfsv2_sattr
*sp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_SYMLINK
]++;
nfsm_reqhead(dvp
, NFSPROC_SYMLINK
,
NFSX_FH
+2*NFSX_UNSIGNED
+nfsm_rndup(cnp
->cn_namelen
)+nfsm_rndup(slen
)+NFSX_SATTR
);
nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_strtom(nm
, slen
, NFS_MAXPATHLEN
);
nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
sp
->sa_mode
= vtonfs_mode(VLNK
, vap
->va_mode
);
sp
->sa_uid
= txdr_unsigned(cnp
->cn_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(cnp
->cn_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(dvp
, NFSPROC_SYMLINK
, cnp
->cn_proc
, cnp
->cn_cred
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(dvp
)->n_flag
|= NMODIFIED
;
* Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
struct vop_mkdir_args
*ap
;
register struct nfsv2_sattr
*sp
;
int error
= 0, firsttry
= 1;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_MKDIR
]++;
nfsm_reqhead(dvp
, NFSPROC_MKDIR
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
)+NFSX_SATTR
);
nfsm_strtom(cnp
->cn_nameptr
, 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(cnp
->cn_cred
->cr_uid
);
sp
->sa_gid
= txdr_unsigned(cnp
->cn_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(dvp
, NFSPROC_MKDIR
, cnp
->cn_proc
, cnp
->cn_cred
);
VTONFS(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(dvp
, NFSPROC_LOOKUP
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
nfsm_strtom(cnp
->cn_nameptr
, len
, NFS_MAXNAMLEN
);
nfsm_request(dvp
, NFSPROC_LOOKUP
, cnp
->cn_proc
, cnp
->cn_cred
);
if ((*vpp
)->v_type
!= VDIR
) {
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
* nfs remove directory call
struct vop_rmdir_args
*ap
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
nfsstats
.rpccnt
[NFSPROC_RMDIR
]++;
nfsm_reqhead(dvp
, NFSPROC_RMDIR
,
NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(cnp
->cn_namelen
));
nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
);
nfsm_request(dvp
, NFSPROC_RMDIR
, cnp
->cn_proc
, cnp
->cn_cred
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
VTONFS(dvp
)->n_flag
|= NMODIFIED
;
* 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.
struct vop_readdir_args
*ap
;
#define cred (ap->a_cred)
#define eofflagp (ap->a_eofflagp)
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) {
if (VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_NQNFS
) {
if (NQNFS_CKCACHABLE(vp
, NQL_READ
)) {
nfsstats
.direofcache_hits
++;
} else if (VOP_GETATTR(vp
, &vattr
, cred
, 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 readdir
*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 NFS_DIRBLKSIZ.
* The stopping criteria is EOF or buffer full.
while (more_dirs
&& uiop
->uio_resid
>= NFS_DIRBLKSIZ
) {
nfsstats
.rpccnt
[NFSPROC_READDIR
]++;
nfsm_reqhead(vp
, NFSPROC_READDIR
,
NFSX_FH
+2*NFSX_UNSIGNED
);
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
, cred
);
nfsm_dissect(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 */
dp
= (struct readdir
*)0;
while (more_dirs
&& siz
< uiop
->uio_resid
) {
savoff
= off
; /* Hold onto offset and dp */
nfsm_dissecton(tl
, u_long
*, 2*NFSX_UNSIGNED
);
dp
= (struct readdir
*)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_dissecton(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_dissecton(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
;
* Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc() when
* the "rdirlook" mount option is specified.
nfs_readdirlookrpc(vp
, uiop
, cred
)
register struct uio
*uiop
;
register struct readdir
*dp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
struct nameidata nami
, *ndp
= &nami
;
struct componentname
*cnp
= &ndp
->ni_cnd
;
int error
= 0, tlen
, more_dirs
= 1, tresid
, doit
, bigenough
, i
;
if (uiop
->uio_iovcnt
!= 1)
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
[NQNFSPROC_READDIRLOOK
]++;
nfsm_reqhead(vp
, NQNFSPROC_READDIRLOOK
,
NFSX_FH
+3*NFSX_UNSIGNED
);
nfsm_build(tl
, u_long
*, 3*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));
*tl
= txdr_unsigned(nmp
->nm_leaseterm
);
nfsm_request(vp
, NQNFSPROC_READDIRLOOK
, uiop
->uio_procp
, cred
);
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
more_dirs
= fxdr_unsigned(int, *tl
);
/* loop thru the dir entries, doctoring them to 4bsd form */
while (more_dirs
&& bigenough
) {
nfsm_dissect(tl
, u_long
*, 4*NFSX_UNSIGNED
);
cachable
= fxdr_unsigned(int, *tl
++);
ltime
= reqtime
+ fxdr_unsigned(int, *tl
++);
nfsm_dissect(fhp
, nfsv2fh_t
*, NFSX_FH
);
if (!bcmp(VTONFS(vp
)->n_fh
.fh_bytes
, (caddr_t
)fhp
, NFSX_FH
)) {
if (error
= nfs_nget(vp
->v_mount
, fhp
, &np
))
if (error
= nfs_loadattrcache(&newvp
, &md
, &dpos
,
nfsm_dissect(tl
, u_long
*, 2*NFSX_UNSIGNED
);
fileno
= fxdr_unsigned(u_long
, *tl
++);
len
= fxdr_unsigned(int, *tl
);
if (len
<= 0 || len
> NFS_MAXNAMLEN
) {
if ((tlen
+ DIRHDSIZ
) > uiop
->uio_resid
)
dp
= (struct readdir
*)uiop
->uio_iov
->iov_base
;
dp
->d_reclen
= tlen
+ DIRHDSIZ
;
uiop
->uio_resid
-= DIRHDSIZ
;
uiop
->uio_iov
->iov_base
+= DIRHDSIZ
;
uiop
->uio_iov
->iov_len
-= DIRHDSIZ
;
cnp
->cn_nameptr
= uiop
->uio_iov
->iov_base
;
cp
= uiop
->uio_iov
->iov_base
;
for (i
= 0; i
< tlen
; i
++)
uiop
->uio_iov
->iov_base
+= tlen
;
uiop
->uio_iov
->iov_len
-= tlen
;
for (cp
= cnp
->cn_nameptr
, i
= 1; i
<= len
; i
++, cp
++)
cnp
->cn_hash
+= (unsigned char)*cp
* i
;
if (ltime
> time
.tv_sec
) {
if (np
->n_tnext
== (struct nfsnode
*)nmp
)
nmp
->nm_tprev
= np
->n_tprev
;
np
->n_tnext
->n_tprev
= np
->n_tprev
;
if (np
->n_tprev
== (struct nfsnode
*)nmp
)
nmp
->nm_tnext
= np
->n_tnext
;
np
->n_tprev
->n_tnext
= np
->n_tnext
;
np
->n_flag
&= ~NQNFSWRITE
;
np
->n_flag
&= ~NQNFSNONCACHE
;
np
->n_flag
|= NQNFSNONCACHE
;
while (tp
!= (struct nfsnode
*)nmp
&& tp
->n_expiry
> np
->n_expiry
)
if (tp
== (struct nfsnode
*)nmp
) {
np
->n_tnext
= nmp
->nm_tnext
;
np
->n_tnext
= tp
->n_tnext
;
if (np
->n_tnext
== (struct nfsnode
*)nmp
)
np
->n_tnext
->n_tprev
= np
;
cache_enter(ndp
->ni_dvp
, ndp
->ni_vp
, cnp
);
nfsm_adv(nfsm_rndup(len
));
nfsm_dissect(tl
, u_long
*, 2*NFSX_UNSIGNED
);
endoff
= off
= fxdr_unsigned(off_t
, *tl
++);
endoff
= fxdr_unsigned(off_t
, *tl
++);
more_dirs
= fxdr_unsigned(int, *tl
);
* If at end of rpc data, get the eof boolean
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
more_dirs
= (fxdr_unsigned(int, *tl
) == 0);
* If at EOF, cache directory offset
VTONFS(vp
)->n_direofoffset
= endoff
;
if (uiop
->uio_resid
< tresid
)
* 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
+= 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(dvp
, vp
, cnp
)
struct componentname
*cnp
;
register struct nfsnode
*np
;
register struct sillyrename
*sp
;
MALLOC(sp
, struct sillyrename
*, sizeof (struct sillyrename
),
sp
->s_cred
= crdup(cnp
->cn_cred
);
/* Fudge together a funny name */
pid
= cnp
->cn_proc
->p_pid
;
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, cnp
->cn_proc
) == 0) {
if (sp
->s_name
[4] > 'z') {
if (error
= nfs_renameit(dvp
, cnp
, sp
))
nfs_lookitup(sp
, &np
->n_fh
, cnp
->cn_proc
);
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
nfs_lookitup(sp
, fhp
, procp
)
register struct sillyrename
*sp
;
register struct vnode
*vp
= sp
->s_dvp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
nfsstats
.rpccnt
[NFSPROC_LOOKUP
]++;
nfsm_reqhead(vp
, NFSPROC_LOOKUP
, NFSX_FH
+NFSX_UNSIGNED
+nfsm_rndup(len
));
nfsm_strtom(sp
->s_name
, len
, NFS_MAXNAMLEN
);
nfsm_request(vp
, NFSPROC_LOOKUP
, procp
, sp
->s_cred
);
nfsm_dissect(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).
struct vop_bmap_args
*ap
;
*bnp
= bn
* btodb(vp
->v_mount
->mnt_stat
.f_iosize
);
* Strategy routine for phys. i/o
* If the biod's are running, queue a request
* otherwise just call nfs_doio() to get it done
struct vop_strategy_args
*ap
;
* 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
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
uiop
->uio_procp
= bp
->b_proc
;
* 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
;
if (VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_RDIRALOOK
)
error
= nfs_readdirlookrpc(vp
, uiop
, bp
->b_rcred
);
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_dirtyoff
= bp
->b_dirtyend
= 0;
bp
->b_resid
= uiop
->uio_resid
;
* NB Currently unsupported.
struct vop_mmap_args
*ap
;
#define fflags (ap->a_fflags)
#define cred (ap->a_cred)
* Flush all the blocks associated with a vnode.
* Walk through the buffer pool and push any dirty pages
* associated with the vnode.
struct vop_fsync_args
*ap
;
#define fflags (ap->a_fflags)
#define cred (ap->a_cred)
#define waitfor (ap->a_waitfor)
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.
struct vop_advlock_args
*ap
;
#define flags (ap->a_flags)
* Print out the contents of an nfsnode.
struct vop_print_args
*ap
;
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
);
* NFS directory offset lookup.
struct vop_blkatoff_args
*ap
;
#define offset (ap->a_offset)
* NFS flat namespace lookup.
struct vop_vget_args
*ap
;
* NFS flat namespace allocation.
struct vop_valloc_args
*ap
;
#define mode (ap->a_mode)
#define cred (ap->a_cred)
* NFS flat namespace free.
struct vop_vfree_args
*ap
;
#define mode (ap->a_mode)
struct vop_truncate_args
*ap
;
#define length (ap->a_length)
#define flags (ap->a_flags)
#define cred (ap->a_cred)
printf("nfs_truncate: need to implement!!");
struct vop_update_args
*ap
;
#define waitfor (ap->a_waitfor)
printf("nfs_update: need to implement!!");