2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * @(#)nfs_vnops.c 8.16 (Berkeley) 5/27/95
65 * FreeBSD-Id: nfs_vnops.c,v 1.72 1997/11/07 09:20:48 phk Exp $
70 * vnode op calls for Sun NFS version 2 and 3
72 #include <sys/param.h>
73 #include <sys/kernel.h>
74 #include <sys/systm.h>
75 #include <sys/resourcevar.h>
76 #include <sys/proc_internal.h>
77 #include <sys/kauth.h>
78 #include <sys/mount_internal.h>
79 #include <sys/malloc.h>
80 #include <sys/kpi_mbuf.h>
82 #include <sys/vnode_internal.h>
83 #include <sys/dirent.h>
84 #include <sys/fcntl.h>
85 #include <sys/lockf.h>
86 #include <sys/ubc_internal.h>
88 #include <sys/signalvar.h>
89 #include <sys/uio_internal.h>
91 #include <vfs/vfs_support.h>
96 #include <kern/clock.h>
97 #include <libkern/OSAtomic.h>
99 #include <miscfs/fifofs/fifo.h>
100 #include <miscfs/specfs/specdev.h>
102 #include <nfs/rpcv2.h>
103 #include <nfs/nfsproto.h>
105 #include <nfs/nfsnode.h>
106 #include <nfs/nfsmount.h>
107 #include <nfs/nfs_lock.h>
108 #include <nfs/xdr_subs.h>
109 #include <nfs/nfsm_subs.h>
112 #include <netinet/in.h>
113 #include <netinet/in_var.h>
114 #include <vm/vm_kern.h>
116 #include <kern/task.h>
117 #include <kern/sched_prim.h>
119 #include <sys/kdebug.h>
121 #define FSDBG(A, B, C, D, E) \
122 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_NONE, \
123 (int)(B), (int)(C), (int)(D), (int)(E), 0)
124 #define FSDBG_TOP(A, B, C, D, E) \
125 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_START, \
126 (int)(B), (int)(C), (int)(D), (int)(E), 0)
127 #define FSDBG_BOT(A, B, C, D, E) \
128 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_END, \
129 (int)(B), (int)(C), (int)(D), (int)(E), 0)
131 static int nfsspec_read(struct vnop_read_args
*);
132 static int nfsspec_write(struct vnop_write_args
*);
133 static int nfsfifo_read(struct vnop_read_args
*);
134 static int nfsfifo_write(struct vnop_write_args
*);
135 static int nfsspec_close(struct vnop_close_args
*);
136 static int nfsfifo_close(struct vnop_close_args
*);
137 static int nfs_ioctl(struct vnop_ioctl_args
*);
138 static int nfs_select(struct vnop_select_args
*);
139 static int nfs_setattrrpc(vnode_t
,struct vnode_attr
*,kauth_cred_t
,proc_t
);
140 static int nfs_lookup(struct vnop_lookup_args
*);
141 static int nfs_create(struct vnop_create_args
*);
142 static int nfs_mknod(struct vnop_mknod_args
*);
143 static int nfs_open(struct vnop_open_args
*);
144 static int nfs_close(struct vnop_close_args
*);
145 static int nfs_access(struct vnop_access_args
*);
146 static int nfs_vnop_getattr(struct vnop_getattr_args
*);
147 static int nfs_setattr(struct vnop_setattr_args
*);
148 static int nfs_read(struct vnop_read_args
*);
149 static int nfs_mmap(struct vnop_mmap_args
*);
150 static int nfs_fsync(struct vnop_fsync_args
*);
151 static int nfs_remove(struct vnop_remove_args
*);
152 static int nfs_link(struct vnop_link_args
*);
153 static int nfs_rename(struct vnop_rename_args
*);
154 static int nfs_mkdir(struct vnop_mkdir_args
*);
155 static int nfs_rmdir(struct vnop_rmdir_args
*);
156 static int nfs_symlink(struct vnop_symlink_args
*);
157 static int nfs_readdir(struct vnop_readdir_args
*);
158 static int nfs_lookitup(vnode_t
,char *,int,kauth_cred_t
,proc_t
,struct nfsnode
**);
159 static int nfs_sillyrename(vnode_t
,vnode_t
,struct componentname
*,kauth_cred_t
,proc_t
);
160 static int nfs_readlink(struct vnop_readlink_args
*);
161 static int nfs_pathconf(struct vnop_pathconf_args
*);
162 static int nfs_advlock(struct vnop_advlock_args
*);
163 static int nfs_pagein(struct vnop_pagein_args
*);
164 static int nfs_pageout(struct vnop_pageout_args
*);
165 static int nfs_blktooff(struct vnop_blktooff_args
*);
166 static int nfs_offtoblk(struct vnop_offtoblk_args
*);
167 static int nfs_blockmap(struct vnop_blockmap_args
*);
170 * Global vfs data structures for nfs
172 vnop_t
**nfsv2_vnodeop_p
;
173 static struct vnodeopv_entry_desc nfsv2_vnodeop_entries
[] = {
174 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
175 { &vnop_lookup_desc
, (vnop_t
*)nfs_lookup
}, /* lookup */
176 { &vnop_create_desc
, (vnop_t
*)nfs_create
}, /* create */
177 { &vnop_mknod_desc
, (vnop_t
*)nfs_mknod
}, /* mknod */
178 { &vnop_open_desc
, (vnop_t
*)nfs_open
}, /* open */
179 { &vnop_close_desc
, (vnop_t
*)nfs_close
}, /* close */
180 { &vnop_access_desc
, (vnop_t
*)nfs_access
}, /* access */
181 { &vnop_getattr_desc
, (vnop_t
*)nfs_vnop_getattr
}, /* getattr */
182 { &vnop_setattr_desc
, (vnop_t
*)nfs_setattr
}, /* setattr */
183 { &vnop_read_desc
, (vnop_t
*)nfs_read
}, /* read */
184 { &vnop_write_desc
, (vnop_t
*)nfs_write
}, /* write */
185 { &vnop_ioctl_desc
, (vnop_t
*)nfs_ioctl
}, /* ioctl */
186 { &vnop_select_desc
, (vnop_t
*)nfs_select
}, /* select */
187 { &vnop_revoke_desc
, (vnop_t
*)nfs_revoke
}, /* revoke */
188 { &vnop_mmap_desc
, (vnop_t
*)nfs_mmap
}, /* mmap */
189 { &vnop_fsync_desc
, (vnop_t
*)nfs_fsync
}, /* fsync */
190 { &vnop_remove_desc
, (vnop_t
*)nfs_remove
}, /* remove */
191 { &vnop_link_desc
, (vnop_t
*)nfs_link
}, /* link */
192 { &vnop_rename_desc
, (vnop_t
*)nfs_rename
}, /* rename */
193 { &vnop_mkdir_desc
, (vnop_t
*)nfs_mkdir
}, /* mkdir */
194 { &vnop_rmdir_desc
, (vnop_t
*)nfs_rmdir
}, /* rmdir */
195 { &vnop_symlink_desc
, (vnop_t
*)nfs_symlink
}, /* symlink */
196 { &vnop_readdir_desc
, (vnop_t
*)nfs_readdir
}, /* readdir */
197 { &vnop_readlink_desc
, (vnop_t
*)nfs_readlink
}, /* readlink */
198 { &vnop_inactive_desc
, (vnop_t
*)nfs_inactive
}, /* inactive */
199 { &vnop_reclaim_desc
, (vnop_t
*)nfs_reclaim
}, /* reclaim */
200 { &vnop_strategy_desc
, (vnop_t
*)err_strategy
}, /* strategy */
201 { &vnop_pathconf_desc
, (vnop_t
*)nfs_pathconf
}, /* pathconf */
202 { &vnop_advlock_desc
, (vnop_t
*)nfs_advlock
}, /* advlock */
203 { &vnop_bwrite_desc
, (vnop_t
*)err_bwrite
}, /* bwrite */
204 { &vnop_pagein_desc
, (vnop_t
*)nfs_pagein
}, /* Pagein */
205 { &vnop_pageout_desc
, (vnop_t
*)nfs_pageout
}, /* Pageout */
206 { &vnop_copyfile_desc
, (vnop_t
*)err_copyfile
}, /* Copyfile */
207 { &vnop_blktooff_desc
, (vnop_t
*)nfs_blktooff
}, /* blktooff */
208 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_offtoblk
}, /* offtoblk */
209 { &vnop_blockmap_desc
, (vnop_t
*)nfs_blockmap
}, /* blockmap */
212 struct vnodeopv_desc nfsv2_vnodeop_opv_desc
=
213 { &nfsv2_vnodeop_p
, nfsv2_vnodeop_entries
};
215 VNODEOP_SET(nfsv2_vnodeop_opv_desc
);
219 * Special device vnode ops
221 vnop_t
**spec_nfsv2nodeop_p
;
222 static struct vnodeopv_entry_desc spec_nfsv2nodeop_entries
[] = {
223 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
224 { &vnop_lookup_desc
, (vnop_t
*)spec_lookup
}, /* lookup */
225 { &vnop_create_desc
, (vnop_t
*)spec_create
}, /* create */
226 { &vnop_mknod_desc
, (vnop_t
*)spec_mknod
}, /* mknod */
227 { &vnop_open_desc
, (vnop_t
*)spec_open
}, /* open */
228 { &vnop_close_desc
, (vnop_t
*)nfsspec_close
}, /* close */
229 { &vnop_getattr_desc
, (vnop_t
*)nfs_vnop_getattr
}, /* getattr */
230 { &vnop_setattr_desc
, (vnop_t
*)nfs_setattr
}, /* setattr */
231 { &vnop_read_desc
, (vnop_t
*)nfsspec_read
}, /* read */
232 { &vnop_write_desc
, (vnop_t
*)nfsspec_write
}, /* write */
233 { &vnop_ioctl_desc
, (vnop_t
*)spec_ioctl
}, /* ioctl */
234 { &vnop_select_desc
, (vnop_t
*)spec_select
}, /* select */
235 { &vnop_revoke_desc
, (vnop_t
*)spec_revoke
}, /* revoke */
236 { &vnop_mmap_desc
, (vnop_t
*)spec_mmap
}, /* mmap */
237 { &vnop_fsync_desc
, (vnop_t
*)nfs_fsync
}, /* fsync */
238 { &vnop_remove_desc
, (vnop_t
*)spec_remove
}, /* remove */
239 { &vnop_link_desc
, (vnop_t
*)spec_link
}, /* link */
240 { &vnop_rename_desc
, (vnop_t
*)spec_rename
}, /* rename */
241 { &vnop_mkdir_desc
, (vnop_t
*)spec_mkdir
}, /* mkdir */
242 { &vnop_rmdir_desc
, (vnop_t
*)spec_rmdir
}, /* rmdir */
243 { &vnop_symlink_desc
, (vnop_t
*)spec_symlink
}, /* symlink */
244 { &vnop_readdir_desc
, (vnop_t
*)spec_readdir
}, /* readdir */
245 { &vnop_readlink_desc
, (vnop_t
*)spec_readlink
}, /* readlink */
246 { &vnop_inactive_desc
, (vnop_t
*)nfs_inactive
}, /* inactive */
247 { &vnop_reclaim_desc
, (vnop_t
*)nfs_reclaim
}, /* reclaim */
248 { &vnop_strategy_desc
, (vnop_t
*)spec_strategy
}, /* strategy */
249 { &vnop_pathconf_desc
, (vnop_t
*)spec_pathconf
}, /* pathconf */
250 { &vnop_advlock_desc
, (vnop_t
*)spec_advlock
}, /* advlock */
251 { &vnop_bwrite_desc
, (vnop_t
*)vn_bwrite
}, /* bwrite */
252 { &vnop_pagein_desc
, (vnop_t
*)nfs_pagein
}, /* Pagein */
253 { &vnop_pageout_desc
, (vnop_t
*)nfs_pageout
}, /* Pageout */
254 { &vnop_blktooff_desc
, (vnop_t
*)nfs_blktooff
}, /* blktooff */
255 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_offtoblk
}, /* offtoblk */
256 { &vnop_blockmap_desc
, (vnop_t
*)nfs_blockmap
}, /* blockmap */
259 struct vnodeopv_desc spec_nfsv2nodeop_opv_desc
=
260 { &spec_nfsv2nodeop_p
, spec_nfsv2nodeop_entries
};
262 VNODEOP_SET(spec_nfsv2nodeop_opv_desc
);
265 vnop_t
**fifo_nfsv2nodeop_p
;
266 static struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries
[] = {
267 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
268 { &vnop_lookup_desc
, (vnop_t
*)fifo_lookup
}, /* lookup */
269 { &vnop_create_desc
, (vnop_t
*)fifo_create
}, /* create */
270 { &vnop_mknod_desc
, (vnop_t
*)fifo_mknod
}, /* mknod */
271 { &vnop_open_desc
, (vnop_t
*)fifo_open
}, /* open */
272 { &vnop_close_desc
, (vnop_t
*)nfsfifo_close
}, /* close */
273 { &vnop_getattr_desc
, (vnop_t
*)nfs_vnop_getattr
}, /* getattr */
274 { &vnop_setattr_desc
, (vnop_t
*)nfs_setattr
}, /* setattr */
275 { &vnop_read_desc
, (vnop_t
*)nfsfifo_read
}, /* read */
276 { &vnop_write_desc
, (vnop_t
*)nfsfifo_write
}, /* write */
277 { &vnop_ioctl_desc
, (vnop_t
*)fifo_ioctl
}, /* ioctl */
278 { &vnop_select_desc
, (vnop_t
*)fifo_select
}, /* select */
279 { &vnop_revoke_desc
, (vnop_t
*)fifo_revoke
}, /* revoke */
280 { &vnop_mmap_desc
, (vnop_t
*)fifo_mmap
}, /* mmap */
281 { &vnop_fsync_desc
, (vnop_t
*)nfs_fsync
}, /* fsync */
282 { &vnop_remove_desc
, (vnop_t
*)fifo_remove
}, /* remove */
283 { &vnop_link_desc
, (vnop_t
*)fifo_link
}, /* link */
284 { &vnop_rename_desc
, (vnop_t
*)fifo_rename
}, /* rename */
285 { &vnop_mkdir_desc
, (vnop_t
*)fifo_mkdir
}, /* mkdir */
286 { &vnop_rmdir_desc
, (vnop_t
*)fifo_rmdir
}, /* rmdir */
287 { &vnop_symlink_desc
, (vnop_t
*)fifo_symlink
}, /* symlink */
288 { &vnop_readdir_desc
, (vnop_t
*)fifo_readdir
}, /* readdir */
289 { &vnop_readlink_desc
, (vnop_t
*)fifo_readlink
}, /* readlink */
290 { &vnop_inactive_desc
, (vnop_t
*)nfs_inactive
}, /* inactive */
291 { &vnop_reclaim_desc
, (vnop_t
*)nfs_reclaim
}, /* reclaim */
292 { &vnop_strategy_desc
, (vnop_t
*)fifo_strategy
}, /* strategy */
293 { &vnop_pathconf_desc
, (vnop_t
*)fifo_pathconf
}, /* pathconf */
294 { &vnop_advlock_desc
, (vnop_t
*)fifo_advlock
}, /* advlock */
295 { &vnop_bwrite_desc
, (vnop_t
*)vn_bwrite
}, /* bwrite */
296 { &vnop_pagein_desc
, (vnop_t
*)nfs_pagein
}, /* Pagein */
297 { &vnop_pageout_desc
, (vnop_t
*)nfs_pageout
}, /* Pageout */
298 { &vnop_blktooff_desc
, (vnop_t
*)nfs_blktooff
}, /* blktooff */
299 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_offtoblk
}, /* offtoblk */
300 { &vnop_blockmap_desc
, (vnop_t
*)nfs_blockmap
}, /* blockmap */
303 struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc
=
304 { &fifo_nfsv2nodeop_p
, fifo_nfsv2nodeop_entries
};
306 VNODEOP_SET(fifo_nfsv2nodeop_opv_desc
);
309 static int nfs_mknodrpc(vnode_t dvp
, vnode_t
*vpp
,
310 struct componentname
*cnp
,
311 struct vnode_attr
*vap
,
312 kauth_cred_t cred
, proc_t p
);
313 static int nfs_removerpc(vnode_t dvp
, char *name
, int namelen
,
314 kauth_cred_t cred
, proc_t proc
);
315 static int nfs_renamerpc(vnode_t fdvp
, char *fnameptr
,
316 int fnamelen
, vnode_t tdvp
,
317 char *tnameptr
, int tnamelen
,
318 kauth_cred_t cred
, proc_t proc
);
323 extern u_long nfs_xdrneg1
;
324 extern u_long nfs_true
, nfs_false
;
325 extern struct nfsstats nfsstats
;
326 extern nfstype nfsv3_type
[9];
327 proc_t nfs_iodwant
[NFS_MAXASYNCDAEMON
];
328 struct nfsmount
*nfs_iodmount
[NFS_MAXASYNCDAEMON
];
330 lck_grp_t
*nfs_iod_lck_grp
;
331 lck_grp_attr_t
*nfs_iod_lck_grp_attr
;
332 lck_attr_t
*nfs_iod_lck_attr
;
333 lck_mtx_t
*nfs_iod_mutex
;
335 int nfs_numasync
= 0;
336 int nfs_ioddelwri
= 0;
338 #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
340 static int nfsaccess_cache_timeout
= NFS_MAXATTRTIMO
;
341 /* SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW,
342 &nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout");
344 #define NFSV3ACCESS_ALL (NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY \
345 | NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE \
346 | NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP)
350 * the following are needed only by nfs_pageout to know how to handle errors
351 * see nfs_pageout comments on explanation of actions.
352 * the errors here are copied from errno.h and errors returned by servers
353 * are expected to match the same numbers here. If not, our actions maybe
356 enum actiontype
{NOACTION
, DUMP
, DUMPANDLOG
, RETRY
, RETRYWITHSLEEP
, SEVER
};
358 static int errorcount
[ELAST
+1]; /* better be zeros when initialized */
360 static const short errortooutcome
[ELAST
+1] = {
362 DUMP
, /* EPERM 1 Operation not permitted */
363 DUMP
, /* ENOENT 2 No such file or directory */
364 DUMPANDLOG
, /* ESRCH 3 No such process */
365 RETRY
, /* EINTR 4 Interrupted system call */
366 DUMP
, /* EIO 5 Input/output error */
367 DUMP
, /* ENXIO 6 Device not configured */
368 DUMPANDLOG
, /* E2BIG 7 Argument list too long */
369 DUMPANDLOG
, /* ENOEXEC 8 Exec format error */
370 DUMPANDLOG
, /* EBADF 9 Bad file descriptor */
371 DUMPANDLOG
, /* ECHILD 10 No child processes */
372 DUMPANDLOG
, /* EDEADLK 11 Resource deadlock avoided - was EAGAIN */
373 RETRY
, /* ENOMEM 12 Cannot allocate memory */
374 DUMP
, /* EACCES 13 Permission denied */
375 DUMPANDLOG
, /* EFAULT 14 Bad address */
376 DUMPANDLOG
, /* ENOTBLK 15 POSIX - Block device required */
377 RETRY
, /* EBUSY 16 Device busy */
378 DUMP
, /* EEXIST 17 File exists */
379 DUMP
, /* EXDEV 18 Cross-device link */
380 DUMP
, /* ENODEV 19 Operation not supported by device */
381 DUMP
, /* ENOTDIR 20 Not a directory */
382 DUMP
, /* EISDIR 21 Is a directory */
383 DUMP
, /* EINVAL 22 Invalid argument */
384 DUMPANDLOG
, /* ENFILE 23 Too many open files in system */
385 DUMPANDLOG
, /* EMFILE 24 Too many open files */
386 DUMPANDLOG
, /* ENOTTY 25 Inappropriate ioctl for device */
387 DUMPANDLOG
, /* ETXTBSY 26 Text file busy - POSIX */
388 DUMP
, /* EFBIG 27 File too large */
389 DUMP
, /* ENOSPC 28 No space left on device */
390 DUMPANDLOG
, /* ESPIPE 29 Illegal seek */
391 DUMP
, /* EROFS 30 Read-only file system */
392 DUMP
, /* EMLINK 31 Too many links */
393 RETRY
, /* EPIPE 32 Broken pipe */
395 DUMPANDLOG
, /* EDOM 33 Numerical argument out of domain */
396 DUMPANDLOG
, /* ERANGE 34 Result too large */
397 RETRY
, /* EAGAIN/EWOULDBLOCK 35 Resource temporarily unavailable */
398 DUMPANDLOG
, /* EINPROGRESS 36 Operation now in progress */
399 DUMPANDLOG
, /* EALREADY 37 Operation already in progress */
400 /* ipc/network software -- argument errors */
401 DUMPANDLOG
, /* ENOTSOC 38 Socket operation on non-socket */
402 DUMPANDLOG
, /* EDESTADDRREQ 39 Destination address required */
403 DUMPANDLOG
, /* EMSGSIZE 40 Message too long */
404 DUMPANDLOG
, /* EPROTOTYPE 41 Protocol wrong type for socket */
405 DUMPANDLOG
, /* ENOPROTOOPT 42 Protocol not available */
406 DUMPANDLOG
, /* EPROTONOSUPPORT 43 Protocol not supported */
407 DUMPANDLOG
, /* ESOCKTNOSUPPORT 44 Socket type not supported */
408 DUMPANDLOG
, /* ENOTSUP 45 Operation not supported */
409 DUMPANDLOG
, /* EPFNOSUPPORT 46 Protocol family not supported */
410 DUMPANDLOG
, /* EAFNOSUPPORT 47 Address family not supported by protocol family */
411 DUMPANDLOG
, /* EADDRINUSE 48 Address already in use */
412 DUMPANDLOG
, /* EADDRNOTAVAIL 49 Can't assign requested address */
413 /* ipc/network software -- operational errors */
414 RETRY
, /* ENETDOWN 50 Network is down */
415 RETRY
, /* ENETUNREACH 51 Network is unreachable */
416 RETRY
, /* ENETRESET 52 Network dropped connection on reset */
417 RETRY
, /* ECONNABORTED 53 Software caused connection abort */
418 RETRY
, /* ECONNRESET 54 Connection reset by peer */
419 RETRY
, /* ENOBUFS 55 No buffer space available */
420 RETRY
, /* EISCONN 56 Socket is already connected */
421 RETRY
, /* ENOTCONN 57 Socket is not connected */
422 RETRY
, /* ESHUTDOWN 58 Can't send after socket shutdown */
423 RETRY
, /* ETOOMANYREFS 59 Too many references: can't splice */
424 RETRY
, /* ETIMEDOUT 60 Operation timed out */
425 RETRY
, /* ECONNREFUSED 61 Connection refused */
427 DUMPANDLOG
, /* ELOOP 62 Too many levels of symbolic links */
428 DUMP
, /* ENAMETOOLONG 63 File name too long */
429 RETRY
, /* EHOSTDOWN 64 Host is down */
430 RETRY
, /* EHOSTUNREACH 65 No route to host */
431 DUMP
, /* ENOTEMPTY 66 Directory not empty */
433 DUMPANDLOG
, /* PROCLIM 67 Too many processes */
434 DUMPANDLOG
, /* EUSERS 68 Too many users */
435 DUMPANDLOG
, /* EDQUOT 69 Disc quota exceeded */
436 /* Network File System */
437 DUMP
, /* ESTALE 70 Stale NFS file handle */
438 DUMP
, /* EREMOTE 71 Too many levels of remote in path */
439 DUMPANDLOG
, /* EBADRPC 72 RPC struct is bad */
440 DUMPANDLOG
, /* ERPCMISMATCH 73 RPC version wrong */
441 DUMPANDLOG
, /* EPROGUNAVAIL 74 RPC prog. not avail */
442 DUMPANDLOG
, /* EPROGMISMATCH 75 Program version wrong */
443 DUMPANDLOG
, /* EPROCUNAVAIL 76 Bad procedure for program */
445 DUMPANDLOG
, /* ENOLCK 77 No locks available */
446 DUMPANDLOG
, /* ENOSYS 78 Function not implemented */
447 DUMPANDLOG
, /* EFTYPE 79 Inappropriate file type or format */
448 DUMPANDLOG
, /* EAUTH 80 Authentication error */
449 DUMPANDLOG
, /* ENEEDAUTH 81 Need authenticator */
450 /* Intelligent device errors */
451 DUMPANDLOG
, /* EPWROFF 82 Device power is off */
452 DUMPANDLOG
, /* EDEVERR 83 Device error, e.g. paper out */
453 DUMPANDLOG
, /* EOVERFLOW 84 Value too large to be stored in data type */
454 /* Program loading errors */
455 DUMPANDLOG
, /* EBADEXEC 85 Bad executable */
456 DUMPANDLOG
, /* EBADARCH 86 Bad CPU type in executable */
457 DUMPANDLOG
, /* ESHLIBVERS 87 Shared library version mismatch */
458 DUMPANDLOG
, /* EBADMACHO 88 Malformed Macho file */
463 nfs_pageouterrorhandler(int error
)
468 return(errortooutcome
[error
]);
472 nfs3_access_otw(vnode_t vp
,
479 int error
= 0, attrflag
;
481 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
482 caddr_t bpos
, dpos
, cp2
;
483 register long t1
, t2
;
486 struct nfsnode
*np
= VTONFS(vp
);
490 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
);
493 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_ACCESS
]);
495 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
496 *tl
= txdr_unsigned(wmode
);
497 nfsm_request(vp
, NFSPROC_ACCESS
, p
, cred
, &xid
);
499 nfsm_postop_attr_update(vp
, 1, attrflag
, &xid
);
502 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
503 rmode
= fxdr_unsigned(u_int32_t
, *tl
);
505 np
->n_modeuid
= kauth_cred_getuid(cred
);
507 np
->n_modestamp
= now
.tv_sec
;
514 * nfs access vnode op.
515 * For nfs version 2, just return ok. File accesses may fail later.
516 * For nfs version 3, use the access rpc to check accessibility. If file modes
517 * are changed on the server, accesses might still fail later.
521 struct vnop_access_args
/* {
522 struct vnodeop_desc *a_desc;
525 vfs_context_t a_context;
528 vnode_t vp
= ap
->a_vp
;
529 int error
= 0, dorpc
;
531 int v3
= NFS_ISV3(vp
);
532 struct nfsnode
*np
= VTONFS(vp
);
537 * For nfs v3, do an access rpc, otherwise you are stuck emulating
538 * ufs_access() locally using the vattr. This may not be correct,
539 * since the server may apply other access criteria such as
540 * client uid-->server uid mapping that we do not know about, but
541 * this is better than just returning anything that is lying about
546 * Convert KAUTH primitives to NFS access rights.
549 if (vnode_isdir(vp
)) {
552 (KAUTH_VNODE_LIST_DIRECTORY
|
553 KAUTH_VNODE_READ_EXTATTRIBUTES
))
554 mode
|= NFSV3ACCESS_READ
;
555 if (ap
->a_action
& KAUTH_VNODE_SEARCH
)
556 mode
|= NFSV3ACCESS_LOOKUP
;
558 (KAUTH_VNODE_ADD_FILE
|
559 KAUTH_VNODE_ADD_SUBDIRECTORY
))
560 mode
|= NFSV3ACCESS_MODIFY
| NFSV3ACCESS_EXTEND
;
561 if (ap
->a_action
& KAUTH_VNODE_DELETE_CHILD
)
562 mode
|= NFSV3ACCESS_MODIFY
;
566 (KAUTH_VNODE_READ_DATA
|
567 KAUTH_VNODE_READ_EXTATTRIBUTES
))
568 mode
|= NFSV3ACCESS_READ
;
569 if (ap
->a_action
& KAUTH_VNODE_WRITE_DATA
)
570 mode
|= NFSV3ACCESS_MODIFY
| NFSV3ACCESS_EXTEND
;
571 if (ap
->a_action
& KAUTH_VNODE_APPEND_DATA
)
572 mode
|= NFSV3ACCESS_EXTEND
;
573 if (ap
->a_action
& KAUTH_VNODE_EXECUTE
)
574 mode
|= NFSV3ACCESS_EXECUTE
;
577 if (ap
->a_action
& KAUTH_VNODE_DELETE
)
578 mode
|= NFSV3ACCESS_DELETE
;
580 (KAUTH_VNODE_WRITE_ATTRIBUTES
|
581 KAUTH_VNODE_WRITE_EXTATTRIBUTES
|
582 KAUTH_VNODE_WRITE_SECURITY
))
583 mode
|= NFSV3ACCESS_MODIFY
;
584 /* XXX this is pretty dubious */
585 if (ap
->a_action
& KAUTH_VNODE_CHANGE_OWNER
)
586 mode
|= NFSV3ACCESS_MODIFY
;
588 /* if caching, always ask for every right */
589 if (nfsaccess_cache_timeout
> 0) {
590 wmode
= NFSV3ACCESS_READ
| NFSV3ACCESS_MODIFY
|
591 NFSV3ACCESS_EXTEND
| NFSV3ACCESS_EXECUTE
|
592 NFSV3ACCESS_DELETE
| NFSV3ACCESS_LOOKUP
;
596 cred
= vfs_context_ucred(ap
->a_context
);
599 * Does our cached result allow us to give a definite yes to
603 if (NMODEVALID(np
)) {
605 if ((now
.tv_sec
< (np
->n_modestamp
+ nfsaccess_cache_timeout
)) &&
606 (kauth_cred_getuid(cred
) == np
->n_modeuid
) &&
607 ((np
->n_mode
& mode
) == mode
)) {
608 /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_hits); */
613 /* Either a no, or a don't know. Go to the wire. */
614 /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_misses); */
615 error
= nfs3_access_otw(vp
, wmode
, vfs_context_proc(ap
->a_context
), cred
);
619 * If we asked for DELETE but didn't get it, the server
620 * may simply not support returning that bit (possible
621 * on UNIX systems). So, we'll assume that it is OK,
622 * and just let any subsequent delete action fail if it
623 * really isn't deletable.
625 if ((mode
& NFSV3ACCESS_DELETE
) &&
626 !(np
->n_mode
& NFSV3ACCESS_DELETE
))
627 np
->n_mode
|= NFSV3ACCESS_DELETE
;
628 if ((np
->n_mode
& mode
) != mode
)
633 if ((ap
->a_action
& KAUTH_VNODE_WRITE_RIGHTS
) && vfs_isrdonly(vnode_mount(vp
))) {
645 * Check to see if the type is ok
646 * and that deletion is not in progress.
647 * For paged in text files, you will need to flush the page cache
648 * if consistency is lost.
654 struct vnop_open_args
/* {
655 struct vnodeop_desc *a_desc;
658 vfs_context_t a_context;
661 vnode_t vp
= ap
->a_vp
;
662 struct nfsnode
*np
= VTONFS(vp
);
663 struct nfs_vattr nvattr
;
669 vtype
= vnode_vtype(vp
);
670 if (vtype
!= VREG
&& vtype
!= VDIR
&& vtype
!= VLNK
) {
674 cred
= vfs_context_ucred(ap
->a_context
);
675 p
= vfs_context_proc(ap
->a_context
);
677 if (np
->n_flag
& NNEEDINVALIDATE
) {
678 np
->n_flag
&= ~NNEEDINVALIDATE
;
679 nfs_vinvalbuf(vp
, V_SAVE
|V_IGNORE_WRITEERR
, cred
, p
, 1);
681 if (np
->n_flag
& NMODIFIED
) {
682 if ((error
= nfs_vinvalbuf(vp
, V_SAVE
, cred
, p
, 1)) == EINTR
)
686 np
->n_direofoffset
= 0;
687 error
= nfs_getattr(vp
, &nvattr
, cred
, p
);
691 /* if directory changed, purge any name cache entries */
692 if (nfstimespeccmp(&np
->n_ncmtime
, &nvattr
.nva_mtime
, !=))
694 np
->n_ncmtime
= nvattr
.nva_mtime
;
696 np
->n_mtime
= nvattr
.nva_mtime
;
698 error
= nfs_getattr(vp
, &nvattr
, cred
, p
);
701 if (nfstimespeccmp(&np
->n_mtime
, &nvattr
.nva_mtime
, !=)) {
703 np
->n_direofoffset
= 0;
705 /* purge name cache entries */
706 if (nfstimespeccmp(&np
->n_ncmtime
, &nvattr
.nva_mtime
, !=))
709 if ((error
= nfs_vinvalbuf(vp
, V_SAVE
, cred
, p
, 1)) == EINTR
)
712 np
->n_ncmtime
= nvattr
.nva_mtime
;
713 np
->n_mtime
= nvattr
.nva_mtime
;
716 NATTRINVALIDATE(np
); /* For Open/Close consistency */
722 * What an NFS client should do upon close after writing is a debatable issue.
723 * Most NFS clients push delayed writes to the server upon close, basically for
725 * 1 - So that any write errors may be reported back to the client process
726 * doing the close system call. By far the two most likely errors are
727 * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
728 * 2 - To put a worst case upper bound on cache inconsistency between
729 * multiple clients for the file.
730 * There is also a consistency problem for Version 2 of the protocol w.r.t.
731 * not being able to tell if other clients are writing a file concurrently,
732 * since there is no way of knowing if the changed modify time in the reply
733 * is only due to the write for this client.
734 * (NFS Version 3 provides weak cache consistency data in the reply that
735 * should be sufficient to detect and handle this case.)
737 * The current code does the following:
738 * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
739 * for NFS Version 3 - flush dirty buffers to the server but don't invalidate
745 struct vnop_close_args
/* {
746 struct vnodeop_desc *a_desc;
749 vfs_context_t a_context;
752 vnode_t vp
= ap
->a_vp
;
753 struct nfsnode
*np
= VTONFS(vp
);
754 struct nfsmount
*nmp
;
759 cred
= vfs_context_ucred(ap
->a_context
);
760 p
= vfs_context_proc(ap
->a_context
);
762 if (vnode_vtype(vp
) == VREG
) {
764 register struct sillyrename
*sp
= np
->n_sillyrename
;
766 kprintf("nfs_close: %s, dvp=%x, vp=%x, ap=%x, np=%x, sp=%x\n",
767 &sp
->s_name
[0], (unsigned)(sp
->s_dvp
), (unsigned)vp
,
768 (unsigned)ap
, (unsigned)np
, (unsigned)sp
);
770 nmp
= VFSTONFS(vnode_mount(vp
));
773 if (np
->n_flag
& NNEEDINVALIDATE
) {
774 np
->n_flag
&= ~NNEEDINVALIDATE
;
775 nfs_vinvalbuf(vp
, V_SAVE
|V_IGNORE_WRITEERR
, cred
, p
, 1);
777 if (np
->n_flag
& NMODIFIED
) {
779 error
= nfs_flush(vp
, MNT_WAIT
, cred
, p
, 0);
781 * We cannot clear the NMODIFIED bit in np->n_flag due to
782 * potential races with other processes
783 * NMODIFIED is a hint
785 /* np->n_flag &= ~NMODIFIED; */
787 error
= nfs_vinvalbuf(vp
, V_SAVE
, cred
, p
, 1);
791 if (np
->n_flag
& NWRITEERR
) {
792 np
->n_flag
&= ~NWRITEERR
;
801 nfs_getattr_no_vnode(
807 struct nfs_vattr
*nvap
,
810 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
815 struct nfsmount
*nmp
= VFSTONFS(mp
);
816 int v3
= (nmp
->nm_flag
& NFSMNT_NFSV3
);
820 // XXX fix this to use macros once the macros get cleaned up
821 //nfsm_reqhead(NFSX_FH(v3));
824 if (hsiz
>= nfs_mbuf_minclsize
)
825 error
= mbuf_mclget(MBUF_WAITOK
, MBUF_TYPE_DATA
, &mb
);
827 error
= mbuf_get(MBUF_WAITOK
, MBUF_TYPE_DATA
, &mb
);
830 bpos
= mbuf_data(mb
);
832 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_GETATTR
]);
833 //nfsm_fhtom(vp, v3);
835 t2
= nfsm_rndup(fhsize
) + NFSX_UNSIGNED
;
836 if (t2
<= mbuf_trailingspace(mb
)) {
837 nfsm_build(tl
, u_long
*, t2
);
838 *tl
++ = txdr_unsigned(fhsize
);
839 *(tl
+ ((t2
>>2) - 2)) = 0;
840 bcopy((caddr_t
)fhp
,(caddr_t
)tl
, fhsize
);
841 } else if ((t2
= nfsm_strtmbuf(&mb
, &bpos
, (caddr_t
)fhp
, fhsize
))) {
847 nfsm_build(cp
, caddr_t
, NFSX_V2FH
);
848 bcopy((caddr_t
)fhp
, cp
, NFSX_V2FH
);
850 //nfsm_request(vp, NFSPROC_GETATTR, p, cred, xidp);
851 if ((error
= nfs_request(NULL
, mp
, mreq
, NFSPROC_GETATTR
, p
, cred
, &mrep
, &md
, &dpos
, xidp
))) {
852 if (error
& NFSERR_RETERR
)
853 error
&= ~NFSERR_RETERR
;
858 //nfsm_loadattr(vp, nvap, xidp);
859 error
= nfs_parsefattr(&md
, &dpos
, v3
, nvap
);
870 * nfs getattr call from vfs.
875 struct nfs_vattr
*nvap
,
879 struct nfsnode
*np
= VTONFS(vp
);
885 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
890 FSDBG_TOP(513, np
->n_size
, np
, np
->n_vattr
.nva_size
, np
->n_flag
);
893 * Update local times for special files.
895 if (np
->n_flag
& (NACC
| NUPD
))
898 * First look in the cache.
900 if ((error
= nfs_getattrcache(vp
, nvap
)) == 0) {
901 FSDBG_BOT(513, np
->n_size
, 0, np
->n_vattr
.nva_size
, np
->n_flag
);
904 if (error
!= ENOENT
) {
905 FSDBG_BOT(513, np
->n_size
, error
, np
->n_vattr
.nva_size
,
910 if (!VFSTONFS(vnode_mount(vp
))) {
911 FSDBG_BOT(513, np
->n_size
, ENXIO
, np
->n_vattr
.nva_size
, np
->n_flag
);
918 * Try to get both the attributes and access info by making an
919 * ACCESS call and seeing if it returns updated attributes.
920 * But don't bother if we aren't caching access info or if the
921 * attributes returned wouldn't be cached.
923 if (v3
&& (nfsaccess_cache_timeout
> 0) &&
924 (nfs_attrcachetimeout(vp
) > 0)) {
925 /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_misses); */
926 if ((error
= nfs3_access_otw(vp
, NFSV3ACCESS_ALL
, p
, cred
)))
928 if ((error
= nfs_getattrcache(vp
, nvap
)) == 0)
936 nfsm_reqhead(NFSX_FH(v3
));
938 FSDBG_BOT(513, np
->n_size
, error
, np
->n_vattr
.nva_size
, np
->n_flag
);
941 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_GETATTR
]);
943 nfsm_request(vp
, NFSPROC_GETATTR
, p
, cred
, &xid
);
945 nfsm_loadattr(vp
, v3
, nvap
, &xid
);
946 if (!xid
) { /* out-of-order rpc - attributes were dropped */
949 FSDBG(513, -1, np
, np
->n_xid
<< 32, np
->n_xid
);
950 if (avoidfloods
++ < 100)
953 * avoidfloods>1 is bizarre. at 100 pull the plug
955 panic("nfs_getattr: getattr flood\n");
957 if (nfstimespeccmp(&np
->n_mtime
, &nvap
->nva_mtime
, !=)) {
958 enum vtype vtype
= vnode_vtype(vp
);
959 FSDBG(513, -1, np
, -1, vp
);
962 /* purge name cache entries */
963 if (nfstimespeccmp(&np
->n_ncmtime
, &nvap
->nva_mtime
, !=))
966 error
= nfs_vinvalbuf(vp
, V_SAVE
, cred
, p
, 1);
967 FSDBG(513, -1, np
, -2, error
);
970 np
->n_ncmtime
= nvap
->nva_mtime
;
971 np
->n_mtime
= nvap
->nva_mtime
;
977 FSDBG_BOT(513, np
->n_size
, -1, np
->n_vattr
.nva_size
, error
);
984 struct vnop_getattr_args
/* {
985 struct vnodeop_desc *a_desc;
987 struct vnode_attr *a_vap;
988 vfs_context_t a_context;
992 struct nfs_vattr nva
;
993 struct vnode_attr
*vap
= ap
->a_vap
;
995 error
= nfs_getattr(ap
->a_vp
, &nva
,
996 vfs_context_ucred(ap
->a_context
),
997 vfs_context_proc(ap
->a_context
));
1001 /* copy nva to *a_vap */
1002 VATTR_RETURN(vap
, va_type
, nva
.nva_type
);
1003 VATTR_RETURN(vap
, va_mode
, nva
.nva_mode
);
1004 VATTR_RETURN(vap
, va_rdev
, nva
.nva_rdev
);
1005 VATTR_RETURN(vap
, va_uid
, nva
.nva_uid
);
1006 VATTR_RETURN(vap
, va_gid
, nva
.nva_gid
);
1007 VATTR_RETURN(vap
, va_nlink
, nva
.nva_nlink
);
1008 VATTR_RETURN(vap
, va_fileid
, nva
.nva_fileid
);
1009 VATTR_RETURN(vap
, va_data_size
, nva
.nva_size
);
1010 VATTR_RETURN(vap
, va_data_alloc
, nva
.nva_bytes
);
1011 VATTR_RETURN(vap
, va_iosize
, nva
.nva_blocksize
); /* should this just be f_iosize? */
1012 VATTR_RETURN(vap
, va_fsid
, nva
.nva_fsid
);
1013 vap
->va_access_time
.tv_sec
= nva
.nva_atime
.tv_sec
;
1014 vap
->va_access_time
.tv_nsec
= nva
.nva_atime
.tv_nsec
;
1015 VATTR_SET_SUPPORTED(vap
, va_access_time
);
1016 vap
->va_modify_time
.tv_sec
= nva
.nva_mtime
.tv_sec
;
1017 vap
->va_modify_time
.tv_nsec
= nva
.nva_mtime
.tv_nsec
;
1018 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
1019 vap
->va_change_time
.tv_sec
= nva
.nva_ctime
.tv_sec
;
1020 vap
->va_change_time
.tv_nsec
= nva
.nva_ctime
.tv_nsec
;
1021 VATTR_SET_SUPPORTED(vap
, va_change_time
);
1031 struct vnop_setattr_args
/* {
1032 struct vnodeop_desc *a_desc;
1034 struct vnode_attr *a_vap;
1035 vfs_context_t a_context;
1038 vnode_t vp
= ap
->a_vp
;
1039 struct nfsnode
*np
= VTONFS(vp
);
1040 struct nfsmount
*nmp
;
1041 struct vnode_attr
*vap
= ap
->a_vap
;
1049 tsize
= (u_quad_t
)0;
1051 nmp
= VFSTONFS(vnode_mount(vp
));
1054 biosize
= nmp
->nm_biosize
;
1056 /* Setting of flags is not supported. */
1057 if (VATTR_IS_ACTIVE(vap
, va_flags
))
1060 cred
= vfs_context_ucred(ap
->a_context
);
1061 p
= vfs_context_proc(ap
->a_context
);
1063 VATTR_SET_SUPPORTED(vap
, va_mode
);
1064 VATTR_SET_SUPPORTED(vap
, va_uid
);
1065 VATTR_SET_SUPPORTED(vap
, va_gid
);
1066 VATTR_SET_SUPPORTED(vap
, va_data_size
);
1067 VATTR_SET_SUPPORTED(vap
, va_access_time
);
1068 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
1070 /* Disallow write attempts if the filesystem is mounted read-only. */
1071 if ((VATTR_IS_ACTIVE(vap
, va_flags
) || VATTR_IS_ACTIVE(vap
, va_mode
) ||
1072 VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
) ||
1073 VATTR_IS_ACTIVE(vap
, va_access_time
) ||
1074 VATTR_IS_ACTIVE(vap
, va_modify_time
)) &&
1075 vnode_vfsisrdonly(vp
))
1078 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
1079 switch (vnode_vtype(vp
)) {
1086 if (!VATTR_IS_ACTIVE(vap
, va_modify_time
) &&
1087 !VATTR_IS_ACTIVE(vap
, va_access_time
) &&
1088 !VATTR_IS_ACTIVE(vap
, va_mode
) &&
1089 !VATTR_IS_ACTIVE(vap
, va_uid
) &&
1090 !VATTR_IS_ACTIVE(vap
, va_gid
))
1092 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
1096 * Disallow write attempts if the filesystem is
1097 * mounted read-only.
1099 if (vnode_vfsisrdonly(vp
))
1101 FSDBG_TOP(512, np
->n_size
, vap
->va_data_size
,
1102 np
->n_vattr
.nva_size
, np
->n_flag
);
1103 if (np
->n_flag
& NMODIFIED
) {
1104 if (vap
->va_data_size
== 0)
1105 error
= nfs_vinvalbuf(vp
, 0, cred
, p
, 1);
1107 error
= nfs_vinvalbuf(vp
, V_SAVE
, cred
, p
, 1);
1109 printf("nfs_setattr: nfs_vinvalbuf %d\n", error
);
1110 FSDBG_BOT(512, np
->n_size
, vap
->va_data_size
,
1111 np
->n_vattr
.nva_size
, -1);
1114 } else if (np
->n_size
> vap
->va_data_size
) { /* shrinking? */
1116 int neweofoff
, mustwrite
;
1119 obn
= (np
->n_size
- 1) / biosize
;
1120 bn
= vap
->va_data_size
/ biosize
;
1121 for ( ; obn
>= bn
; obn
--) {
1122 if (!nfs_buf_is_incore(vp
, obn
))
1124 error
= nfs_buf_get(vp
, obn
, biosize
, 0, NBLK_READ
, &bp
);
1128 FSDBG(512, bp
, bp
->nb_flags
, 0, obn
);
1129 SET(bp
->nb_flags
, NB_INVAL
);
1130 nfs_buf_release(bp
, 1);
1134 neweofoff
= vap
->va_data_size
- NBOFF(bp
);
1135 /* check for any dirty data before the new EOF */
1136 if (bp
->nb_dirtyend
&& bp
->nb_dirtyoff
< neweofoff
) {
1137 /* clip dirty range to EOF */
1138 if (bp
->nb_dirtyend
> neweofoff
)
1139 bp
->nb_dirtyend
= neweofoff
;
1142 bp
->nb_dirty
&= (1 << round_page_32(neweofoff
)/PAGE_SIZE
) - 1;
1146 FSDBG(512, bp
, bp
->nb_flags
, 0, obn
);
1147 SET(bp
->nb_flags
, NB_INVAL
);
1148 nfs_buf_release(bp
, 1);
1151 /* gotta write out dirty data before invalidating */
1152 /* (NB_STABLE indicates that data writes should be FILESYNC) */
1153 /* (NB_NOCACHE indicates buffer should be discarded) */
1154 CLR(bp
->nb_flags
, (NB_DONE
| NB_ERROR
| NB_INVAL
| NB_ASYNC
| NB_READ
));
1155 SET(bp
->nb_flags
, NB_STABLE
| NB_NOCACHE
);
1156 if (!IS_VALID_CRED(bp
->nb_wcred
)) {
1157 kauth_cred_ref(cred
);
1158 bp
->nb_wcred
= cred
;
1160 error
= nfs_buf_write(bp
);
1161 // Note: bp has been released
1163 FSDBG(512, bp
, 0xd00dee, 0xbad, error
);
1164 np
->n_error
= error
;
1165 np
->n_flag
|= NWRITEERR
;
1167 * There was a write error and we need to
1168 * invalidate attrs and flush buffers in
1169 * order to sync up with the server.
1170 * (if this write was extending the file,
1171 * we may no longer know the correct size)
1173 NATTRINVALIDATE(np
);
1174 nfs_vinvalbuf(vp
, V_SAVE
|V_IGNORE_WRITEERR
, cred
, p
, 1);
1180 np
->n_size
= np
->n_vattr
.nva_size
= vap
->va_data_size
;
1181 ubc_setsize(vp
, (off_t
)vap
->va_data_size
); /* XXX error? */
1183 } else if ((VATTR_IS_ACTIVE(vap
, va_modify_time
) ||
1184 VATTR_IS_ACTIVE(vap
, va_access_time
)) &&
1185 (np
->n_flag
& NMODIFIED
) && (vnode_vtype(vp
) == VREG
)) {
1186 error
= nfs_vinvalbuf(vp
, V_SAVE
, cred
, p
, 1);
1190 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
1191 NMODEINVALIDATE(np
);
1193 error
= nfs_setattrrpc(vp
, vap
, cred
, p
);
1194 FSDBG_BOT(512, np
->n_size
, vap
->va_data_size
, np
->n_vattr
.nva_size
, error
);
1195 if (error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
1196 /* make every effort to resync file size w/ server... */
1197 int err
; /* preserve "error" for return */
1199 np
->n_size
= np
->n_vattr
.nva_size
= tsize
;
1200 ubc_setsize(vp
, (off_t
)np
->n_size
); /* XXX check error */
1201 vap
->va_data_size
= tsize
;
1202 err
= nfs_setattrrpc(vp
, vap
, cred
, p
);
1203 printf("nfs_setattr: nfs_setattrrpc %d %d\n", error
, err
);
1209 * Do an nfs setattr rpc.
1212 nfs_setattrrpc(vp
, vap
, cred
, procp
)
1214 struct vnode_attr
*vap
;
1218 register struct nfsv2_sattr
*sp
;
1219 register caddr_t cp
;
1220 register long t1
, t2
;
1221 caddr_t bpos
, dpos
, cp2
;
1223 int error
= 0, wccpostattr
= 0;
1224 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
1229 if (!VFSTONFS(vnode_mount(vp
)))
1233 nfsm_reqhead(NFSX_FH(v3
) + NFSX_SATTR(v3
));
1236 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_SETATTR
]);
1239 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
1240 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
1242 *tl
= txdr_unsigned(vap
->va_mode
);
1244 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1247 if (VATTR_IS_ACTIVE(vap
, va_uid
)) {
1248 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
1250 *tl
= txdr_unsigned(vap
->va_uid
);
1252 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1255 if (VATTR_IS_ACTIVE(vap
, va_gid
)) {
1256 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
1258 *tl
= txdr_unsigned(vap
->va_gid
);
1260 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1263 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
1264 nfsm_build(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
1266 txdr_hyper(&vap
->va_data_size
, tl
);
1268 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1272 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
1273 if (vap
->va_access_time
.tv_sec
!= now
.tv_sec
) {
1274 nfsm_build(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
1275 *tl
++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT
);
1276 txdr_nfsv3time(&vap
->va_access_time
, tl
);
1278 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1279 *tl
= txdr_unsigned(NFSV3SATTRTIME_TOSERVER
);
1282 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1283 *tl
= txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE
);
1285 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
1286 if (vap
->va_modify_time
.tv_sec
!= now
.tv_sec
) {
1287 nfsm_build(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
1288 *tl
++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT
);
1289 txdr_nfsv3time(&vap
->va_modify_time
, tl
);
1291 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1292 *tl
= txdr_unsigned(NFSV3SATTRTIME_TOSERVER
);
1295 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1296 *tl
= txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE
);
1298 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1301 struct timespec neg1time
= { -1, -1 };
1302 nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
1303 if (VATTR_IS_ACTIVE(vap
, va_mode
))
1304 sp
->sa_mode
= vtonfsv2_mode(vnode_vtype(vp
), vap
->va_mode
);
1306 sp
->sa_mode
= nfs_xdrneg1
;
1307 if (VATTR_IS_ACTIVE(vap
, va_uid
))
1308 sp
->sa_uid
= txdr_unsigned(vap
->va_uid
);
1310 sp
->sa_uid
= nfs_xdrneg1
;
1311 if (VATTR_IS_ACTIVE(vap
, va_gid
))
1312 sp
->sa_gid
= txdr_unsigned(vap
->va_gid
);
1314 sp
->sa_gid
= nfs_xdrneg1
;
1315 if (VATTR_IS_ACTIVE(vap
, va_data_size
))
1316 sp
->sa_size
= txdr_unsigned(vap
->va_data_size
);
1318 sp
->sa_size
= nfs_xdrneg1
;
1319 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
1320 txdr_nfsv2time(&vap
->va_access_time
, &sp
->sa_atime
);
1322 txdr_nfsv2time(&neg1time
, &sp
->sa_atime
);
1324 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
1325 txdr_nfsv2time(&vap
->va_modify_time
, &sp
->sa_mtime
);
1327 txdr_nfsv2time(&neg1time
, &sp
->sa_mtime
);
1330 nfsm_request(vp
, NFSPROC_SETATTR
, procp
, cred
, &xid
);
1332 struct timespec premtime
= { 0, 0 };
1334 nfsm_wcc_data(vp
, &premtime
, wccpostattr
, &xid
);
1336 /* if file hadn't changed, update cached mtime */
1337 if (nfstimespeccmp(&VTONFS(vp
)->n_mtime
, &premtime
, ==)) {
1338 VTONFS(vp
)->n_mtime
= VTONFS(vp
)->n_vattr
.nva_mtime
;
1340 /* if directory hadn't changed, update namecache mtime */
1341 if ((vnode_vtype(vp
) == VDIR
) &&
1342 nfstimespeccmp(&VTONFS(vp
)->n_ncmtime
, &premtime
, ==)) {
1343 VTONFS(vp
)->n_ncmtime
= VTONFS(vp
)->n_vattr
.nva_mtime
;
1346 NATTRINVALIDATE(VTONFS(vp
));
1349 nfsm_loadattr(vp
, v3
, NULL
, &xid
);
1357 * nfs lookup call, one step at a time...
1358 * First look in cache
1359 * If not found, unlock the directory nfsnode and do the rpc
1363 struct vnop_lookup_args
/* {
1364 struct vnodeop_desc *a_desc;
1367 struct componentname *a_cnp;
1368 vfs_context_t a_context;
1371 struct componentname
*cnp
= ap
->a_cnp
;
1372 vnode_t dvp
= ap
->a_dvp
;
1373 vnode_t
*vpp
= ap
->a_vpp
;
1374 int flags
= cnp
->cn_flags
;
1379 caddr_t bpos
, dpos
, cp2
;
1380 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
1383 struct nfsnode
*dnp
, *np
;
1384 int wantparent
, error
, attrflag
, dattrflag
, fhsize
, fhisdvp
;
1385 int v3
= NFS_ISV3(dvp
);
1386 u_int64_t xid
, dxid
;
1387 struct nfs_vattr nvattr
;
1394 cred
= vfs_context_ucred(ap
->a_context
);
1395 p
= vfs_context_proc(ap
->a_context
);
1397 wantparent
= flags
& (LOCKPARENT
|WANTPARENT
);
1400 error
= nfs_getattr(dvp
, &nvattr
, cred
, p
);
1403 if (nfstimespeccmp(&dnp
->n_ncmtime
, &nvattr
.nva_mtime
, !=)) {
1405 * This directory has changed on us.
1406 * Purge any name cache entries.
1409 dnp
->n_ncmtime
= nvattr
.nva_mtime
;
1412 error
= cache_lookup(dvp
, vpp
, cnp
);
1415 /* negative cache entry same as cache miss */
1422 /* cache hit, not really an error */
1424 struct vnop_access_args naa
;
1426 OSAddAtomic(1, (SInt32
*)&nfsstats
.lookupcache_hits
);
1428 /* check for directory access */
1430 naa
.a_action
= KAUTH_VNODE_SEARCH
;
1431 naa
.a_context
= ap
->a_context
;
1433 /* compute actual success/failure based on accessibility */
1434 error
= nfs_access(&naa
);
1438 /* unexpected error from cache_lookup */
1442 /* check for lookup of "." */
1443 if ((cnp
->cn_nameptr
[0] == '.') && (cnp
->cn_namelen
== 1)) {
1444 /* skip lookup, we know who we are */
1452 /* do we know this name is too long? */
1454 /* For NFSv3: need uniform pathconf info to test pc_namemax */
1455 struct nfsmount
*nmp
= VFSTONFS(vnode_mount(dvp
));
1460 if (((nmp
->nm_state
& (NFSSTA_GOTFSINFO
|NFSSTA_GOTPATHCONF
)) ==
1461 (NFSSTA_GOTFSINFO
|NFSSTA_GOTPATHCONF
)) &&
1462 (nmp
->nm_fsinfo
.fsproperties
& NFSV3FSINFO_HOMOGENEOUS
) &&
1463 (cnp
->cn_namelen
> (long)nmp
->nm_fsinfo
.namemax
)) {
1464 error
= ENAMETOOLONG
;
1467 } else if (cnp
->cn_namelen
> NFS_MAXNAMLEN
) {
1468 error
= ENAMETOOLONG
;
1475 OSAddAtomic(1, (SInt32
*)&nfsstats
.lookupcache_misses
);
1476 len
= cnp
->cn_namelen
;
1477 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
+ nfsm_rndup(len
));
1480 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_LOOKUP
]);
1481 nfsm_fhtom(dvp
, v3
);
1482 nfsm_strtom(cnp
->cn_nameptr
, len
, NFS_MAXNAMLEN
, v3
);
1483 /* nfsm_request for NFSv2 causes you to goto to nfsmout upon errors */
1484 nfsm_request(dvp
, NFSPROC_LOOKUP
, p
, cred
, &xid
);
1488 nfsm_postop_attr_update(dvp
, v3
, dattrflag
, &xid
);
1494 /* get the filehandle */
1495 nfsm_getfh(fhp
, fhsize
, v3
);
1496 /* is the file handle the same as this directory's file handle? */
1497 fhisdvp
= NFS_CMPFH(dnp
, fhp
, fhsize
);
1499 /* get attributes */
1502 nfsm_postop_attr_get(v3
, attrflag
, &nvattr
);
1503 nfsm_postop_attr_update(dvp
, v3
, dattrflag
, &dxid
);
1504 if (!attrflag
&& (!fhisdvp
|| !dattrflag
)) {
1505 /* We need valid attributes in order */
1506 /* to call nfs_nget/vnode_create(). */
1507 error
= nfs_getattr_no_vnode(vnode_mount(dvp
),
1508 fhp
, fhsize
, cred
, p
, &nvattr
, &xid
);
1515 nfsm_attr_get(v3
, &nvattr
);
1521 * Handle RENAME case...
1523 if (cnp
->cn_nameiop
== RENAME
&& wantparent
&& (flags
& ISLASTCN
)) {
1529 error
= nfs_nget(vnode_mount(dvp
), dvp
, cnp
, fhp
, fhsize
,
1530 &nvattr
, &xid
, 0, &np
);
1541 if ((cnp
->cn_flags
& MAKEENTRY
) &&
1542 (cnp
->cn_nameiop
!= DELETE
|| !(flags
& ISLASTCN
)))
1543 ngflags
= NG_MAKEENTRY
;
1548 error
= vnode_get(dvp
);
1554 /* test fhp to see if we have valid attributes in nvattr */
1555 if (fhp
&& (dnp
->n_xid
<= xid
)) {
1556 error
= nfs_loadattrcache(dnp
, &nvattr
, &xid
, 0);
1564 error
= nfs_nget(vnode_mount(dvp
), dvp
, cnp
, fhp
, fhsize
,
1565 &nvattr
, &xid
, ngflags
, &np
);
1573 // if (error == 0 && *vpp != NULL && *vpp != dvp)
1574 // nfs_unlock(VTONFS(*vpp));
1578 if ((cnp
->cn_nameiop
== CREATE
|| cnp
->cn_nameiop
== RENAME
) &&
1579 (flags
& ISLASTCN
) && error
== ENOENT
) {
1580 if (vnode_mount(dvp
) && vnode_vfsisrdonly(dvp
))
1583 error
= EJUSTRETURN
;
1587 if (error
&& *vpp
) {
1596 * Just call nfs_bioread() to do the work.
1600 struct vnop_read_args
/* {
1601 struct vnodeop_desc *a_desc;
1605 vfs_context_t a_context;
1608 if (vnode_vtype(ap
->a_vp
) != VREG
)
1610 return (nfs_bioread(ap
->a_vp
, ap
->a_uio
, ap
->a_ioflag
,
1611 vfs_context_ucred(ap
->a_context
),
1612 vfs_context_proc(ap
->a_context
)));
1621 struct vnop_readlink_args
/* {
1622 struct vnodeop_desc *a_desc;
1625 vfs_context_t a_context;
1628 if (vnode_vtype(ap
->a_vp
) != VLNK
)
1630 return (nfs_bioread(ap
->a_vp
, ap
->a_uio
, 0,
1631 vfs_context_ucred(ap
->a_context
),
1632 vfs_context_proc(ap
->a_context
)));
1636 * Do a readlink rpc.
1637 * Called by nfs_doio() from below the buffer cache.
1646 register u_long
*tl
;
1647 register caddr_t cp
;
1648 register long t1
, t2
;
1649 caddr_t bpos
, dpos
, cp2
;
1650 int error
= 0, len
, attrflag
;
1651 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
1655 if (!VFSTONFS(vnode_mount(vp
)))
1659 nfsm_reqhead(NFSX_FH(v3
));
1662 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_READLINK
]);
1664 nfsm_request(vp
, NFSPROC_READLINK
, p
, cred
, &xid
);
1666 nfsm_postop_attr_update(vp
, v3
, attrflag
, &xid
);
1668 nfsm_strsiz(len
, NFS_MAXPATHLEN
, v3
);
1669 if (len
>= NFS_MAXPATHLEN
) {
1670 struct nfsnode
*np
= VTONFS(vp
);
1673 panic("nfs_readlinkrpc: null np");
1675 if (np
->n_size
&& np
->n_size
< NFS_MAXPATHLEN
)
1678 nfsm_mtouio(uiop
, len
);
1695 register u_long
*tl
;
1696 register caddr_t cp
;
1697 register long t1
, t2
;
1698 caddr_t bpos
, dpos
, cp2
;
1699 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
1700 struct nfsmount
*nmp
;
1701 int error
= 0, len
, retlen
, tsiz
, eof
= 0, attrflag
;
1705 FSDBG_TOP(536, vp
, uiop
->uio_offset
, uio_uio_resid(uiop
), 0);
1706 nmp
= VFSTONFS(vnode_mount(vp
));
1710 nmrsize
= nmp
->nm_rsize
;
1712 // LP64todo - fix this
1713 tsiz
= uio_uio_resid(uiop
);
1714 if (((u_int64_t
)uiop
->uio_offset
+ (unsigned int)tsiz
> 0xffffffff) && !v3
) {
1715 FSDBG_BOT(536, vp
, uiop
->uio_offset
, uio_uio_resid(uiop
), EFBIG
);
1719 len
= (tsiz
> nmrsize
) ? nmrsize
: tsiz
;
1720 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
* 3);
1723 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_READ
]);
1725 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
* 3);
1727 txdr_hyper(&uiop
->uio_offset
, tl
);
1728 *(tl
+ 2) = txdr_unsigned(len
);
1730 *tl
++ = txdr_unsigned(uiop
->uio_offset
);
1731 *tl
++ = txdr_unsigned(len
);
1734 FSDBG(536, vp
, uiop
->uio_offset
, len
, 0);
1735 nfsm_request(vp
, NFSPROC_READ
, p
, cred
, &xid
);
1738 nfsm_postop_attr_update(vp
, v3
, attrflag
, &xid
);
1744 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
1745 eof
= fxdr_unsigned(int, *(tl
+ 1));
1748 nfsm_loadattr(vp
, v3
, NULL
, &xid
);
1752 nfsm_strsiz(retlen
, nmrsize
, 0);
1753 nfsm_mtouio(uiop
, retlen
);
1760 if (eof
|| retlen
== 0)
1762 } else if (retlen
< len
)
1766 FSDBG_BOT(536, vp
, eof
, uio_uio_resid(uiop
), error
);
1782 register u_long
*tl
;
1783 register caddr_t cp
;
1784 register int t1
, t2
, backup
;
1785 caddr_t bpos
, dpos
, cp2
;
1786 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
1787 struct nfsmount
*nmp
;
1788 int error
= 0, len
, tsiz
, updatemtime
= 0, wccpostattr
= 0, rlen
, commit
;
1789 int v3
, committed
= NFSV3WRITE_FILESYNC
;
1790 u_int64_t xid
, wverf
;
1794 if (uiop
->uio_iovcnt
!= 1)
1795 panic("nfs_writerpc: iovcnt > 1");
1797 FSDBG_TOP(537, vp
, uiop
->uio_offset
, uio_uio_resid(uiop
), *iomode
);
1798 nmp
= VFSTONFS(vnode_mount(vp
));
1802 // LP64todo - fix this
1803 tsiz
= uio_uio_resid(uiop
);
1804 if (((u_int64_t
)uiop
->uio_offset
+ (unsigned int)tsiz
> 0xffffffff) && !v3
) {
1805 FSDBG_BOT(537, vp
, uiop
->uio_offset
, uio_uio_resid(uiop
), EFBIG
);
1809 nmp
= VFSTONFS(vnode_mount(vp
));
1814 len
= (tsiz
> nmp
->nm_wsize
) ? nmp
->nm_wsize
: tsiz
;
1815 nfsm_reqhead(NFSX_FH(v3
) + 5 * NFSX_UNSIGNED
+ nfsm_rndup(len
));
1818 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_WRITE
]);
1821 nfsm_build(tl
, u_long
*, 5 * NFSX_UNSIGNED
);
1822 txdr_hyper(&uiop
->uio_offset
, tl
);
1824 *tl
++ = txdr_unsigned(len
);
1825 *tl
++ = txdr_unsigned(*iomode
);
1827 nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
1828 *++tl
= txdr_unsigned(uiop
->uio_offset
);
1831 *tl
= txdr_unsigned(len
);
1832 FSDBG(537, vp
, uiop
->uio_offset
, len
, 0);
1833 nfsm_uiotom(uiop
, len
);
1834 nfsm_request(vp
, NFSPROC_WRITE
, p
, cred
, &xid
);
1835 nmp
= VFSTONFS(vnode_mount(vp
));
1840 struct timespec premtime
;
1841 nfsm_wcc_data(vp
, &premtime
, wccpostattr
, &xid
);
1842 if (nfstimespeccmp(&VTONFS(vp
)->n_mtime
, &premtime
, ==))
1846 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
+
1848 rlen
= fxdr_unsigned(int, *tl
++);
1852 } else if (rlen
< len
) {
1853 backup
= len
- rlen
;
1854 uio_iov_base_add(uiop
, -backup
);
1855 uio_iov_len_add(uiop
, backup
);
1856 uiop
->uio_offset
-= backup
;
1857 uio_uio_resid_add(uiop
, backup
);
1860 commit
= fxdr_unsigned(int, *tl
++);
1863 * Return the lowest committment level
1864 * obtained by any of the RPCs.
1866 if (committed
== NFSV3WRITE_FILESYNC
)
1868 else if (committed
== NFSV3WRITE_DATASYNC
&&
1869 commit
== NFSV3WRITE_UNSTABLE
)
1871 fxdr_hyper(tl
, &wverf
);
1874 if ((nmp
->nm_state
& NFSSTA_HASWRITEVERF
) == 0) {
1875 nmp
->nm_verf
= wverf
;
1876 nmp
->nm_state
|= NFSSTA_HASWRITEVERF
;
1877 } else if (wverf
!= nmp
->nm_verf
) {
1878 nmp
->nm_verf
= wverf
;
1883 nfsm_loadattr(vp
, v3
, NULL
, &xid
);
1888 VTONFS(vp
)->n_mtime
= VTONFS(vp
)->n_vattr
.nva_mtime
;
1891 * we seem to have a case where we end up looping on shutdown
1892 * and taking down nfs servers. For V3, error cases, there is
1893 * no way to terminate loop, if the len was 0, meaning,
1894 * nmp->nm_wsize was trashed. FreeBSD has this fix in it.
1902 if ((mp
= vnode_mount(vp
)) && (vfs_flags(mp
) & MNT_ASYNC
))
1903 committed
= NFSV3WRITE_FILESYNC
;
1904 *iomode
= committed
;
1906 uio_uio_resid_set(uiop
, tsiz
);
1907 FSDBG_BOT(537, vp
, committed
, uio_uio_resid(uiop
), error
);
1913 * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1914 * mode set to specify the file type and the size field for rdev.
1920 struct componentname
*cnp
,
1921 struct vnode_attr
*vap
,
1925 register struct nfsv2_sattr
*sp
;
1926 register u_long
*tl
;
1927 register caddr_t cp
;
1928 register long t1
, t2
;
1929 vnode_t newvp
= (vnode_t
)0;
1930 struct nfsnode
*np
= (struct nfsnode
*)0;
1931 struct nfs_vattr nvattr
;
1934 int error
= 0, wccpostattr
= 0, gotvp
= 0;
1935 struct timespec premtime
= { 0, 0 };
1936 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
1939 int v3
= NFS_ISV3(dvp
);
1942 if (!VATTR_IS_ACTIVE(vap
, va_type
))
1944 if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
) {
1945 if (!VATTR_IS_ACTIVE(vap
, va_rdev
))
1947 rdev
= txdr_unsigned(vap
->va_rdev
);
1948 } else if (vap
->va_type
== VFIFO
|| vap
->va_type
== VSOCK
)
1953 nfsm_reqhead(NFSX_FH(v3
) + 4 * NFSX_UNSIGNED
+
1954 nfsm_rndup(cnp
->cn_namelen
) + NFSX_SATTR(v3
));
1958 VATTR_SET_SUPPORTED(vap
, va_mode
);
1959 VATTR_SET_SUPPORTED(vap
, va_uid
);
1960 VATTR_SET_SUPPORTED(vap
, va_gid
);
1961 VATTR_SET_SUPPORTED(vap
, va_data_size
);
1962 VATTR_SET_SUPPORTED(vap
, va_access_time
);
1963 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
1964 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
1965 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
1967 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_MKNOD
]);
1968 nfsm_fhtom(dvp
, v3
);
1969 nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
, v3
);
1971 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
1972 *tl
++ = vtonfsv3_type(vap
->va_type
);
1974 if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
) {
1975 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
1976 *tl
++ = txdr_unsigned(major(vap
->va_rdev
));
1977 *tl
= txdr_unsigned(minor(vap
->va_rdev
));
1980 struct timespec neg1time
= { -1, -1 };
1981 nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
1982 sp
->sa_mode
= vtonfsv2_mode(vap
->va_type
,
1983 (VATTR_IS_ACTIVE(vap
, va_mode
) ? vap
->va_mode
: 0600));
1984 sp
->sa_uid
= gotuid
? (u_long
)txdr_unsigned(vap
->va_uid
) : nfs_xdrneg1
;
1985 sp
->sa_gid
= gotgid
? (u_long
)txdr_unsigned(vap
->va_gid
) : nfs_xdrneg1
;
1987 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
1988 txdr_nfsv2time(&vap
->va_access_time
, &sp
->sa_atime
);
1990 txdr_nfsv2time(&neg1time
, &sp
->sa_atime
);
1992 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
1993 txdr_nfsv2time(&vap
->va_modify_time
, &sp
->sa_mtime
);
1995 txdr_nfsv2time(&neg1time
, &sp
->sa_mtime
);
1998 nfsm_request(dvp
, NFSPROC_MKNOD
, p
, cred
, &xid
);
1999 /* XXX no EEXIST kludge here? */
2001 nfsm_mtofh(dvp
, cnp
, newvp
, v3
, &xid
, gotvp
);
2003 error
= nfs_lookitup(dvp
, cnp
->cn_nameptr
,
2004 cnp
->cn_namelen
, cred
, p
, &np
);
2010 nfsm_wcc_data(dvp
, &premtime
, wccpostattr
, &xid
);
2011 if (!error
&& (gotuid
|| gotgid
) &&
2012 (!newvp
|| nfs_getattrcache(newvp
, &nvattr
) ||
2013 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
2014 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
2015 /* clear ID bits if server didn't use them (or we can't tell) */
2016 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
2017 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
2026 VTONFS(dvp
)->n_flag
|= NMODIFIED
;
2027 /* if directory hadn't changed, update namecache mtime */
2028 if (nfstimespeccmp(&VTONFS(dvp
)->n_ncmtime
, &premtime
, ==))
2029 VTONFS(dvp
)->n_ncmtime
= VTONFS(dvp
)->n_vattr
.nva_mtime
;
2031 NATTRINVALIDATE(VTONFS(dvp
));
2037 * just call nfs_mknodrpc() to do the work.
2042 struct vnop_mknod_args
/* {
2043 struct vnodeop_desc *a_desc;
2046 struct componentname *a_cnp;
2047 struct vnode_attr *a_vap;
2048 vfs_context_t a_context;
2053 error
= nfs_mknodrpc(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
,
2054 vfs_context_ucred(ap
->a_context
),
2055 vfs_context_proc(ap
->a_context
));
2060 static u_long create_verf
;
2062 * nfs file create call
2066 struct vnop_create_args
/* {
2067 struct vnodeop_desc *a_desc;
2070 struct componentname *a_cnp;
2071 struct vnode_attr *a_vap;
2072 vfs_context_t a_context;
2075 vnode_t dvp
= ap
->a_dvp
;
2076 struct vnode_attr
*vap
= ap
->a_vap
;
2077 struct componentname
*cnp
= ap
->a_cnp
;
2078 struct nfs_vattr nvattr
;
2079 struct nfsv2_sattr
*sp
;
2083 struct nfsnode
*np
= (struct nfsnode
*)0;
2084 vnode_t newvp
= (vnode_t
)0;
2085 caddr_t bpos
, dpos
, cp2
;
2086 int error
= 0, wccpostattr
= 0, gotvp
= 0, fmode
= 0;
2087 struct timespec premtime
= { 0, 0 };
2088 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2089 int v3
= NFS_ISV3(dvp
);
2095 cred
= vfs_context_ucred(ap
->a_context
);
2096 p
= vfs_context_proc(ap
->a_context
);
2098 if (!VATTR_IS_ACTIVE(vap
, va_type
))
2102 * Oops, not for me..
2104 if (vap
->va_type
== VSOCK
)
2105 return (nfs_mknodrpc(dvp
, ap
->a_vpp
, cnp
, vap
, cred
, p
));
2107 VATTR_SET_SUPPORTED(vap
, va_mode
);
2108 VATTR_SET_SUPPORTED(vap
, va_uid
);
2109 VATTR_SET_SUPPORTED(vap
, va_gid
);
2110 VATTR_SET_SUPPORTED(vap
, va_data_size
);
2111 VATTR_SET_SUPPORTED(vap
, va_access_time
);
2112 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
2113 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
2114 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
2116 if (vap
->va_vaflags
& VA_EXCLUSIVE
)
2119 nfsm_reqhead(NFSX_FH(v3
) + 2 * NFSX_UNSIGNED
+
2120 nfsm_rndup(cnp
->cn_namelen
) + NFSX_SATTR(v3
));
2123 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_CREATE
]);
2124 nfsm_fhtom(dvp
, v3
);
2125 nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
, v3
);
2127 nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
2128 if (fmode
& O_EXCL
) {
2129 *tl
= txdr_unsigned(NFSV3CREATE_EXCLUSIVE
);
2130 nfsm_build(tl
, u_long
*, NFSX_V3CREATEVERF
);
2131 if (!TAILQ_EMPTY(&in_ifaddrhead
))
2132 *tl
++ = IA_SIN(in_ifaddrhead
.tqh_first
)->sin_addr
.s_addr
;
2134 *tl
++ = create_verf
;
2135 *tl
= ++create_verf
;
2137 *tl
= txdr_unsigned(NFSV3CREATE_UNCHECKED
);
2141 struct timespec neg1time
= { -1, -1 };
2142 nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
2143 sp
->sa_mode
= vtonfsv2_mode(vap
->va_type
,
2144 (VATTR_IS_ACTIVE(vap
, va_mode
) ? vap
->va_mode
: 0600));
2145 sp
->sa_uid
= gotuid
? (u_long
)txdr_unsigned(vap
->va_uid
) : nfs_xdrneg1
;
2146 sp
->sa_gid
= gotgid
? (u_long
)txdr_unsigned(vap
->va_gid
) : nfs_xdrneg1
;
2148 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
2149 txdr_nfsv2time(&vap
->va_access_time
, &sp
->sa_atime
);
2151 txdr_nfsv2time(&neg1time
, &sp
->sa_atime
);
2153 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
2154 txdr_nfsv2time(&vap
->va_modify_time
, &sp
->sa_mtime
);
2156 txdr_nfsv2time(&neg1time
, &sp
->sa_mtime
);
2159 nfsm_request(dvp
, NFSPROC_CREATE
, p
, cred
, &xid
);
2161 nfsm_mtofh(dvp
, cnp
, newvp
, v3
, &xid
, gotvp
);
2163 error
= nfs_lookitup(dvp
, cnp
->cn_nameptr
,
2164 cnp
->cn_namelen
, cred
, p
, &np
);
2170 nfsm_wcc_data(dvp
, &premtime
, wccpostattr
, &xid
);
2173 if (v3
&& (fmode
& O_EXCL
) && error
== NFSERR_NOTSUPP
) {
2179 } else if (v3
&& (fmode
& O_EXCL
)) {
2180 error
= nfs_setattrrpc(newvp
, vap
, cred
, p
);
2181 if (error
&& (gotuid
|| gotgid
)) {
2182 /* it's possible the server didn't like our attempt to set IDs. */
2183 /* so, let's try it again without those */
2184 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2185 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2186 error
= nfs_setattrrpc(newvp
, vap
, cred
, p
);
2194 VTONFS(dvp
)->n_flag
|= NMODIFIED
;
2195 /* if directory hadn't changed, update namecache mtime */
2196 if (nfstimespeccmp(&VTONFS(dvp
)->n_ncmtime
, &premtime
, ==))
2197 VTONFS(dvp
)->n_ncmtime
= VTONFS(dvp
)->n_vattr
.nva_mtime
;
2199 NATTRINVALIDATE(VTONFS(dvp
));
2200 if (!error
&& (gotuid
|| gotgid
) &&
2201 (!newvp
|| nfs_getattrcache(newvp
, &nvattr
) ||
2202 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
2203 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
2204 /* clear ID bits if server didn't use them (or we can't tell) */
2205 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
2206 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
2212 * nfs file remove call
2213 * To try and make nfs semantics closer to ufs semantics, a file that has
2214 * other processes using the vnode is renamed instead of removed and then
2215 * removed later on the last close.
2216 * - If vnode_isinuse()
2217 * If a rename is not already in the works
2218 * call nfs_sillyrename() to set it up
2224 struct vnop_remove_args
/* {
2225 struct vnodeop_desc *a_desc;
2228 struct componentname *a_cnp;
2230 vfs_context_t a_context;
2233 vnode_t vp
= ap
->a_vp
;
2234 vnode_t dvp
= ap
->a_dvp
;
2235 struct componentname
*cnp
= ap
->a_cnp
;
2236 struct nfsnode
*np
= VTONFS(vp
);
2237 int error
= 0, gofree
= 0;
2238 struct nfs_vattr nvattr
;
2242 cred
= vfs_context_ucred(ap
->a_context
);
2243 p
= vfs_context_proc(ap
->a_context
);
2245 gofree
= vnode_isinuse(vp
, 0) ? 0 : 1;
2246 if ((ap
->a_flags
& VNODE_REMOVE_NODELETEBUSY
) && !gofree
) {
2247 /* Caller requested Carbon delete semantics, but file is busy */
2250 if (gofree
|| (np
->n_sillyrename
&&
2251 nfs_getattr(vp
, &nvattr
, cred
, p
) == 0 &&
2252 nvattr
.nva_nlink
> 1)) {
2254 * Purge the name cache so that the chance of a lookup for
2255 * the name succeeding while the remove is in progress is
2260 * throw away biocache buffers, mainly to avoid
2261 * unnecessary delayed writes later.
2263 error
= nfs_vinvalbuf(vp
, 0, cred
, p
, 1);
2265 ubc_setsize(vp
, (off_t
)0); /* XXX check error */
2268 error
= nfs_removerpc(dvp
, cnp
->cn_nameptr
,
2269 cnp
->cn_namelen
, cred
, p
);
2271 * Kludge City: If the first reply to the remove rpc is lost..
2272 * the reply to the retransmitted request will be ENOENT
2273 * since the file was in fact removed
2274 * Therefore, we cheat and return success.
2276 if (error
== ENOENT
)
2280 * remove nfsnode from hash now so we can't accidentally find it
2281 * again if another object gets created with the same filehandle
2282 * before this vnode gets reclaimed
2284 lck_mtx_lock(nfs_node_hash_mutex
);
2285 LIST_REMOVE(np
, n_hash
);
2286 np
->n_flag
&= ~NHASHED
;
2287 lck_mtx_unlock(nfs_node_hash_mutex
);
2289 if (!error
&& !np
->n_sillyrename
) {
2290 /* clear flags now: won't get nfs_inactive for recycled vnode */
2291 /* clear all flags other than these */
2292 np
->n_flag
&= (NMODIFIED
| NFLUSHINPROG
| NFLUSHWANT
| NHASHED
);
2295 } else if (!np
->n_sillyrename
) {
2296 error
= nfs_sillyrename(dvp
, vp
, cnp
, cred
, p
);
2298 NATTRINVALIDATE(np
);
2304 * nfs file remove rpc called from nfs_inactive
2307 nfs_removeit(struct sillyrename
*sp
)
2309 return (nfs_removerpc(sp
->s_dvp
, sp
->s_name
, sp
->s_namlen
, sp
->s_cred
, NULL
));
2313 * Nfs remove rpc, called from nfs_remove() and nfs_removeit().
2316 nfs_removerpc(dvp
, name
, namelen
, cred
, proc
)
2323 register u_long
*tl
;
2324 register caddr_t cp
;
2325 register long t1
, t2
;
2326 caddr_t bpos
, dpos
, cp2
;
2327 int error
= 0, wccpostattr
= 0;
2328 struct timespec premtime
= { 0, 0 };
2329 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2333 if (!VFSTONFS(vnode_mount(dvp
)))
2337 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
+ nfsm_rndup(namelen
));
2340 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_REMOVE
]);
2341 nfsm_fhtom(dvp
, v3
);
2342 nfsm_strtom(name
, namelen
, NFS_MAXNAMLEN
, v3
);
2343 nfsm_request(dvp
, NFSPROC_REMOVE
, proc
, cred
, &xid
);
2345 nfsm_wcc_data(dvp
, &premtime
, wccpostattr
, &xid
);
2347 VTONFS(dvp
)->n_flag
|= NMODIFIED
;
2348 /* if directory hadn't changed, update namecache mtime */
2349 if (nfstimespeccmp(&VTONFS(dvp
)->n_ncmtime
, &premtime
, ==))
2350 VTONFS(dvp
)->n_ncmtime
= VTONFS(dvp
)->n_vattr
.nva_mtime
;
2352 NATTRINVALIDATE(VTONFS(dvp
));
2357 * nfs file rename call
2361 struct vnop_rename_args
/* {
2362 struct vnodeop_desc *a_desc;
2365 struct componentname *a_fcnp;
2368 struct componentname *a_tcnp;
2369 vfs_context_t a_context;
2372 vnode_t fvp
= ap
->a_fvp
;
2373 vnode_t tvp
= ap
->a_tvp
;
2374 vnode_t fdvp
= ap
->a_fdvp
;
2375 vnode_t tdvp
= ap
->a_tdvp
;
2376 struct componentname
*tcnp
= ap
->a_tcnp
;
2377 struct componentname
*fcnp
= ap
->a_fcnp
;
2379 mount_t fmp
, tdmp
, tmp
;
2380 struct nfsnode
*tnp
;
2384 cred
= vfs_context_ucred(ap
->a_context
);
2385 p
= vfs_context_proc(ap
->a_context
);
2387 tnp
= tvp
? VTONFS(tvp
) : NULL
;
2389 /* Check for cross-device rename */
2390 fmp
= vnode_mount(fvp
);
2391 tmp
= tvp
? vnode_mount(tvp
) : NULL
;
2392 tdmp
= vnode_mount(tdvp
);
2393 if ((fmp
!= tdmp
) || (tvp
&& (fmp
!= tmp
))) {
2399 * If the tvp exists and is in use, sillyrename it before doing the
2400 * rename of the new file over it.
2401 * XXX Can't sillyrename a directory.
2402 * Don't sillyrename if source and target are same vnode (hard
2403 * links or case-variants)
2405 if (tvp
&& tvp
!= fvp
) {
2406 inuse
= vnode_isinuse(tvp
, 0);
2408 if (inuse
&& !tnp
->n_sillyrename
&& vnode_vtype(tvp
) != VDIR
) {
2409 if ((error
= nfs_sillyrename(tdvp
, tvp
, tcnp
, cred
, p
))) {
2410 /* sillyrename failed. Instead of pressing on, return error */
2411 goto out
; /* should not be ENOENT. */
2413 /* sillyrename succeeded.*/
2418 error
= nfs_renamerpc(fdvp
, fcnp
->cn_nameptr
, fcnp
->cn_namelen
,
2419 tdvp
, tcnp
->cn_nameptr
, tcnp
->cn_namelen
, cred
, p
);
2422 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
2424 if (error
== ENOENT
)
2427 if (!error
&& tvp
&& tvp
!= fvp
&& !tnp
->n_sillyrename
) {
2429 * remove nfsnode from hash now so we can't accidentally find it
2430 * again if another object gets created with the same filehandle
2431 * before this vnode gets reclaimed
2433 lck_mtx_lock(nfs_node_hash_mutex
);
2434 LIST_REMOVE(tnp
, n_hash
);
2435 tnp
->n_flag
&= ~NHASHED
;
2436 lck_mtx_unlock(nfs_node_hash_mutex
);
2439 /* purge the old name cache entries and enter the new one */
2443 if (!error
&& !tnp
->n_sillyrename
) {
2444 /* clear flags now: won't get nfs_inactive for recycled vnode */
2445 /* clear all flags other than these */
2446 tnp
->n_flag
&= (NMODIFIED
| NFLUSHINPROG
| NFLUSHWANT
| NHASHED
);
2451 cache_enter(tdvp
, fvp
, tcnp
);
2455 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
2457 if (error
== ENOENT
)
2463 * Do an nfs rename rpc. Called from nfs_rename() and nfs_sillyrename().
2466 nfs_renamerpc(fdvp
, fnameptr
, fnamelen
, tdvp
, tnameptr
, tnamelen
, cred
, proc
)
2476 register u_long
*tl
;
2477 register caddr_t cp
;
2478 register long t1
, t2
;
2479 caddr_t bpos
, dpos
, cp2
;
2480 int error
= 0, fwccpostattr
= 0, twccpostattr
= 0;
2481 struct timespec fpremtime
= { 0, 0 }, tpremtime
= { 0, 0 };
2482 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2486 if (!VFSTONFS(vnode_mount(fdvp
)))
2488 v3
= NFS_ISV3(fdvp
);
2490 nfsm_reqhead((NFSX_FH(v3
) + NFSX_UNSIGNED
)*2 + nfsm_rndup(fnamelen
) +
2491 nfsm_rndup(tnamelen
));
2494 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_RENAME
]);
2495 nfsm_fhtom(fdvp
, v3
);
2496 nfsm_strtom(fnameptr
, fnamelen
, NFS_MAXNAMLEN
, v3
);
2497 nfsm_fhtom(tdvp
, v3
);
2498 nfsm_strtom(tnameptr
, tnamelen
, NFS_MAXNAMLEN
, v3
);
2499 nfsm_request(fdvp
, NFSPROC_RENAME
, proc
, cred
, &xid
);
2501 u_int64_t txid
= xid
;
2503 nfsm_wcc_data(fdvp
, &fpremtime
, fwccpostattr
, &xid
);
2504 nfsm_wcc_data(tdvp
, &tpremtime
, twccpostattr
, &txid
);
2507 VTONFS(fdvp
)->n_flag
|= NMODIFIED
;
2508 /* if directory hadn't changed, update namecache mtime */
2509 if (nfstimespeccmp(&VTONFS(fdvp
)->n_ncmtime
, &fpremtime
, ==))
2510 VTONFS(fdvp
)->n_ncmtime
= VTONFS(fdvp
)->n_vattr
.nva_mtime
;
2512 NATTRINVALIDATE(VTONFS(fdvp
));
2513 VTONFS(tdvp
)->n_flag
|= NMODIFIED
;
2514 /* if directory hadn't changed, update namecache mtime */
2515 if (nfstimespeccmp(&VTONFS(tdvp
)->n_ncmtime
, &tpremtime
, ==))
2516 VTONFS(tdvp
)->n_ncmtime
= VTONFS(tdvp
)->n_vattr
.nva_mtime
;
2518 NATTRINVALIDATE(VTONFS(tdvp
));
2523 * nfs hard link create call
2527 struct vnop_link_args
/* {
2528 struct vnodeop_desc *a_desc;
2531 struct componentname *a_cnp;
2532 vfs_context_t a_context;
2535 vnode_t vp
= ap
->a_vp
;
2536 vnode_t tdvp
= ap
->a_tdvp
;
2537 struct componentname
*cnp
= ap
->a_cnp
;
2541 caddr_t bpos
, dpos
, cp2
;
2542 int error
= 0, wccpostattr
= 0, attrflag
= 0;
2543 struct timespec premtime
= { 0, 0 };
2544 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2550 if (vnode_mount(vp
) != vnode_mount(tdvp
)) {
2554 cred
= vfs_context_ucred(ap
->a_context
);
2555 p
= vfs_context_proc(ap
->a_context
);
2560 * Push all writes to the server, so that the attribute cache
2561 * doesn't get "out of sync" with the server.
2562 * XXX There should be a better way!
2564 nfs_flush(vp
, MNT_WAIT
, cred
, p
, 0);
2566 nfsm_reqhead(NFSX_FH(v3
)*2 + NFSX_UNSIGNED
+ nfsm_rndup(cnp
->cn_namelen
));
2569 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_LINK
]);
2571 nfsm_fhtom(tdvp
, v3
);
2572 nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
, v3
);
2573 nfsm_request(vp
, NFSPROC_LINK
, p
, cred
, &xid
);
2575 u_int64_t txid
= xid
;
2577 nfsm_postop_attr_update(vp
, v3
, attrflag
, &xid
);
2578 nfsm_wcc_data(tdvp
, &premtime
, wccpostattr
, &txid
);
2582 VTONFS(tdvp
)->n_flag
|= NMODIFIED
;
2584 NATTRINVALIDATE(VTONFS(vp
));
2585 /* if directory hadn't changed, update namecache mtime */
2586 if (nfstimespeccmp(&VTONFS(tdvp
)->n_ncmtime
, &premtime
, ==))
2587 VTONFS(tdvp
)->n_ncmtime
= VTONFS(tdvp
)->n_vattr
.nva_mtime
;
2589 NATTRINVALIDATE(VTONFS(tdvp
));
2591 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2593 if (error
== EEXIST
)
2599 * nfs symbolic link create call
2603 struct vnop_symlink_args
/* {
2604 struct vnodeop_desc *a_desc;
2607 struct componentname *a_cnp;
2608 struct vnode_attr *a_vap;
2610 vfs_context_t a_context;
2613 vnode_t dvp
= ap
->a_dvp
;
2614 struct vnode_attr
*vap
= ap
->a_vap
;
2615 struct componentname
*cnp
= ap
->a_cnp
;
2616 struct nfs_vattr nvattr
;
2617 struct nfsv2_sattr
*sp
;
2621 caddr_t bpos
, dpos
, cp2
;
2622 int slen
, error
= 0, wccpostattr
= 0, gotvp
= 0;
2623 struct timespec premtime
= { 0, 0 };
2624 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2625 vnode_t newvp
= (vnode_t
)0;
2626 int v3
= NFS_ISV3(dvp
);
2631 struct nfsnode
*np
= NULL
;
2633 cred
= vfs_context_ucred(ap
->a_context
);
2634 p
= vfs_context_proc(ap
->a_context
);
2636 slen
= strlen(ap
->a_target
);
2637 nfsm_reqhead(NFSX_FH(v3
) + 2*NFSX_UNSIGNED
+
2638 nfsm_rndup(cnp
->cn_namelen
) + nfsm_rndup(slen
) + NFSX_SATTR(v3
));
2642 VATTR_SET_SUPPORTED(vap
, va_mode
);
2643 VATTR_SET_SUPPORTED(vap
, va_uid
);
2644 VATTR_SET_SUPPORTED(vap
, va_gid
);
2645 VATTR_SET_SUPPORTED(vap
, va_data_size
);
2646 VATTR_SET_SUPPORTED(vap
, va_access_time
);
2647 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
2648 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
2649 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
2651 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_SYMLINK
]);
2652 nfsm_fhtom(dvp
, v3
);
2653 nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
, v3
);
2657 nfsm_strtom(ap
->a_target
, slen
, NFS_MAXPATHLEN
, v3
);
2659 struct timespec neg1time
= { -1, -1 };
2660 nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
2661 sp
->sa_mode
= vtonfsv2_mode(VLNK
,
2662 (VATTR_IS_ACTIVE(vap
, va_mode
) ? vap
->va_mode
: 0600));
2663 sp
->sa_uid
= gotuid
? (u_long
)txdr_unsigned(vap
->va_uid
) : nfs_xdrneg1
;
2664 sp
->sa_gid
= gotgid
? (u_long
)txdr_unsigned(vap
->va_gid
) : nfs_xdrneg1
;
2665 sp
->sa_size
= nfs_xdrneg1
;
2666 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
2667 txdr_nfsv2time(&vap
->va_access_time
, &sp
->sa_atime
);
2669 txdr_nfsv2time(&neg1time
, &sp
->sa_atime
);
2671 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
2672 txdr_nfsv2time(&vap
->va_modify_time
, &sp
->sa_mtime
);
2674 txdr_nfsv2time(&neg1time
, &sp
->sa_mtime
);
2677 nfsm_request(dvp
, NFSPROC_SYMLINK
, p
, cred
, &xid
);
2679 u_int64_t dxid
= xid
;
2682 nfsm_mtofh(dvp
, cnp
, newvp
, v3
, &xid
, gotvp
);
2683 nfsm_wcc_data(dvp
, &premtime
, wccpostattr
, &dxid
);
2687 VTONFS(dvp
)->n_flag
|= NMODIFIED
;
2688 /* if directory hadn't changed, update namecache mtime */
2689 if (nfstimespeccmp(&VTONFS(dvp
)->n_ncmtime
, &premtime
, ==))
2690 VTONFS(dvp
)->n_ncmtime
= VTONFS(dvp
)->n_vattr
.nva_mtime
;
2692 NATTRINVALIDATE(VTONFS(dvp
));
2695 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
2696 * if we can succeed in looking up the symlink.
2698 if ((error
== EEXIST
) || (!error
&& !gotvp
)) {
2703 error
= nfs_lookitup(dvp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, cred
, p
, &np
);
2706 if (vnode_vtype(newvp
) != VLNK
)
2710 if (!error
&& (gotuid
|| gotgid
) &&
2711 (!newvp
|| nfs_getattrcache(newvp
, &nvattr
) ||
2712 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
2713 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
2714 /* clear ID bits if server didn't use them (or we can't tell) */
2715 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
2716 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
2732 struct vnop_mkdir_args
/* {
2733 struct vnodeop_desc *a_desc;
2736 struct componentname *a_cnp;
2737 struct vnode_attr *a_vap;
2738 vfs_context_t a_context;
2741 vnode_t dvp
= ap
->a_dvp
;
2742 struct vnode_attr
*vap
= ap
->a_vap
;
2743 struct componentname
*cnp
= ap
->a_cnp
;
2744 struct nfs_vattr nvattr
;
2745 struct nfsv2_sattr
*sp
;
2750 struct nfsnode
*np
= (struct nfsnode
*)0;
2751 vnode_t newvp
= (vnode_t
)0;
2752 caddr_t bpos
, dpos
, cp2
;
2753 int error
= 0, wccpostattr
= 0;
2754 struct timespec premtime
= { 0, 0 };
2756 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2757 int v3
= NFS_ISV3(dvp
);
2759 u_int64_t xid
, dxid
;
2763 cred
= vfs_context_ucred(ap
->a_context
);
2764 p
= vfs_context_proc(ap
->a_context
);
2766 len
= cnp
->cn_namelen
;
2767 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
+ nfsm_rndup(len
) + NFSX_SATTR(v3
));
2771 VATTR_SET_SUPPORTED(vap
, va_mode
);
2772 VATTR_SET_SUPPORTED(vap
, va_uid
);
2773 VATTR_SET_SUPPORTED(vap
, va_gid
);
2774 VATTR_SET_SUPPORTED(vap
, va_data_size
);
2775 VATTR_SET_SUPPORTED(vap
, va_access_time
);
2776 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
2777 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
2778 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
2780 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_MKDIR
]);
2781 nfsm_fhtom(dvp
, v3
);
2782 nfsm_strtom(cnp
->cn_nameptr
, len
, NFS_MAXNAMLEN
, v3
);
2786 struct timespec neg1time
= { -1, -1 };
2787 nfsm_build(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
2788 sp
->sa_mode
= vtonfsv2_mode(VDIR
,
2789 (VATTR_IS_ACTIVE(vap
, va_mode
) ? vap
->va_mode
: 0600));
2790 sp
->sa_uid
= gotuid
? (u_long
)txdr_unsigned(vap
->va_uid
) : nfs_xdrneg1
;
2791 sp
->sa_gid
= gotgid
? (u_long
)txdr_unsigned(vap
->va_gid
) : nfs_xdrneg1
;
2792 sp
->sa_size
= nfs_xdrneg1
;
2793 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
2794 txdr_nfsv2time(&vap
->va_access_time
, &sp
->sa_atime
);
2796 txdr_nfsv2time(&neg1time
, &sp
->sa_atime
);
2798 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
2799 txdr_nfsv2time(&vap
->va_modify_time
, &sp
->sa_mtime
);
2801 txdr_nfsv2time(&neg1time
, &sp
->sa_mtime
);
2804 nfsm_request(dvp
, NFSPROC_MKDIR
, p
, cred
, &xid
);
2807 nfsm_mtofh(dvp
, cnp
, newvp
, v3
, &xid
, gotvp
);
2809 nfsm_wcc_data(dvp
, &premtime
, wccpostattr
, &dxid
);
2811 VTONFS(dvp
)->n_flag
|= NMODIFIED
;
2812 /* if directory hadn't changed, update namecache mtime */
2813 if (nfstimespeccmp(&VTONFS(dvp
)->n_ncmtime
, &premtime
, ==))
2814 VTONFS(dvp
)->n_ncmtime
= VTONFS(dvp
)->n_vattr
.nva_mtime
;
2816 NATTRINVALIDATE(VTONFS(dvp
));
2818 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
2819 * if we can succeed in looking up the directory.
2821 if (error
== EEXIST
|| (!error
&& !gotvp
)) {
2826 error
= nfs_lookitup(dvp
, cnp
->cn_nameptr
, len
, cred
, p
, &np
);
2829 if (vnode_vtype(newvp
) != VDIR
)
2833 if (!error
&& (gotuid
|| gotgid
) &&
2834 (!newvp
|| nfs_getattrcache(newvp
, &nvattr
) ||
2835 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
2836 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
2837 /* clear ID bits if server didn't use them (or we can't tell) */
2838 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
2839 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
2851 * nfs remove directory call
2855 struct vnop_rmdir_args
/* {
2856 struct vnodeop_desc *a_desc;
2859 struct componentname *a_cnp;
2860 vfs_context_t a_context;
2863 vnode_t vp
= ap
->a_vp
;
2864 vnode_t dvp
= ap
->a_dvp
;
2865 struct componentname
*cnp
= ap
->a_cnp
;
2869 caddr_t bpos
, dpos
, cp2
;
2870 int error
= 0, wccpostattr
= 0;
2871 struct timespec premtime
= { 0, 0 };
2872 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2873 int v3
= NFS_ISV3(dvp
);
2878 cred
= vfs_context_ucred(ap
->a_context
);
2879 p
= vfs_context_proc(ap
->a_context
);
2881 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
+ nfsm_rndup(cnp
->cn_namelen
));
2884 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_RMDIR
]);
2885 nfsm_fhtom(dvp
, v3
);
2886 nfsm_strtom(cnp
->cn_nameptr
, cnp
->cn_namelen
, NFS_MAXNAMLEN
, v3
);
2887 nfsm_request(dvp
, NFSPROC_RMDIR
, p
, cred
, &xid
);
2889 nfsm_wcc_data(dvp
, &premtime
, wccpostattr
, &xid
);
2891 VTONFS(dvp
)->n_flag
|= NMODIFIED
;
2892 /* if directory hadn't changed, update namecache mtime */
2893 if (nfstimespeccmp(&VTONFS(dvp
)->n_ncmtime
, &premtime
, ==))
2894 VTONFS(dvp
)->n_ncmtime
= VTONFS(dvp
)->n_vattr
.nva_mtime
;
2896 NATTRINVALIDATE(VTONFS(dvp
));
2899 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2901 if (error
== ENOENT
)
2905 * remove nfsnode from hash now so we can't accidentally find it
2906 * again if another object gets created with the same filehandle
2907 * before this vnode gets reclaimed
2909 lck_mtx_lock(nfs_node_hash_mutex
);
2910 LIST_REMOVE(VTONFS(vp
), n_hash
);
2911 VTONFS(vp
)->n_flag
&= ~NHASHED
;
2912 lck_mtx_unlock(nfs_node_hash_mutex
);
2922 struct vnop_readdir_args
/* {
2923 struct vnodeop_desc *a_desc;
2929 vfs_context_t a_context;
2932 vnode_t vp
= ap
->a_vp
;
2933 struct nfsnode
*np
= VTONFS(vp
);
2934 struct uio
*uio
= ap
->a_uio
;
2936 struct nfs_vattr nvattr
;
2940 if (vnode_vtype(vp
) != VDIR
)
2943 cred
= vfs_context_ucred(ap
->a_context
);
2944 p
= vfs_context_proc(ap
->a_context
);
2947 * First, check for hit on the EOF offset cache
2949 if (np
->n_direofoffset
> 0 && uio
->uio_offset
>= np
->n_direofoffset
&&
2950 (np
->n_flag
& NMODIFIED
) == 0) {
2951 if (!nfs_getattr(vp
, &nvattr
, cred
, p
)) {
2952 if (nfstimespeccmp(&np
->n_mtime
, &nvattr
.nva_mtime
, ==)) {
2953 OSAddAtomic(1, (SInt32
*)&nfsstats
.direofcache_hits
);
2956 if (nfstimespeccmp(&np
->n_ncmtime
, &nvattr
.nva_mtime
, !=)) {
2957 /* directory changed, purge any name cache entries */
2964 * Call nfs_bioread() to do the real work.
2966 // LP64todo - fix this
2967 tresid
= uio_uio_resid(uio
);
2968 error
= nfs_bioread(vp
, uio
, 0, cred
, p
);
2970 if (!error
&& uio_uio_resid(uio
) == tresid
)
2971 OSAddAtomic(1, (SInt32
*)&nfsstats
.direofcache_misses
);
2977 * Called from below the buffer cache by nfs_doio().
2986 register int len
, skiplen
, left
;
2987 register struct dirent
*dp
;
2988 register u_long
*tl
;
2989 register caddr_t cp
;
2990 register long t1
, t2
;
2991 register nfsuint64
*cookiep
;
2992 caddr_t bpos
, dpos
, cp2
;
2993 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
2995 struct nfsmount
*nmp
;
2996 struct nfsnode
*dnp
= VTONFS(vp
);
2998 int error
= 0, tlen
, more_dirs
= 1, blksiz
= 0, bigenough
= 1;
3000 int v3
, nmreaddirsize
;
3004 dp
= (struct dirent
*)0;
3007 if (uiop
->uio_iovcnt
!= 1 || (uiop
->uio_offset
& (NFS_DIRBLKSIZ
- 1)) ||
3008 (uio_uio_resid(uiop
) & (NFS_DIRBLKSIZ
- 1)))
3009 panic("nfs_readdirrpc: bad uio");
3011 nmp
= VFSTONFS(vnode_mount(vp
));
3015 nmreaddirsize
= nmp
->nm_readdirsize
;
3018 * If there is no cookie, assume directory was stale.
3020 cookiep
= nfs_getcookie(dnp
, uiop
->uio_offset
, 0);
3024 return (NFSERR_BAD_COOKIE
);
3026 * Loop around doing readdir rpc's of size nm_readdirsize
3027 * truncated to a multiple of DIRBLKSIZ.
3028 * The stopping criteria is EOF or buffer full.
3030 while (more_dirs
&& bigenough
) {
3031 nfsm_reqhead(NFSX_FH(v3
) + NFSX_READDIR(v3
));
3034 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_READDIR
]);
3037 nfsm_build(tl
, u_long
*, 5 * NFSX_UNSIGNED
);
3038 *tl
++ = cookie
.nfsuquad
[0];
3039 *tl
++ = cookie
.nfsuquad
[1];
3040 *tl
++ = dnp
->n_cookieverf
.nfsuquad
[0];
3041 *tl
++ = dnp
->n_cookieverf
.nfsuquad
[1];
3043 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
3044 *tl
++ = cookie
.nfsuquad
[0];
3046 *tl
= txdr_unsigned(nmreaddirsize
);
3047 nfsm_request(vp
, NFSPROC_READDIR
, p
, cred
, &xid
);
3050 nfsm_postop_attr_update(vp
, v3
, attrflag
, &xid
);
3053 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
3054 dnp
->n_cookieverf
.nfsuquad
[0] = *tl
++;
3055 dnp
->n_cookieverf
.nfsuquad
[1] = *tl
;
3061 // XXX assert error?
3064 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
3065 more_dirs
= fxdr_unsigned(int, *tl
);
3067 /* loop thru the dir entries, doctoring them to 4bsd form */
3068 while (more_dirs
&& bigenough
) {
3070 nfsm_dissect(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
3071 fxdr_hyper(tl
, &fileno
);
3072 len
= fxdr_unsigned(int, *(tl
+ 2));
3074 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
3075 fileno
= fxdr_unsigned(u_quad_t
, *tl
++);
3076 len
= fxdr_unsigned(int, *tl
);
3078 /* Note: v3 supports longer names, but struct dirent doesn't */
3079 /* so we just truncate the names to fit */
3085 if (len
> MAXNAMLEN
) {
3086 skiplen
= len
- MAXNAMLEN
;
3091 tlen
= nfsm_rndup(len
);
3093 tlen
+= 4; /* To ensure null termination */
3094 left
= DIRBLKSIZ
- blksiz
;
3095 if ((tlen
+ (int)DIRHDSIZ
) > left
) {
3096 dp
->d_reclen
+= left
;
3097 uio_iov_base_add(uiop
, left
);
3098 uio_iov_len_add(uiop
, -left
);
3099 uiop
->uio_offset
+= left
;
3100 uio_uio_resid_add(uiop
, -left
);
3103 if ((tlen
+ (int)DIRHDSIZ
) > uio_uio_resid(uiop
))
3106 // LP64todo - fix this!
3107 dp
= (struct dirent
*) CAST_DOWN(caddr_t
, uio_iov_base(uiop
));
3108 dp
->d_fileno
= (int)fileno
;
3110 dp
->d_reclen
= tlen
+ DIRHDSIZ
;
3111 dp
->d_type
= DT_UNKNOWN
;
3112 blksiz
+= dp
->d_reclen
;
3113 if (blksiz
== DIRBLKSIZ
)
3115 uiop
->uio_offset
+= DIRHDSIZ
;
3117 uio_uio_resid_add(uiop
, -((int64_t)DIRHDSIZ
));
3118 uio_iov_len_add(uiop
, -((int64_t)DIRHDSIZ
));
3120 uio_uio_resid_add(uiop
, -((int)DIRHDSIZ
));
3121 uio_iov_len_add(uiop
, -((int)DIRHDSIZ
));
3123 uio_iov_base_add(uiop
, DIRHDSIZ
);
3124 nfsm_mtouio(uiop
, len
);
3125 // LP64todo - fix this!
3126 cp
= CAST_DOWN(caddr_t
, uio_iov_base(uiop
));
3128 *cp
= '\0'; /* null terminate */
3129 uio_iov_base_add(uiop
, tlen
);
3130 uio_iov_len_add(uiop
, -tlen
);
3131 uiop
->uio_offset
+= tlen
;
3132 uio_uio_resid_add(uiop
, -tlen
);
3134 nfsm_adv(nfsm_rndup(len
));
3137 nfsm_adv(nfsm_rndup(skiplen
));
3139 nfsm_dissect(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
3141 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
3144 cookie
.nfsuquad
[0] = *tl
++;
3146 cookie
.nfsuquad
[1] = *tl
++;
3151 more_dirs
= fxdr_unsigned(int, *tl
);
3154 * If at end of rpc data, get the eof boolean
3157 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
3158 more_dirs
= (fxdr_unsigned(int, *tl
) == 0);
3163 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3164 * by increasing d_reclen for the last record.
3167 left
= DIRBLKSIZ
- blksiz
;
3168 dp
->d_reclen
+= left
;
3169 uio_iov_base_add(uiop
, left
);
3170 uio_iov_len_add(uiop
, -left
);
3171 uiop
->uio_offset
+= left
;
3172 uio_uio_resid_add(uiop
, -left
);
3176 * We are now either at the end of the directory or have filled the
3180 dnp
->n_direofoffset
= uiop
->uio_offset
;
3182 if (uio_uio_resid(uiop
) > 0)
3183 printf("EEK! readdirrpc resid > 0\n");
3184 cookiep
= nfs_getcookie(dnp
, uiop
->uio_offset
, 1);
3193 * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc().
3202 int len
, skiplen
, left
;
3209 caddr_t bpos
, dpos
, cp2
;
3210 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
3211 struct componentname cn
, *cnp
= &cn
;
3213 struct nfsmount
*nmp
;
3214 struct nfsnode
*dnp
= VTONFS(vp
), *np
;
3217 int error
= 0, tlen
, more_dirs
= 1, blksiz
= 0, doit
, bigenough
= 1, i
;
3218 int attrflag
, fhsize
, nmreaddirsize
, nmrsize
;
3219 u_int64_t xid
, savexid
;
3220 struct nfs_vattr nvattr
;
3223 dp
= (struct dirent
*)0;
3226 if (uiop
->uio_iovcnt
!= 1 || (uiop
->uio_offset
& (DIRBLKSIZ
- 1)) ||
3227 (uio_uio_resid(uiop
) & (DIRBLKSIZ
- 1)))
3228 panic("nfs_readdirplusrpc: bad uio");
3230 nmp
= VFSTONFS(vnode_mount(vp
));
3233 nmreaddirsize
= nmp
->nm_readdirsize
;
3234 nmrsize
= nmp
->nm_rsize
;
3236 bzero(cnp
, sizeof(*cnp
));
3240 * If there is no cookie, assume directory was stale.
3242 cookiep
= nfs_getcookie(dnp
, uiop
->uio_offset
, 0);
3246 return (NFSERR_BAD_COOKIE
);
3248 * Loop around doing readdir rpc's of size nm_readdirsize
3249 * truncated to a multiple of DIRBLKSIZ.
3250 * The stopping criteria is EOF or buffer full.
3252 while (more_dirs
&& bigenough
) {
3253 nfsm_reqhead(NFSX_FH(1) + 6 * NFSX_UNSIGNED
);
3256 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_READDIRPLUS
]);
3258 nfsm_build(tl
, u_long
*, 6 * NFSX_UNSIGNED
);
3259 *tl
++ = cookie
.nfsuquad
[0];
3260 *tl
++ = cookie
.nfsuquad
[1];
3261 *tl
++ = dnp
->n_cookieverf
.nfsuquad
[0];
3262 *tl
++ = dnp
->n_cookieverf
.nfsuquad
[1];
3263 *tl
++ = txdr_unsigned(nmreaddirsize
);
3264 *tl
= txdr_unsigned(nmrsize
);
3265 nfsm_request(vp
, NFSPROC_READDIRPLUS
, p
, cred
, &xid
);
3268 nfsm_postop_attr_update(vp
, 1, attrflag
, &xid
);
3274 nfsm_dissect(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
3275 dnp
->n_cookieverf
.nfsuquad
[0] = *tl
++;
3276 dnp
->n_cookieverf
.nfsuquad
[1] = *tl
++;
3277 more_dirs
= fxdr_unsigned(int, *tl
);
3279 /* loop thru the dir entries, doctoring them to 4bsd form */
3280 while (more_dirs
&& bigenough
) {
3281 nfsm_dissect(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
3282 fxdr_hyper(tl
, &fileno
);
3283 len
= fxdr_unsigned(int, *(tl
+ 2));
3284 /* Note: v3 supports longer names, but struct dirent doesn't */
3285 /* so we just truncate the names to fit */
3291 if (len
> MAXNAMLEN
) {
3292 skiplen
= len
- MAXNAMLEN
;
3297 tlen
= nfsm_rndup(len
);
3299 tlen
+= 4; /* To ensure null termination*/
3300 left
= DIRBLKSIZ
- blksiz
;
3301 if ((tlen
+ (int)DIRHDSIZ
) > left
) {
3302 dp
->d_reclen
+= left
;
3303 uio_iov_base_add(uiop
, left
);
3304 uio_iov_len_add(uiop
, -left
);
3305 uiop
->uio_offset
+= left
;
3306 uio_uio_resid_add(uiop
, -left
);
3309 if ((tlen
+ (int)DIRHDSIZ
) > uio_uio_resid(uiop
))
3312 // LP64todo - fix this!
3313 dp
= (struct dirent
*) CAST_DOWN(caddr_t
, uio_iov_base(uiop
));
3314 dp
->d_fileno
= (int)fileno
;
3316 dp
->d_reclen
= tlen
+ DIRHDSIZ
;
3317 dp
->d_type
= DT_UNKNOWN
;
3318 blksiz
+= dp
->d_reclen
;
3319 if (blksiz
== DIRBLKSIZ
)
3321 uiop
->uio_offset
+= DIRHDSIZ
;
3323 uio_uio_resid_add(uiop
, -((int64_t)DIRHDSIZ
));
3324 uio_iov_len_add(uiop
, -((int64_t)DIRHDSIZ
));
3326 uio_uio_resid_add(uiop
, -((int)DIRHDSIZ
));
3327 uio_iov_len_add(uiop
, -((int)DIRHDSIZ
));
3329 uio_iov_base_add(uiop
, DIRHDSIZ
);
3330 // LP64todo - fix this!
3331 cnp
->cn_nameptr
= CAST_DOWN(caddr_t
, uio_iov_base(uiop
));
3332 cnp
->cn_namelen
= len
;
3333 nfsm_mtouio(uiop
, len
);
3334 cp
= CAST_DOWN(caddr_t
, uio_iov_base(uiop
));
3337 uio_iov_base_add(uiop
, tlen
);
3338 uio_iov_len_add(uiop
, -tlen
);
3339 uiop
->uio_offset
+= tlen
;
3340 uio_uio_resid_add(uiop
, -tlen
);
3342 nfsm_adv(nfsm_rndup(len
));
3345 nfsm_adv(nfsm_rndup(skiplen
));
3346 nfsm_dissect(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
3348 cookie
.nfsuquad
[0] = *tl
++;
3349 cookie
.nfsuquad
[1] = *tl
++;
3354 * Since the attributes are before the file handle
3355 * (sigh), we must skip over the attributes and then
3356 * come back and get them.
3358 attrflag
= fxdr_unsigned(int, *tl
);
3360 /* grab attributes */
3361 nfsm_attr_get(1, &nvattr
);
3362 dp
->d_type
= IFTODT(VTTOIF(nvattr
.nva_type
));
3363 /* check for file handle */
3364 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
3365 doit
= fxdr_unsigned(int, *tl
);
3367 nfsm_getfh(fhp
, fhsize
, 1);
3368 if (NFS_CMPFH(dnp
, fhp
, fhsize
)) {
3369 error
= vnode_ref(vp
);
3376 } else if (!bigenough
||
3377 (cnp
->cn_namelen
== 2 &&
3378 cnp
->cn_nameptr
[1] == '.' &&
3379 cnp
->cn_nameptr
[0] == '.')) {
3381 * XXXmacko I don't think this ".." thing is a problem anymore.
3382 * don't doit if we can't guarantee
3383 * that this entry is NOT ".." because
3384 * we would have to drop the lock on
3385 * the directory before getting the
3386 * lock on the ".." vnode... and we
3387 * don't want to drop the dvp lock in
3388 * the middle of a readdirplus.
3394 error
= nfs_nget(vnode_mount(vp
), vp
, cnp
,
3395 fhp
, fhsize
, &nvattr
, &xid
,
3403 /* update attributes if not already updated */
3404 if (doit
&& bigenough
&& (np
->n_xid
<= savexid
)) {
3406 nfs_loadattrcache(np
, &nvattr
, &xid
, 0);
3407 /* any error can be ignored */
3410 /* Just skip over the file handle */
3411 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
3412 i
= fxdr_unsigned(int, *tl
);
3413 nfsm_adv(nfsm_rndup(i
));
3415 if (newvp
!= NULLVP
) {
3422 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
3423 more_dirs
= fxdr_unsigned(int, *tl
);
3426 * If at end of rpc data, get the eof boolean
3429 nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
3430 more_dirs
= (fxdr_unsigned(int, *tl
) == 0);
3435 * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
3436 * by increasing d_reclen for the last record.
3439 left
= DIRBLKSIZ
- blksiz
;
3440 dp
->d_reclen
+= left
;
3441 uio_iov_base_add(uiop
, left
);
3442 uio_iov_len_add(uiop
, -left
);
3443 uiop
->uio_offset
+= left
;
3444 uio_uio_resid_add(uiop
, -left
);
3448 * We are now either at the end of the directory or have filled the
3452 dnp
->n_direofoffset
= uiop
->uio_offset
;
3454 if (uio_uio_resid(uiop
) > 0)
3455 printf("EEK! readdirplusrpc resid > 0\n");
3456 cookiep
= nfs_getcookie(dnp
, uiop
->uio_offset
, 1);
3465 * Silly rename. To make the NFS filesystem that is stateless look a little
3466 * more like the "ufs" a remove of an active vnode is translated to a rename
3467 * to a funny looking filename that is removed by nfs_inactive on the
3468 * nfsnode. There is the potential for another process on a different client
3469 * to create the same funny name between the nfs_lookitup() fails and the
3470 * nfs_rename() completes, but...
3473 /* format of "random" names and next name to try */
3474 /* (note: shouldn't exceed size of sillyrename.s_name) */
3475 static char sillyrename_name
[] = ".nfsAAA%04x4.4";
3481 struct componentname
*cnp
,
3485 register struct sillyrename
*sp
;
3489 kauth_cred_t tmpcred
;
3495 if (vnode_vtype(vp
) == VDIR
)
3496 panic("nfs_sillyrename: dir");
3498 MALLOC_ZONE(sp
, struct sillyrename
*,
3499 sizeof (struct sillyrename
), M_NFSREQ
, M_WAITOK
);
3502 kauth_cred_ref(cred
);
3505 error
= vnode_ref(dvp
);
3509 /* Fudge together a funny name */
3511 sp
->s_namlen
= sprintf(sp
->s_name
, sillyrename_name
, pid
);
3513 /* Try lookitups until we get one that isn't there */
3515 while (nfs_lookitup(dvp
, sp
->s_name
, sp
->s_namlen
, sp
->s_cred
, p
, NULL
) == 0) {
3516 if (sp
->s_name
[4]++ >= 'z')
3517 sp
->s_name
[4] = 'A';
3518 if (++i
> ('z' - 'A' + 1)) {
3520 if (sp
->s_name
[5]++ >= 'z')
3521 sp
->s_name
[5] = 'A';
3522 if (++j
> ('z' - 'A' + 1)) {
3524 if (sp
->s_name
[6]++ >= 'z')
3525 sp
->s_name
[6] = 'A';
3526 if (++k
> ('z' - 'A' + 1)) {
3533 /* make note of next "random" name to try */
3534 if ((sillyrename_name
[4] = (sp
->s_name
[4] + 1)) > 'z') {
3535 sillyrename_name
[4] = 'A';
3536 if ((sillyrename_name
[5] = (sp
->s_name
[5] + 1)) > 'z') {
3537 sillyrename_name
[5] = 'A';
3538 if ((sillyrename_name
[6] = (sp
->s_name
[6] + 1)) > 'z')
3539 sillyrename_name
[6] = 'A';
3542 /* now, do the rename */
3543 error
= nfs_renamerpc(dvp
, cnp
->cn_nameptr
, cnp
->cn_namelen
,
3544 dvp
, sp
->s_name
, sp
->s_namlen
, sp
->s_cred
, p
);
3547 error
= nfs_lookitup(dvp
, sp
->s_name
, sp
->s_namlen
, sp
->s_cred
, p
, &np
);
3549 kprintf("sillyrename: %s, vp=%x, np=%x, dvp=%x\n",
3550 &sp
->s_name
[0], (unsigned)vp
, (unsigned)np
, (unsigned)dvp
);
3552 np
->n_sillyrename
= sp
;
3555 vnode_rele(sp
->s_dvp
);
3557 kauth_cred_unref(&sp
->s_cred
);
3558 FREE_ZONE((caddr_t
)sp
, sizeof (struct sillyrename
), M_NFSREQ
);
3563 * Look up a file name and optionally either update the file handle or
3564 * allocate an nfsnode, depending on the value of npp.
3565 * npp == NULL --> just do the lookup
3566 * *npp == NULL --> allocate a new nfsnode and make sure attributes are
3568 * *npp != NULL --> update the file handle in the vnode
3571 nfs_lookitup(dvp
, name
, len
, cred
, procp
, npp
)
3577 struct nfsnode
**npp
;
3582 vnode_t newvp
= (vnode_t
)0;
3583 struct nfsnode
*np
, *dnp
= VTONFS(dvp
);
3584 caddr_t bpos
, dpos
, cp2
;
3585 int error
= 0, fhlen
, attrflag
;
3586 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
3589 u_int64_t xid
, dxid
, savedxid
;
3590 struct nfs_vattr nvattr
;
3592 if (!VFSTONFS(vnode_mount(dvp
)))
3596 nfsm_reqhead(NFSX_FH(v3
) + NFSX_UNSIGNED
+ nfsm_rndup(len
));
3599 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_LOOKUP
]);
3600 nfsm_fhtom(dvp
, v3
);
3601 nfsm_strtom(name
, len
, NFS_MAXNAMLEN
, v3
);
3602 nfsm_request(dvp
, NFSPROC_LOOKUP
, procp
, cred
, &xid
);
3603 if (npp
&& !error
) {
3605 nfsm_getfh(nfhp
, fhlen
, v3
);
3606 /* get attributes */
3608 nfsm_postop_attr_get(v3
, attrflag
, &nvattr
);
3610 /* We need valid attributes in order */
3611 /* to call nfs_nget/vnode_create(). */
3612 error
= nfs_getattr_no_vnode(vnode_mount(dvp
),
3613 nfhp
, fhlen
, cred
, procp
, &nvattr
, &xid
);
3620 nfsm_postop_attr_update(dvp
, v3
, attrflag
, &dxid
);
3622 nfsm_attr_get(v3
, &nvattr
);
3626 if (fhlen
!= np
->n_fhsize
) {
3627 u_char
*oldbuf
= (np
->n_fhsize
> NFS_SMALLFH
) ? np
->n_fhp
: NULL
;
3628 if (fhlen
> NFS_SMALLFH
) {
3629 MALLOC_ZONE(np
->n_fhp
, u_char
*, fhlen
, M_NFSBIGFH
, M_WAITOK
);
3637 np
->n_fhp
= &np
->n_fh
[0];
3640 FREE_ZONE(oldbuf
, np
->n_fhsize
, M_NFSBIGFH
);
3643 bcopy(nfhp
, np
->n_fhp
, fhlen
);
3644 np
->n_fhsize
= fhlen
;
3646 error
= nfs_loadattrcache(np
, &nvattr
, &xid
, 0);
3651 } else if (NFS_CMPFH(dnp
, nfhp
, fhlen
)) {
3653 if (dnp
->n_xid
<= savedxid
) {
3655 error
= nfs_loadattrcache(dnp
, &nvattr
, &dxid
, 0);
3662 struct componentname cn
, *cnp
= &cn
;
3663 bzero(cnp
, sizeof(*cnp
));
3664 cnp
->cn_nameptr
= name
;
3665 cnp
->cn_namelen
= len
;
3667 error
= nfs_nget(vnode_mount(dvp
), dvp
, cnp
, nfhp
, fhlen
,
3668 &nvattr
, &xid
, NG_MAKEENTRY
, &np
);
3677 if (npp
&& *npp
== NULL
) {
3692 * Nfs Version 3 commit rpc
3695 nfs_commit(vp
, offset
, count
, cred
, procp
)
3705 struct nfsmount
*nmp
= VFSTONFS(vnode_mount(vp
));
3706 caddr_t bpos
, dpos
, cp2
;
3707 int error
= 0, wccpostattr
= 0;
3708 struct timespec premtime
= { 0, 0 };
3709 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
3710 u_int64_t xid
, wverf
;
3712 FSDBG(521, vp
, offset
, count
, nmp
->nm_state
);
3715 if ((nmp
->nm_state
& NFSSTA_HASWRITEVERF
) == 0)
3717 nfsm_reqhead(NFSX_FH(1));
3720 OSAddAtomic(1, (SInt32
*)&nfsstats
.rpccnt
[NFSPROC_COMMIT
]);
3722 nfsm_build(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
3723 txdr_hyper(&offset
, tl
);
3725 *tl
= txdr_unsigned(count
);
3726 nfsm_request(vp
, NFSPROC_COMMIT
, procp
, cred
, &xid
);
3728 nfsm_wcc_data(vp
, &premtime
, wccpostattr
, &xid
);
3729 /* XXX can we do anything useful with the wcc info? */
3732 nfsm_dissect(tl
, u_long
*, NFSX_V3WRITEVERF
);
3733 fxdr_hyper(tl
, &wverf
);
3734 if (wverf
!= nmp
->nm_verf
) {
3735 nmp
->nm_verf
= wverf
;
3736 error
= NFSERR_STALEWRITEVERF
;
3745 __unused
struct vnop_blockmap_args
/* {
3746 struct vnodeop_desc *a_desc;
3762 * NB Currently unsupported.
3767 __unused
struct vnop_mmap_args
/* {
3768 struct vnodeop_desc *a_desc;
3771 kauth_cred_t a_cred;
3780 * fsync vnode op. Just call nfs_flush() with commit == 1.
3785 struct vnop_fsync_args
/* {
3786 struct vnodeop_desc *a_desc;
3789 vfs_context_t a_context;
3792 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
3793 proc_t p
= vfs_context_proc(ap
->a_context
);
3794 struct nfsnode
*np
= VTONFS(ap
->a_vp
);
3797 np
->n_flag
|= NWRBUSY
;
3798 error
= nfs_flush(ap
->a_vp
, ap
->a_waitfor
, cred
, p
, 0);
3799 np
->n_flag
&= ~NWRBUSY
;
3804 nfs_flushcommits(vnode_t vp
, proc_t p
, int nowait
)
3806 struct nfsnode
*np
= VTONFS(vp
);
3808 struct nfsbuflists blist
, commitlist
;
3809 int error
= 0, retv
, wcred_set
, flags
;
3810 u_quad_t off
, endoff
, toff
;
3812 kauth_cred_t wcred
= NULL
;
3814 FSDBG_TOP(557, vp
, np
, 0, 0);
3817 * A nb_flags == (NB_DELWRI | NB_NEEDCOMMIT) block has been written to the
3818 * server, but nas not been committed to stable storage on the server
3819 * yet. The byte range is worked out for as many nfsbufs as we can handle
3820 * and the commit rpc is done.
3822 if (!LIST_EMPTY(&np
->n_dirtyblkhd
))
3823 np
->n_flag
|= NMODIFIED
;
3828 LIST_INIT(&commitlist
);
3830 if (!VFSTONFS(vnode_mount(vp
))) {
3834 if (!NFS_ISV3(vp
)) {
3841 flags
|= NBI_NOWAIT
;
3842 lck_mtx_lock(nfs_buf_mutex
);
3843 if (!nfs_buf_iterprepare(np
, &blist
, flags
)) {
3844 while ((bp
= LIST_FIRST(&blist
))) {
3845 LIST_REMOVE(bp
, nb_vnbufs
);
3846 LIST_INSERT_HEAD(&np
->n_dirtyblkhd
, bp
, nb_vnbufs
);
3847 error
= nfs_buf_acquire(bp
, NBAC_NOWAIT
, 0, 0);
3850 if (ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
))
3851 nfs_buf_check_write_verifier(np
, bp
);
3852 if (((bp
->nb_flags
& (NB_DELWRI
| NB_NEEDCOMMIT
))
3853 != (NB_DELWRI
| NB_NEEDCOMMIT
))) {
3857 nfs_buf_remfree(bp
);
3858 lck_mtx_unlock(nfs_buf_mutex
);
3860 * we need a upl to see if the page has been
3861 * dirtied (think mmap) since the unstable write, and
3862 * also to prevent vm from paging it during our commit rpc
3864 if (!ISSET(bp
->nb_flags
, NB_PAGELIST
)) {
3865 retv
= nfs_buf_upl_setup(bp
);
3867 /* unable to create upl */
3868 /* vm object must no longer exist */
3869 /* this could be fatal if we need */
3870 /* to write the data again, we'll see... */
3871 printf("nfs_flushcommits: upl create failed %d\n", retv
);
3872 bp
->nb_valid
= bp
->nb_dirty
= 0;
3875 nfs_buf_upl_check(bp
);
3876 lck_mtx_lock(nfs_buf_mutex
);
3878 FSDBG(557, bp
, bp
->nb_flags
, bp
->nb_valid
, bp
->nb_dirty
);
3879 FSDBG(557, bp
->nb_validoff
, bp
->nb_validend
,
3880 bp
->nb_dirtyoff
, bp
->nb_dirtyend
);
3883 * We used to check for dirty pages here; if there were any
3884 * we'd abort the commit and force the entire buffer to be
3887 * Instead of doing that, we now go ahead and commit the dirty
3888 * range, and then leave the buffer around with dirty pages
3889 * that will be written out later.
3893 * Work out if all buffers are using the same cred
3894 * so we can deal with them all with one commit.
3896 * XXX creds in bp's must be obtained by kauth_cred_ref on
3897 * the same original cred in order for them to be equal.
3899 if (wcred_set
== 0) {
3900 wcred
= bp
->nb_wcred
;
3901 if (!IS_VALID_CRED(wcred
))
3902 panic("nfs: needcommit w/out wcred");
3904 } else if ((wcred_set
== 1) && wcred
!= bp
->nb_wcred
) {
3907 SET(bp
->nb_flags
, NB_WRITEINPROG
);
3910 * A list of these buffers is kept so that the
3911 * second loop knows which buffers have actually
3912 * been committed. This is necessary, since there
3913 * may be a race between the commit rpc and new
3914 * uncommitted writes on the file.
3916 LIST_REMOVE(bp
, nb_vnbufs
);
3917 LIST_INSERT_HEAD(&commitlist
, bp
, nb_vnbufs
);
3918 toff
= NBOFF(bp
) + bp
->nb_dirtyoff
;
3921 toff
+= (u_quad_t
)(bp
->nb_dirtyend
- bp
->nb_dirtyoff
);
3925 nfs_buf_itercomplete(np
, &blist
, NBI_DIRTY
);
3927 lck_mtx_unlock(nfs_buf_mutex
);
3929 if (LIST_EMPTY(&commitlist
)) {
3935 * Commit data on the server, as required.
3936 * If all bufs are using the same wcred, then use that with
3937 * one call for all of them, otherwise commit each one
3940 if (wcred_set
== 1) {
3942 * Note, it's possible the commit range could be >2^32-1.
3943 * If it is, we'll send one commit that covers the whole file.
3945 if ((endoff
- off
) > 0xffffffff)
3948 count
= (endoff
- off
);
3949 retv
= nfs_commit(vp
, off
, count
, wcred
, p
);
3952 LIST_FOREACH(bp
, &commitlist
, nb_vnbufs
) {
3953 toff
= NBOFF(bp
) + bp
->nb_dirtyoff
;
3954 count
= bp
->nb_dirtyend
- bp
->nb_dirtyoff
;
3955 retv
= nfs_commit(vp
, toff
, count
, bp
->nb_wcred
, p
);
3962 * Now, either mark the blocks I/O done or mark the
3963 * blocks dirty, depending on whether the commit
3966 while ((bp
= LIST_FIRST(&commitlist
))) {
3967 LIST_REMOVE(bp
, nb_vnbufs
);
3968 FSDBG(557, bp
, retv
, bp
->nb_flags
, bp
->nb_dirty
);
3969 CLR(bp
->nb_flags
, (NB_NEEDCOMMIT
| NB_WRITEINPROG
));
3970 np
->n_needcommitcnt
--;
3971 CHECK_NEEDCOMMITCNT(np
);
3974 /* move back to dirty list */
3975 lck_mtx_lock(nfs_buf_mutex
);
3976 LIST_INSERT_HEAD(&VTONFS(vp
)->n_dirtyblkhd
, bp
, nb_vnbufs
);
3977 lck_mtx_unlock(nfs_buf_mutex
);
3978 nfs_buf_release(bp
, 1);
3982 vnode_startwrite(vp
);
3983 if (ISSET(bp
->nb_flags
, NB_DELWRI
)) {
3984 OSAddAtomic(-1, (SInt32
*)&nfs_nbdwrite
);
3986 wakeup(&nfs_nbdwrite
);
3988 CLR(bp
->nb_flags
, (NB_READ
|NB_DONE
|NB_ERROR
|NB_DELWRI
));
3989 /* if block still has dirty pages, we don't want it to */
3990 /* be released in nfs_buf_iodone(). So, don't set NB_ASYNC. */
3992 SET(bp
->nb_flags
, NB_ASYNC
);
3994 /* move to clean list */
3995 lck_mtx_lock(nfs_buf_mutex
);
3996 LIST_INSERT_HEAD(&VTONFS(vp
)->n_cleanblkhd
, bp
, nb_vnbufs
);
3997 lck_mtx_unlock(nfs_buf_mutex
);
3999 bp
->nb_dirtyoff
= bp
->nb_dirtyend
= 0;
4003 /* throw it back in as a delayed write buffer */
4004 CLR(bp
->nb_flags
, NB_DONE
);
4005 nfs_buf_write_delayed(bp
, p
);
4010 FSDBG_BOT(557, vp
, np
, 0, error
);
4015 * Flush all the blocks associated with a vnode.
4016 * Walk through the buffer pool and push any dirty pages
4017 * associated with the vnode.
4023 __unused kauth_cred_t cred
,
4025 int ignore_writeerr
)
4027 struct nfsnode
*np
= VTONFS(vp
);
4029 struct nfsbuflists blist
;
4030 struct nfsmount
*nmp
= VFSTONFS(vnode_mount(vp
));
4031 int error
= 0, error2
, slptimeo
= 0, slpflag
= 0;
4032 int flags
, passone
= 1;
4034 FSDBG_TOP(517, vp
, np
, waitfor
, 0);
4040 if (nmp
->nm_flag
& NFSMNT_INT
)
4044 * On the first pass, start async/unstable writes on all
4045 * delayed write buffers. Then wait for all writes to complete
4046 * and call nfs_flushcommits() to commit any uncommitted buffers.
4047 * On all subsequent passes, start STABLE writes on any remaining
4048 * dirty buffers. Then wait for all writes to complete.
4051 lck_mtx_lock(nfs_buf_mutex
);
4052 FSDBG(518, LIST_FIRST(&np
->n_dirtyblkhd
), np
->n_flag
, 0, 0);
4053 if (!LIST_EMPTY(&np
->n_dirtyblkhd
))
4054 np
->n_flag
|= NMODIFIED
;
4055 if (!VFSTONFS(vnode_mount(vp
))) {
4056 lck_mtx_unlock(nfs_buf_mutex
);
4061 /* Start/do any write(s) that are required. */
4062 if (!nfs_buf_iterprepare(np
, &blist
, NBI_DIRTY
)) {
4063 while ((bp
= LIST_FIRST(&blist
))) {
4064 LIST_REMOVE(bp
, nb_vnbufs
);
4065 LIST_INSERT_HEAD(&np
->n_dirtyblkhd
, bp
, nb_vnbufs
);
4066 flags
= (passone
|| (waitfor
!= MNT_WAIT
)) ? NBAC_NOWAIT
: 0;
4067 if (flags
!= NBAC_NOWAIT
)
4069 while ((error
= nfs_buf_acquire(bp
, flags
, slpflag
, slptimeo
))) {
4070 FSDBG(524, bp
, flags
, bp
->nb_lflags
, bp
->nb_flags
);
4074 error2
= nfs_sigintr(VFSTONFS(vnode_mount(vp
)), NULL
, p
);
4076 if (flags
!= NBAC_NOWAIT
)
4077 nfs_buf_refrele(bp
);
4078 nfs_buf_itercomplete(np
, &blist
, NBI_DIRTY
);
4079 lck_mtx_unlock(nfs_buf_mutex
);
4083 if (slpflag
== PCATCH
) {
4089 if (flags
!= NBAC_NOWAIT
)
4090 nfs_buf_refrele(bp
);
4094 /* buffer is no longer valid */
4098 if (ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
))
4099 nfs_buf_check_write_verifier(np
, bp
);
4100 if (!ISSET(bp
->nb_flags
, NB_DELWRI
))
4101 panic("nfs_flush: not dirty");
4102 FSDBG(525, bp
, passone
, bp
->nb_lflags
, bp
->nb_flags
);
4103 if ((passone
|| (waitfor
!= MNT_WAIT
)) &&
4104 ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
)) {
4108 nfs_buf_remfree(bp
);
4109 lck_mtx_unlock(nfs_buf_mutex
);
4110 if (ISSET(bp
->nb_flags
, NB_ERROR
)) {
4111 np
->n_error
= bp
->nb_error
? bp
->nb_error
: EIO
;
4112 np
->n_flag
|= NWRITEERR
;
4113 nfs_buf_release(bp
, 1);
4114 lck_mtx_lock(nfs_buf_mutex
);
4117 SET(bp
->nb_flags
, NB_ASYNC
);
4119 /* NB_STABLE forces this to be written FILESYNC */
4120 SET(bp
->nb_flags
, NB_STABLE
);
4123 lck_mtx_lock(nfs_buf_mutex
);
4125 nfs_buf_itercomplete(np
, &blist
, NBI_DIRTY
);
4127 lck_mtx_unlock(nfs_buf_mutex
);
4129 if (waitfor
== MNT_WAIT
) {
4130 while ((error
= vnode_waitforwrites(vp
, 0, slpflag
, slptimeo
, "nfsflush"))) {
4131 error2
= nfs_sigintr(VFSTONFS(vnode_mount(vp
)), NULL
, p
);
4136 if (slpflag
== PCATCH
) {
4144 /* loop while it looks like there are still buffers to be */
4145 /* commited and nfs_flushcommits() seems to be handling them. */
4146 while (np
->n_needcommitcnt
)
4147 if (nfs_flushcommits(vp
, p
, 0))
4156 if (waitfor
== MNT_WAIT
) {
4157 if (!LIST_EMPTY(&np
->n_dirtyblkhd
))
4159 /* if we have no dirty blocks, we can clear the modified flag */
4160 np
->n_flag
&= ~NMODIFIED
;
4163 FSDBG(526, np
->n_flag
, np
->n_error
, 0, 0);
4164 if (!ignore_writeerr
&& (np
->n_flag
& NWRITEERR
)) {
4165 error
= np
->n_error
;
4166 np
->n_flag
&= ~NWRITEERR
;
4169 FSDBG_BOT(517, vp
, np
, error
, 0);
4174 * Do an nfs pathconf rpc.
4179 struct nfsv3_pathconf
*pc
,
4183 mbuf_t mreq
, mrep
, md
, mb
, mb2
;
4184 caddr_t bpos
, dpos
, cp
, cp2
;
4188 int attrflag
, error
= 0;
4189 struct nfsv3_pathconf
*mpc
;
4191 /* fetch pathconf info from server */
4192 nfsm_reqhead(NFSX_FH(1));
4196 nfsm_request(vp
, NFSPROC_PATHCONF
, procp
, cred
, &xid
);
4197 nfsm_postop_attr_update(vp
, 1, attrflag
, &xid
);
4199 nfsm_dissect(mpc
, struct nfsv3_pathconf
*, NFSX_V3PATHCONF
);
4200 pc
->pc_linkmax
= fxdr_unsigned(long, mpc
->pc_linkmax
);
4201 pc
->pc_namemax
= fxdr_unsigned(long, mpc
->pc_namemax
);
4202 pc
->pc_chownrestricted
= fxdr_unsigned(long, mpc
->pc_chownrestricted
);
4203 pc
->pc_notrunc
= fxdr_unsigned(long, mpc
->pc_notrunc
);
4204 pc
->pc_caseinsensitive
= fxdr_unsigned(long, mpc
->pc_caseinsensitive
);
4205 pc
->pc_casepreserving
= fxdr_unsigned(long, mpc
->pc_casepreserving
);
4213 nfs_pathconf_cache(struct nfsmount
*nmp
, struct nfsv3_pathconf
*pc
)
4215 nmp
->nm_state
|= NFSSTA_GOTPATHCONF
;
4216 nmp
->nm_fsinfo
.linkmax
= pc
->pc_linkmax
;
4217 nmp
->nm_fsinfo
.namemax
= pc
->pc_namemax
;
4218 nmp
->nm_fsinfo
.pcflags
= 0;
4220 nmp
->nm_fsinfo
.pcflags
|= NFSPCINFO_NOTRUNC
;
4221 if (pc
->pc_chownrestricted
)
4222 nmp
->nm_fsinfo
.pcflags
|= NFSPCINFO_CHOWN_RESTRICTED
;
4223 if (pc
->pc_caseinsensitive
)
4224 nmp
->nm_fsinfo
.pcflags
|= NFSPCINFO_CASE_INSENSITIVE
;
4225 if (pc
->pc_casepreserving
)
4226 nmp
->nm_fsinfo
.pcflags
|= NFSPCINFO_CASE_PRESERVING
;
4230 * Return POSIX pathconf information applicable to nfs.
4232 * The NFS V2 protocol doesn't support this, so just return EINVAL
4238 struct vnop_pathconf_args
/* {
4239 struct vnodeop_desc *a_desc;
4242 register_t *a_retval;
4243 vfs_context_t a_context;
4246 vnode_t vp
= ap
->a_vp
;
4247 struct nfsmount
*nmp
;
4248 struct nfsv3_pathconf pc
;
4249 int error
= 0, cached
;
4251 nmp
= VFSTONFS(vnode_mount(vp
));
4257 switch (ap
->a_name
) {
4260 case _PC_CHOWN_RESTRICTED
:
4262 case _PC_CASE_SENSITIVE
:
4263 case _PC_CASE_PRESERVING
:
4266 /* don't bother contacting the server if we know the answer */
4270 if (!(nmp
->nm_state
& NFSSTA_GOTPATHCONF
)) {
4271 /* no pathconf info cached */
4272 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
4273 proc_t p
= vfs_context_proc(ap
->a_context
);
4274 error
= nfs_pathconfrpc(vp
, &pc
, cred
, p
);
4277 nmp
= VFSTONFS(vnode_mount(vp
));
4280 if (!(nmp
->nm_state
& NFSSTA_GOTFSINFO
)) {
4281 nfs_fsinfo(nmp
, vp
, cred
, p
);
4282 nmp
= VFSTONFS(vnode_mount(vp
));
4286 if ((nmp
->nm_state
& NFSSTA_GOTFSINFO
) &&
4287 (nmp
->nm_fsinfo
.fsproperties
& NFSV3FSINFO_HOMOGENEOUS
)) {
4288 /* all files have the same pathconf info, */
4289 /* so cache a copy of the results */
4290 nfs_pathconf_cache(nmp
, &pc
);
4294 cached
= (nmp
->nm_state
& NFSSTA_GOTPATHCONF
);
4296 switch (ap
->a_name
) {
4298 *ap
->a_retval
= cached
? nmp
->nm_fsinfo
.linkmax
: pc
.pc_linkmax
;
4301 *ap
->a_retval
= cached
? nmp
->nm_fsinfo
.namemax
: pc
.pc_namemax
;
4303 case _PC_CHOWN_RESTRICTED
:
4305 *ap
->a_retval
= (nmp
->nm_fsinfo
.pcflags
& NFSPCINFO_CHOWN_RESTRICTED
) ? 1 : 0;
4307 *ap
->a_retval
= pc
.pc_chownrestricted
;
4311 *ap
->a_retval
= (nmp
->nm_fsinfo
.pcflags
& NFSPCINFO_NOTRUNC
) ? 1 : 0;
4313 *ap
->a_retval
= pc
.pc_notrunc
;
4315 case _PC_CASE_SENSITIVE
:
4317 *ap
->a_retval
= (nmp
->nm_fsinfo
.pcflags
& NFSPCINFO_CASE_INSENSITIVE
) ? 0 : 1;
4319 *ap
->a_retval
= !pc
.pc_caseinsensitive
;
4321 case _PC_CASE_PRESERVING
:
4323 *ap
->a_retval
= (nmp
->nm_fsinfo
.pcflags
& NFSPCINFO_CASE_PRESERVING
) ? 1 : 0;
4325 *ap
->a_retval
= pc
.pc_casepreserving
;
4335 * NFS advisory byte-level locks (client)
4339 struct vnop_advlock_args
/* {
4340 struct vnodeop_desc *a_desc;
4346 vfs_context_t a_context;
4349 return (nfs_dolock(ap
));
4353 * write (or commit) the given NFS buffer
4356 nfs_buf_write(struct nfsbuf
*bp
)
4358 int oldflags
= bp
->nb_flags
, rv
= 0;
4359 vnode_t vp
= bp
->nb_vp
;
4360 struct nfsnode
*np
= VTONFS(vp
);
4362 proc_t p
= current_proc(); // XXX
4364 FSDBG_TOP(553, bp
, NBOFF(bp
), bp
->nb_flags
, 0);
4366 if (!ISSET(bp
->nb_lflags
, NBL_BUSY
))
4367 panic("nfs_buf_write: buffer is not busy???");
4369 CLR(bp
->nb_flags
, (NB_READ
|NB_DONE
|NB_ERROR
|NB_DELWRI
));
4370 if (ISSET(oldflags
, NB_DELWRI
)) {
4371 OSAddAtomic(-1, (SInt32
*)&nfs_nbdwrite
);
4373 wakeup(&nfs_nbdwrite
);
4376 /* move to clean list */
4377 if (ISSET(oldflags
, (NB_ASYNC
|NB_DELWRI
))) {
4378 lck_mtx_lock(nfs_buf_mutex
);
4379 if (bp
->nb_vnbufs
.le_next
!= NFSNOLIST
)
4380 LIST_REMOVE(bp
, nb_vnbufs
);
4381 LIST_INSERT_HEAD(&VTONFS(vp
)->n_cleanblkhd
, bp
, nb_vnbufs
);
4382 lck_mtx_unlock(nfs_buf_mutex
);
4384 vnode_startwrite(vp
);
4386 if (p
&& p
->p_stats
)
4387 p
->p_stats
->p_ru
.ru_oublock
++;
4390 * For async requests when nfsiod(s) are running, queue the request by
4391 * calling nfs_asyncio(), otherwise just all nfs_doio() to do the request.
4393 if (ISSET(bp
->nb_flags
, NB_ASYNC
))
4395 if (ISSET(bp
->nb_flags
, NB_READ
))
4399 if (!ISSET(bp
->nb_flags
, NB_ASYNC
) || nfs_asyncio(bp
, NOCRED
))
4400 rv
= nfs_doio(bp
, cr
, p
);
4402 if ((oldflags
& NB_ASYNC
) == 0) {
4403 rv
= nfs_buf_iowait(bp
);
4404 /* move to clean list */
4405 if (oldflags
& NB_DELWRI
) {
4406 lck_mtx_lock(nfs_buf_mutex
);
4407 if (bp
->nb_vnbufs
.le_next
!= NFSNOLIST
)
4408 LIST_REMOVE(bp
, nb_vnbufs
);
4409 LIST_INSERT_HEAD(&VTONFS(vp
)->n_cleanblkhd
, bp
, nb_vnbufs
);
4410 lck_mtx_unlock(nfs_buf_mutex
);
4412 oldflags
= bp
->nb_flags
;
4413 FSDBG_BOT(553, bp
, NBOFF(bp
), bp
->nb_flags
, rv
);
4414 if (IS_VALID_CRED(cr
)) {
4417 nfs_buf_release(bp
, 1);
4418 if (ISSET(oldflags
, NB_ERROR
) && !(np
->n_flag
& NFLUSHINPROG
)) {
4420 * There was a write error and we need to
4421 * invalidate attrs and flush buffers in
4422 * order to sync up with the server.
4423 * (if this write was extending the file,
4424 * we may no longer know the correct size)
4426 * But we couldn't call vinvalbuf while holding
4427 * the buffer busy. So we call vinvalbuf() after
4428 * releasing the buffer.
4430 nfs_vinvalbuf(vp
, V_SAVE
|V_IGNORE_WRITEERR
, cr
, p
, 1);
4432 if (IS_VALID_CRED(cr
))
4433 kauth_cred_unref(&cr
);
4437 FSDBG_BOT(553, bp
, NBOFF(bp
), bp
->nb_flags
, rv
);
4442 * Read wrapper for special devices.
4446 struct vnop_read_args
/* {
4447 struct vnodeop_desc *a_desc;
4451 vfs_context_t a_context;
4454 register struct nfsnode
*np
= VTONFS(ap
->a_vp
);
4462 np
->n_atim
.tv_sec
= now
.tv_sec
;
4463 np
->n_atim
.tv_nsec
= now
.tv_usec
* 1000;
4464 return (VOCALL(spec_vnodeop_p
, VOFFSET(vnop_read
), ap
));
4468 * Write wrapper for special devices.
4472 struct vnop_write_args
/* {
4473 struct vnodeop_desc *a_desc;
4477 vfs_context_t a_context;
4480 register struct nfsnode
*np
= VTONFS(ap
->a_vp
);
4488 np
->n_mtim
.tv_sec
= now
.tv_sec
;
4489 np
->n_mtim
.tv_nsec
= now
.tv_usec
* 1000;
4490 return (VOCALL(spec_vnodeop_p
, VOFFSET(vnop_write
), ap
));
4494 * Close wrapper for special devices.
4496 * Update the times on the nfsnode then do device close.
4500 struct vnop_close_args
/* {
4501 struct vnodeop_desc *a_desc;
4504 vfs_context_t a_context;
4507 vnode_t vp
= ap
->a_vp
;
4508 struct nfsnode
*np
= VTONFS(vp
);
4509 struct vnode_attr vattr
;
4512 if (np
->n_flag
& (NACC
| NUPD
)) {
4514 if (!vnode_isinuse(vp
, 1) && (mp
= vnode_mount(vp
)) && !vfs_isrdonly(mp
)) {
4516 if (np
->n_flag
& NACC
) {
4517 vattr
.va_access_time
= np
->n_atim
;
4518 VATTR_SET_ACTIVE(&vattr
, va_access_time
);
4520 if (np
->n_flag
& NUPD
) {
4521 vattr
.va_modify_time
= np
->n_mtim
;
4522 VATTR_SET_ACTIVE(&vattr
, va_modify_time
);
4524 vnode_setattr(vp
, &vattr
, ap
->a_context
);
4527 return (VOCALL(spec_vnodeop_p
, VOFFSET(vnop_close
), ap
));
4530 extern vnop_t
**fifo_vnodeop_p
;
4533 * Read wrapper for fifos.
4537 struct vnop_read_args
/* {
4538 struct vnodeop_desc *a_desc;
4542 vfs_context_t a_context;
4545 register struct nfsnode
*np
= VTONFS(ap
->a_vp
);
4553 np
->n_atim
.tv_sec
= now
.tv_sec
;
4554 np
->n_atim
.tv_nsec
= now
.tv_usec
* 1000;
4555 return (VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_read
), ap
));
4559 * Write wrapper for fifos.
4563 struct vnop_write_args
/* {
4564 struct vnodeop_desc *a_desc;
4568 vfs_context_t a_context;
4571 register struct nfsnode
*np
= VTONFS(ap
->a_vp
);
4579 np
->n_mtim
.tv_sec
= now
.tv_sec
;
4580 np
->n_mtim
.tv_nsec
= now
.tv_usec
* 1000;
4581 return (VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_write
), ap
));
4585 * Close wrapper for fifos.
4587 * Update the times on the nfsnode then do fifo close.
4591 struct vnop_close_args
/* {
4592 struct vnodeop_desc *a_desc;
4595 vfs_context_t a_context;
4598 vnode_t vp
= ap
->a_vp
;
4599 struct nfsnode
*np
= VTONFS(vp
);
4600 struct vnode_attr vattr
;
4604 if (np
->n_flag
& (NACC
| NUPD
)) {
4606 if (np
->n_flag
& NACC
) {
4607 np
->n_atim
.tv_sec
= now
.tv_sec
;
4608 np
->n_atim
.tv_nsec
= now
.tv_usec
* 1000;
4610 if (np
->n_flag
& NUPD
) {
4611 np
->n_mtim
.tv_sec
= now
.tv_sec
;
4612 np
->n_mtim
.tv_nsec
= now
.tv_usec
* 1000;
4615 if (!vnode_isinuse(vp
, 1) && (mp
= vnode_mount(vp
)) && !vfs_isrdonly(mp
)) {
4617 if (np
->n_flag
& NACC
) {
4618 vattr
.va_access_time
= np
->n_atim
;
4619 VATTR_SET_ACTIVE(&vattr
, va_access_time
);
4621 if (np
->n_flag
& NUPD
) {
4622 vattr
.va_modify_time
= np
->n_mtim
;
4623 VATTR_SET_ACTIVE(&vattr
, va_modify_time
);
4625 vnode_setattr(vp
, &vattr
, ap
->a_context
);
4628 return (VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_close
), ap
));
4634 __unused
struct vnop_ioctl_args
/* {
4635 struct vnodeop_desc *a_desc;
4640 kauth_cred_t a_cred;
4646 * XXX we were once bogusly enoictl() which returned this (ENOTTY).
4647 * Probably we should return ENODEV.
4655 __unused
struct vnop_select_args
/* {
4656 struct vnodeop_desc *a_desc;
4660 kauth_cred_t a_cred;
4667 * We were once bogusly seltrue() which returns 1. Is this right?
4673 * Vnode op for pagein using getblk_pages
4674 * derived from nfs_bioread()
4675 * No read aheads are started from pagein operation
4679 struct vnop_pagein_args
/* {
4680 struct vnodeop_desc *a_desc;
4683 vm_offset_t a_pl_offset;
4687 vfs_context_t a_context;
4690 vnode_t vp
= ap
->a_vp
;
4691 upl_t pl
= ap
->a_pl
;
4692 size_t size
= ap
->a_size
;
4693 off_t f_offset
= ap
->a_f_offset
;
4694 vm_offset_t pl_offset
= ap
->a_pl_offset
;
4695 int flags
= ap
->a_flags
;
4698 struct nfsnode
*np
= VTONFS(vp
);
4699 int biosize
, xsize
, iosize
;
4700 struct nfsmount
*nmp
;
4704 struct iovec_32 aiov
;
4705 struct uio
* uio
= &auio
;
4706 int nofreeupl
= flags
& UPL_NOCOMMIT
;
4707 upl_page_info_t
*plinfo
;
4709 FSDBG(322, vp
, f_offset
, size
, flags
);
4710 if (pl
== (upl_t
)NULL
)
4711 panic("nfs_pagein: no upl");
4713 if (UBCINVALID(vp
)) {
4714 printf("nfs_pagein: invalid vnode 0x%x", (int)vp
);
4716 (void) ubc_upl_abort(pl
, 0);
4719 UBCINFOCHECK("nfs_pagein", vp
);
4722 printf("nfs_pagein: invalid size %d", size
);
4724 (void) ubc_upl_abort(pl
, 0);
4727 if (f_offset
< 0 || f_offset
>= (off_t
)np
->n_size
|| (f_offset
& PAGE_MASK_64
)) {
4729 ubc_upl_abort_range(pl
, pl_offset
, size
,
4730 UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
4734 cred
= ubc_getcred(vp
);
4735 if (!IS_VALID_CRED(cred
))
4736 cred
= vfs_context_ucred(ap
->a_context
);
4737 p
= vfs_context_proc(ap
->a_context
);
4739 auio
.uio_offset
= f_offset
;
4740 #if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
4741 auio
.uio_segflg
= UIO_SYSSPACE
;
4743 auio
.uio_segflg
= UIO_SYSSPACE32
;
4745 auio
.uio_rw
= UIO_READ
;
4748 nmp
= VFSTONFS(vnode_mount(vp
));
4751 ubc_upl_abort_range(pl
, pl_offset
, size
,
4752 UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
4755 biosize
= nmp
->nm_biosize
;
4756 if ((nmp
->nm_flag
& NFSMNT_NFSV3
) && !(nmp
->nm_state
& NFSSTA_GOTFSINFO
))
4757 nfs_fsinfo(nmp
, vp
, cred
, p
);
4759 plinfo
= ubc_upl_pageinfo(pl
);
4760 ubc_upl_map(pl
, &ioaddr
);
4761 ioaddr
+= pl_offset
;
4766 * It would be nice to be able to issue all these requests
4767 * in parallel instead of waiting for each one to complete
4768 * before sending the next one.
4769 * XXX Should we align these requests to block boundaries?
4771 iosize
= min(biosize
, xsize
);
4772 aiov
.iov_len
= iosize
;
4773 aiov
.iov_base
= (uintptr_t)ioaddr
;
4774 auio
.uio_iovs
.iov32p
= &aiov
;
4775 auio
.uio_iovcnt
= 1;
4776 uio_uio_resid_set(&auio
, iosize
);
4778 FSDBG(322, uio
->uio_offset
, uio_uio_resid(uio
), ioaddr
, xsize
);
4780 * With UBC we get here only when the file data is not in the VM
4781 * page cache, so go ahead and read in.
4784 upl_ubc_alias_set(pl
, current_thread(), 2);
4785 #endif /* UPL_DEBUG */
4786 OSAddAtomic(1, (SInt32
*)&nfsstats
.pageins
);
4788 error
= nfs_readrpc(vp
, uio
, cred
, p
);
4791 if (uio_uio_resid(uio
)) {
4793 * If uio_resid > 0, there is a hole in the file
4794 * and no writes after the hole have been pushed
4795 * to the server yet... or we're at the EOF
4796 * Just zero fill the rest of the valid area.
4798 // LP64todo - fix this
4799 int zcnt
= uio_uio_resid(uio
);
4800 int zoff
= iosize
- zcnt
;
4801 bzero((char *)ioaddr
+ zoff
, zcnt
);
4803 FSDBG(324, uio
->uio_offset
, zoff
, zcnt
, ioaddr
);
4804 uio
->uio_offset
+= zcnt
;
4809 FSDBG(322, uio
->uio_offset
, uio_uio_resid(uio
), error
, -1);
4812 nmp
= VFSTONFS(vnode_mount(vp
));
4813 } while (error
== 0 && xsize
> 0);
4819 ubc_upl_abort_range(pl
, pl_offset
, size
,
4821 UPL_ABORT_FREE_ON_EMPTY
);
4823 ubc_upl_commit_range(pl
, pl_offset
, size
,
4824 UPL_COMMIT_CLEAR_DIRTY
|
4825 UPL_COMMIT_FREE_ON_EMPTY
);
4832 * Vnode op for pageout using UPL
4833 * Derived from nfs_write()
4834 * File size changes are not permitted in pageout.
4838 struct vnop_pageout_args
/* {
4839 struct vnodeop_desc *a_desc;
4842 vm_offset_t a_pl_offset;
4846 vfs_context_t a_context;
4849 vnode_t vp
= ap
->a_vp
;
4850 upl_t pl
= ap
->a_pl
;
4851 size_t size
= ap
->a_size
;
4852 off_t f_offset
= ap
->a_f_offset
;
4853 vm_offset_t pl_offset
= ap
->a_pl_offset
;
4854 int flags
= ap
->a_flags
;
4855 struct nfsnode
*np
= VTONFS(vp
);
4859 struct nfsmount
*nmp
= VFSTONFS(vnode_mount(vp
));
4861 int error
= 0, iomode
;
4865 struct iovec_32 aiov
;
4866 int nofreeupl
= flags
& UPL_NOCOMMIT
;
4867 size_t biosize
, iosize
, pgsize
, xsize
;
4869 FSDBG(323, f_offset
, size
, pl
, pl_offset
);
4871 if (pl
== (upl_t
)NULL
)
4872 panic("nfs_pageout: no upl");
4874 if (UBCINVALID(vp
)) {
4875 printf("nfs_pageout: invalid vnode 0x%x", (int)vp
);
4877 ubc_upl_abort(pl
, 0);
4880 UBCINFOCHECK("nfs_pageout", vp
);
4883 printf("nfs_pageout: invalid size %d", size
);
4885 ubc_upl_abort(pl
, 0);
4891 ubc_upl_abort(pl
, UPL_ABORT_DUMP_PAGES
|UPL_ABORT_FREE_ON_EMPTY
);
4894 biosize
= nmp
->nm_biosize
;
4897 * Check to see whether the buffer is incore.
4898 * If incore and not busy, invalidate it from the cache.
4900 for (iosize
= 0; iosize
< size
; iosize
+= xsize
) {
4901 off
= f_offset
+ iosize
;
4902 /* need make sure we do things on block boundaries */
4903 xsize
= biosize
- (off
% biosize
);
4904 if (off
+ xsize
> f_offset
+ size
)
4905 xsize
= f_offset
+ size
- off
;
4906 lbn
= ubc_offtoblk(vp
, off
);
4907 lck_mtx_lock(nfs_buf_mutex
);
4908 if ((bp
= nfs_buf_incore(vp
, lbn
))) {
4909 FSDBG(323, off
, bp
, bp
->nb_lflags
, bp
->nb_flags
);
4910 if (nfs_buf_acquire(bp
, NBAC_NOWAIT
, 0, 0)) {
4911 lck_mtx_unlock(nfs_buf_mutex
);
4912 /* no panic. just tell vm we are busy */
4914 ubc_upl_abort(pl
, 0);
4917 if (bp
->nb_dirtyend
> 0) {
4919 * if there's a dirty range in the buffer, check
4920 * to see if it extends beyond the pageout region
4922 * if the dirty region lies completely within the
4923 * pageout region, we just invalidate the buffer
4924 * because it's all being written out now anyway.
4926 * if any of the dirty region lies outside the
4927 * pageout region, we'll try to clip the dirty
4928 * region to eliminate the portion that's being
4929 * paged out. If that's not possible, because
4930 * the dirty region extends before and after the
4931 * pageout region, then we'll just return EBUSY.
4933 off_t boff
, start
, end
;
4937 /* clip end to EOF */
4938 if (end
> (off_t
)np
->n_size
)
4942 if ((bp
->nb_dirtyoff
< start
) &&
4943 (bp
->nb_dirtyend
> end
)) {
4944 /* not gonna be able to clip the dirty region */
4945 FSDBG(323, vp
, bp
, 0xd00deebc, EBUSY
);
4947 lck_mtx_unlock(nfs_buf_mutex
);
4949 ubc_upl_abort(pl
, 0);
4952 if ((bp
->nb_dirtyoff
< start
) ||
4953 (bp
->nb_dirtyend
> end
)) {
4954 /* clip dirty region, if necessary */
4955 if (bp
->nb_dirtyoff
< start
)
4956 bp
->nb_dirtyend
= min(bp
->nb_dirtyend
, start
);
4957 if (bp
->nb_dirtyend
> end
)
4958 bp
->nb_dirtyoff
= max(bp
->nb_dirtyoff
, end
);
4959 FSDBG(323, bp
, bp
->nb_dirtyoff
, bp
->nb_dirtyend
, 0xd00dee00);
4960 /* we're leaving this block dirty */
4962 lck_mtx_unlock(nfs_buf_mutex
);
4966 nfs_buf_remfree(bp
);
4967 lck_mtx_unlock(nfs_buf_mutex
);
4968 SET(bp
->nb_flags
, NB_INVAL
);
4969 if (ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
)) {
4970 CLR(bp
->nb_flags
, NB_NEEDCOMMIT
);
4971 np
->n_needcommitcnt
--;
4972 CHECK_NEEDCOMMITCNT(np
);
4974 nfs_buf_release(bp
, 1);
4976 lck_mtx_unlock(nfs_buf_mutex
);
4980 cred
= ubc_getcred(vp
);
4981 if (!IS_VALID_CRED(cred
))
4982 cred
= vfs_context_ucred(ap
->a_context
);
4983 p
= vfs_context_proc(ap
->a_context
);
4985 if (np
->n_flag
& NWRITEERR
) {
4986 np
->n_flag
&= ~NWRITEERR
;
4988 ubc_upl_abort_range(pl
, pl_offset
, size
,
4989 UPL_ABORT_FREE_ON_EMPTY
);
4990 return (np
->n_error
);
4992 if ((nmp
->nm_flag
& NFSMNT_NFSV3
) && !(nmp
->nm_state
& NFSSTA_GOTFSINFO
))
4993 nfs_fsinfo(nmp
, vp
, cred
, p
);
4995 if (f_offset
< 0 || f_offset
>= (off_t
)np
->n_size
||
4996 f_offset
& PAGE_MASK_64
|| size
& PAGE_MASK_64
) {
4998 ubc_upl_abort_range(pl
, pl_offset
, size
,
4999 UPL_ABORT_FREE_ON_EMPTY
);
5003 ubc_upl_map(pl
, &ioaddr
);
5004 ioaddr
+= pl_offset
;
5006 if ((u_quad_t
)f_offset
+ size
> np
->n_size
)
5007 xsize
= np
->n_size
- f_offset
;
5011 pgsize
= round_page_64(xsize
);
5012 if (size
> pgsize
) {
5014 ubc_upl_abort_range(pl
, pl_offset
+ pgsize
,
5016 UPL_ABORT_FREE_ON_EMPTY
);
5020 * check for partial page and clear the
5021 * contents past end of the file before
5022 * releasing it in the VM page cache
5024 if ((u_quad_t
)f_offset
< np
->n_size
&& (u_quad_t
)f_offset
+ size
> np
->n_size
) {
5025 size_t io
= np
->n_size
- f_offset
;
5026 bzero((caddr_t
)(ioaddr
+ io
), size
- io
);
5027 FSDBG(321, np
->n_size
, f_offset
, f_offset
+ io
, size
- io
);
5030 auio
.uio_offset
= f_offset
;
5031 #if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
5032 auio
.uio_segflg
= UIO_SYSSPACE
;
5034 auio
.uio_segflg
= UIO_SYSSPACE32
;
5036 auio
.uio_rw
= UIO_READ
;
5041 * It would be nice to be able to issue all these requests
5042 * in parallel instead of waiting for each one to complete
5043 * before sending the next one.
5044 * XXX Should we align these requests to block boundaries?
5046 iosize
= min(biosize
, xsize
);
5047 uio_uio_resid_set(&auio
, iosize
);
5048 aiov
.iov_len
= iosize
;
5049 aiov
.iov_base
= (uintptr_t)ioaddr
;
5050 auio
.uio_iovs
.iov32p
= &aiov
;
5051 auio
.uio_iovcnt
= 1;
5053 FSDBG(323, auio
.uio_offset
, uio_uio_resid(&auio
), ioaddr
, xsize
);
5054 OSAddAtomic(1, (SInt32
*)&nfsstats
.pageouts
);
5056 vnode_startwrite(vp
);
5058 /* NMODIFIED would be set here if doing unstable writes */
5059 iomode
= NFSV3WRITE_FILESYNC
;
5060 error
= nfs_writerpc(vp
, &auio
, cred
, p
, &iomode
, NULL
);
5061 vnode_writedone(vp
);
5064 /* Note: no need to check uio_resid, because */
5065 /* it'll only be set if there was an error. */
5068 } while (xsize
> 0);
5073 * We've had several different solutions on what to do when the pageout
5074 * gets an error. If we don't handle it, and return an error to the
5075 * caller, vm, it will retry . This can end in endless looping
5076 * between vm and here doing retries of the same page. Doing a dump
5077 * back to vm, will get it out of vm's knowledge and we lose whatever
5078 * data existed. This is risky, but in some cases necessary. For
5079 * example, the initial fix here was to do that for ESTALE. In that case
5080 * the server is telling us that the file is no longer the same. We
5081 * would not want to keep paging out to that. We also saw some 151
5082 * errors from Auspex server and NFSv3 can return errors higher than
5083 * ELAST. Those along with NFS known server errors we will "dump" from
5084 * vm. Errors we don't expect to occur, we dump and log for further
5085 * analysis. Errors that could be transient, networking ones,
5086 * we let vm "retry". Lastly, errors that we retry, but may have potential
5087 * to storm the network, we "retrywithsleep". "sever" will be used in
5088 * in the future to dump all pages of object for cases like ESTALE.
5089 * All this is the basis for the states returned and first guesses on
5090 * error handling. Tweaking expected as more statistics are gathered.
5091 * Note, in the long run we may need another more robust solution to
5092 * have some kind of persistant store when the vm cannot dump nor keep
5093 * retrying as a solution, but this would be a file architectural change
5096 if (!nofreeupl
) { /* otherwise stacked file system has to handle this */
5099 short action
= nfs_pageouterrorhandler(error
);
5103 abortflags
= UPL_ABORT_DUMP_PAGES
|UPL_ABORT_FREE_ON_EMPTY
;
5106 abortflags
= UPL_ABORT_DUMP_PAGES
|UPL_ABORT_FREE_ON_EMPTY
;
5107 if (error
<= ELAST
&&
5108 (errorcount
[error
] % 100 == 0))
5109 printf("nfs_pageout: unexpected error %d. dumping vm page\n", error
);
5110 errorcount
[error
]++;
5113 abortflags
= UPL_ABORT_FREE_ON_EMPTY
;
5115 case RETRYWITHSLEEP
:
5116 abortflags
= UPL_ABORT_FREE_ON_EMPTY
;
5117 /* pri unused. PSOCK for placeholder. */
5118 tsleep(&lbolt
, PSOCK
, "nfspageout", 0);
5120 case SEVER
: /* not implemented */
5122 printf("nfs_pageout: action %d not expected\n", action
);
5126 ubc_upl_abort_range(pl
, pl_offset
, size
, abortflags
);
5127 /* return error in all cases above */
5130 ubc_upl_commit_range(pl
, pl_offset
, pgsize
,
5131 UPL_COMMIT_CLEAR_DIRTY
|
5132 UPL_COMMIT_FREE_ON_EMPTY
);
5137 /* Blktooff derives file offset given a logical block number */
5140 struct vnop_blktooff_args
/* {
5141 struct vnodeop_desc *a_desc;
5148 vnode_t vp
= ap
->a_vp
;
5149 struct nfsmount
*nmp
= VFSTONFS(vnode_mount(vp
));
5153 biosize
= nmp
->nm_biosize
;
5155 *ap
->a_offset
= (off_t
)(ap
->a_lblkno
* biosize
);
5162 struct vnop_offtoblk_args
/* {
5163 struct vnodeop_desc *a_desc;
5166 daddr64_t *a_lblkno;
5170 vnode_t vp
= ap
->a_vp
;
5171 struct nfsmount
*nmp
= VFSTONFS(vnode_mount(vp
));
5175 biosize
= nmp
->nm_biosize
;
5177 *ap
->a_lblkno
= (daddr64_t
)(ap
->a_offset
/ biosize
);