]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_vnops.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_vnops.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31 /*
32 * Copyright (c) 1989, 1993
33 * The Regents of the University of California. All rights reserved.
34 *
35 * This code is derived from software contributed to Berkeley by
36 * Rick Macklem at The University of Guelph.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)nfs_vnops.c 8.16 (Berkeley) 5/27/95
67 * FreeBSD-Id: nfs_vnops.c,v 1.72 1997/11/07 09:20:48 phk Exp $
68 */
69
70
71 /*
72 * vnode op calls for Sun NFS version 2 and 3
73 */
74 #include <sys/param.h>
75 #include <sys/kernel.h>
76 #include <sys/systm.h>
77 #include <sys/resourcevar.h>
78 #include <sys/proc_internal.h>
79 #include <sys/kauth.h>
80 #include <sys/mount_internal.h>
81 #include <sys/malloc.h>
82 #include <sys/kpi_mbuf.h>
83 #include <sys/conf.h>
84 #include <sys/vnode_internal.h>
85 #include <sys/dirent.h>
86 #include <sys/fcntl.h>
87 #include <sys/lockf.h>
88 #include <sys/ubc_internal.h>
89 #include <sys/attr.h>
90 #include <sys/signalvar.h>
91 #include <sys/uio_internal.h>
92
93 #include <vfs/vfs_support.h>
94
95 #include <sys/vm.h>
96
97 #include <sys/time.h>
98 #include <kern/clock.h>
99 #include <libkern/OSAtomic.h>
100
101 #include <miscfs/fifofs/fifo.h>
102 #include <miscfs/specfs/specdev.h>
103
104 #include <nfs/rpcv2.h>
105 #include <nfs/nfsproto.h>
106 #include <nfs/nfs.h>
107 #include <nfs/nfsnode.h>
108 #include <nfs/nfsmount.h>
109 #include <nfs/nfs_lock.h>
110 #include <nfs/xdr_subs.h>
111 #include <nfs/nfsm_subs.h>
112
113 #include <net/if.h>
114 #include <netinet/in.h>
115 #include <netinet/in_var.h>
116 #include <vm/vm_kern.h>
117
118 #include <kern/task.h>
119 #include <kern/sched_prim.h>
120
121 #include <sys/kdebug.h>
122
123 #define FSDBG(A, B, C, D, E) \
124 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_NONE, \
125 (int)(B), (int)(C), (int)(D), (int)(E), 0)
126 #define FSDBG_TOP(A, B, C, D, E) \
127 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_START, \
128 (int)(B), (int)(C), (int)(D), (int)(E), 0)
129 #define FSDBG_BOT(A, B, C, D, E) \
130 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_END, \
131 (int)(B), (int)(C), (int)(D), (int)(E), 0)
132
133 static int nfsspec_read(struct vnop_read_args *);
134 static int nfsspec_write(struct vnop_write_args *);
135 static int nfsfifo_read(struct vnop_read_args *);
136 static int nfsfifo_write(struct vnop_write_args *);
137 static int nfsspec_close(struct vnop_close_args *);
138 static int nfsfifo_close(struct vnop_close_args *);
139 static int nfs_ioctl(struct vnop_ioctl_args *);
140 static int nfs_select(struct vnop_select_args *);
141 static int nfs_setattrrpc(vnode_t,struct vnode_attr *,kauth_cred_t,proc_t);
142 static int nfs_lookup(struct vnop_lookup_args *);
143 static int nfs_create(struct vnop_create_args *);
144 static int nfs_mknod(struct vnop_mknod_args *);
145 static int nfs_open(struct vnop_open_args *);
146 static int nfs_close(struct vnop_close_args *);
147 static int nfs_access(struct vnop_access_args *);
148 static int nfs_vnop_getattr(struct vnop_getattr_args *);
149 static int nfs_setattr(struct vnop_setattr_args *);
150 static int nfs_read(struct vnop_read_args *);
151 static int nfs_mmap(struct vnop_mmap_args *);
152 static int nfs_fsync(struct vnop_fsync_args *);
153 static int nfs_remove(struct vnop_remove_args *);
154 static int nfs_link(struct vnop_link_args *);
155 static int nfs_rename(struct vnop_rename_args *);
156 static int nfs_mkdir(struct vnop_mkdir_args *);
157 static int nfs_rmdir(struct vnop_rmdir_args *);
158 static int nfs_symlink(struct vnop_symlink_args *);
159 static int nfs_readdir(struct vnop_readdir_args *);
160 static int nfs_lookitup(vnode_t,char *,int,kauth_cred_t,proc_t,struct nfsnode **);
161 static int nfs_sillyrename(vnode_t,vnode_t,struct componentname *,kauth_cred_t,proc_t);
162 static int nfs_readlink(struct vnop_readlink_args *);
163 static int nfs_pathconf(struct vnop_pathconf_args *);
164 static int nfs_advlock(struct vnop_advlock_args *);
165 static int nfs_pagein(struct vnop_pagein_args *);
166 static int nfs_pageout(struct vnop_pageout_args *);
167 static int nfs_blktooff(struct vnop_blktooff_args *);
168 static int nfs_offtoblk(struct vnop_offtoblk_args *);
169 static int nfs_blockmap(struct vnop_blockmap_args *);
170
171 /*
172 * Global vfs data structures for nfs
173 */
174 vnop_t **nfsv2_vnodeop_p;
175 static struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
176 { &vnop_default_desc, (vnop_t *)vn_default_error },
177 { &vnop_lookup_desc, (vnop_t *)nfs_lookup }, /* lookup */
178 { &vnop_create_desc, (vnop_t *)nfs_create }, /* create */
179 { &vnop_mknod_desc, (vnop_t *)nfs_mknod }, /* mknod */
180 { &vnop_open_desc, (vnop_t *)nfs_open }, /* open */
181 { &vnop_close_desc, (vnop_t *)nfs_close }, /* close */
182 { &vnop_access_desc, (vnop_t *)nfs_access }, /* access */
183 { &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr }, /* getattr */
184 { &vnop_setattr_desc, (vnop_t *)nfs_setattr }, /* setattr */
185 { &vnop_read_desc, (vnop_t *)nfs_read }, /* read */
186 { &vnop_write_desc, (vnop_t *)nfs_write }, /* write */
187 { &vnop_ioctl_desc, (vnop_t *)nfs_ioctl }, /* ioctl */
188 { &vnop_select_desc, (vnop_t *)nfs_select }, /* select */
189 { &vnop_revoke_desc, (vnop_t *)nfs_revoke }, /* revoke */
190 { &vnop_mmap_desc, (vnop_t *)nfs_mmap }, /* mmap */
191 { &vnop_fsync_desc, (vnop_t *)nfs_fsync }, /* fsync */
192 { &vnop_remove_desc, (vnop_t *)nfs_remove }, /* remove */
193 { &vnop_link_desc, (vnop_t *)nfs_link }, /* link */
194 { &vnop_rename_desc, (vnop_t *)nfs_rename }, /* rename */
195 { &vnop_mkdir_desc, (vnop_t *)nfs_mkdir }, /* mkdir */
196 { &vnop_rmdir_desc, (vnop_t *)nfs_rmdir }, /* rmdir */
197 { &vnop_symlink_desc, (vnop_t *)nfs_symlink }, /* symlink */
198 { &vnop_readdir_desc, (vnop_t *)nfs_readdir }, /* readdir */
199 { &vnop_readlink_desc, (vnop_t *)nfs_readlink }, /* readlink */
200 { &vnop_inactive_desc, (vnop_t *)nfs_inactive }, /* inactive */
201 { &vnop_reclaim_desc, (vnop_t *)nfs_reclaim }, /* reclaim */
202 { &vnop_strategy_desc, (vnop_t *)err_strategy }, /* strategy */
203 { &vnop_pathconf_desc, (vnop_t *)nfs_pathconf }, /* pathconf */
204 { &vnop_advlock_desc, (vnop_t *)nfs_advlock }, /* advlock */
205 { &vnop_bwrite_desc, (vnop_t *)err_bwrite }, /* bwrite */
206 { &vnop_pagein_desc, (vnop_t *)nfs_pagein }, /* Pagein */
207 { &vnop_pageout_desc, (vnop_t *)nfs_pageout }, /* Pageout */
208 { &vnop_copyfile_desc, (vnop_t *)err_copyfile }, /* Copyfile */
209 { &vnop_blktooff_desc, (vnop_t *)nfs_blktooff }, /* blktooff */
210 { &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk }, /* offtoblk */
211 { &vnop_blockmap_desc, (vnop_t *)nfs_blockmap }, /* blockmap */
212 { NULL, NULL }
213 };
214 struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
215 { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries };
216 #ifdef __FreeBSD__
217 VNODEOP_SET(nfsv2_vnodeop_opv_desc);
218 #endif
219
220 /*
221 * Special device vnode ops
222 */
223 vnop_t **spec_nfsv2nodeop_p;
224 static struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
225 { &vnop_default_desc, (vnop_t *)vn_default_error },
226 { &vnop_lookup_desc, (vnop_t *)spec_lookup }, /* lookup */
227 { &vnop_create_desc, (vnop_t *)spec_create }, /* create */
228 { &vnop_mknod_desc, (vnop_t *)spec_mknod }, /* mknod */
229 { &vnop_open_desc, (vnop_t *)spec_open }, /* open */
230 { &vnop_close_desc, (vnop_t *)nfsspec_close }, /* close */
231 { &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr }, /* getattr */
232 { &vnop_setattr_desc, (vnop_t *)nfs_setattr }, /* setattr */
233 { &vnop_read_desc, (vnop_t *)nfsspec_read }, /* read */
234 { &vnop_write_desc, (vnop_t *)nfsspec_write }, /* write */
235 { &vnop_ioctl_desc, (vnop_t *)spec_ioctl }, /* ioctl */
236 { &vnop_select_desc, (vnop_t *)spec_select }, /* select */
237 { &vnop_revoke_desc, (vnop_t *)spec_revoke }, /* revoke */
238 { &vnop_mmap_desc, (vnop_t *)spec_mmap }, /* mmap */
239 { &vnop_fsync_desc, (vnop_t *)nfs_fsync }, /* fsync */
240 { &vnop_remove_desc, (vnop_t *)spec_remove }, /* remove */
241 { &vnop_link_desc, (vnop_t *)spec_link }, /* link */
242 { &vnop_rename_desc, (vnop_t *)spec_rename }, /* rename */
243 { &vnop_mkdir_desc, (vnop_t *)spec_mkdir }, /* mkdir */
244 { &vnop_rmdir_desc, (vnop_t *)spec_rmdir }, /* rmdir */
245 { &vnop_symlink_desc, (vnop_t *)spec_symlink }, /* symlink */
246 { &vnop_readdir_desc, (vnop_t *)spec_readdir }, /* readdir */
247 { &vnop_readlink_desc, (vnop_t *)spec_readlink }, /* readlink */
248 { &vnop_inactive_desc, (vnop_t *)nfs_inactive }, /* inactive */
249 { &vnop_reclaim_desc, (vnop_t *)nfs_reclaim }, /* reclaim */
250 { &vnop_strategy_desc, (vnop_t *)spec_strategy }, /* strategy */
251 { &vnop_pathconf_desc, (vnop_t *)spec_pathconf }, /* pathconf */
252 { &vnop_advlock_desc, (vnop_t *)spec_advlock }, /* advlock */
253 { &vnop_bwrite_desc, (vnop_t *)vn_bwrite }, /* bwrite */
254 { &vnop_pagein_desc, (vnop_t *)nfs_pagein }, /* Pagein */
255 { &vnop_pageout_desc, (vnop_t *)nfs_pageout }, /* Pageout */
256 { &vnop_blktooff_desc, (vnop_t *)nfs_blktooff }, /* blktooff */
257 { &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk }, /* offtoblk */
258 { &vnop_blockmap_desc, (vnop_t *)nfs_blockmap }, /* blockmap */
259 { NULL, NULL }
260 };
261 struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
262 { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
263 #ifdef __FreeBSD__
264 VNODEOP_SET(spec_nfsv2nodeop_opv_desc);
265 #endif
266
267 vnop_t **fifo_nfsv2nodeop_p;
268 static struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
269 { &vnop_default_desc, (vnop_t *)vn_default_error },
270 { &vnop_lookup_desc, (vnop_t *)fifo_lookup }, /* lookup */
271 { &vnop_create_desc, (vnop_t *)fifo_create }, /* create */
272 { &vnop_mknod_desc, (vnop_t *)fifo_mknod }, /* mknod */
273 { &vnop_open_desc, (vnop_t *)fifo_open }, /* open */
274 { &vnop_close_desc, (vnop_t *)nfsfifo_close }, /* close */
275 { &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr }, /* getattr */
276 { &vnop_setattr_desc, (vnop_t *)nfs_setattr }, /* setattr */
277 { &vnop_read_desc, (vnop_t *)nfsfifo_read }, /* read */
278 { &vnop_write_desc, (vnop_t *)nfsfifo_write }, /* write */
279 { &vnop_ioctl_desc, (vnop_t *)fifo_ioctl }, /* ioctl */
280 { &vnop_select_desc, (vnop_t *)fifo_select }, /* select */
281 { &vnop_revoke_desc, (vnop_t *)fifo_revoke }, /* revoke */
282 { &vnop_mmap_desc, (vnop_t *)fifo_mmap }, /* mmap */
283 { &vnop_fsync_desc, (vnop_t *)nfs_fsync }, /* fsync */
284 { &vnop_remove_desc, (vnop_t *)fifo_remove }, /* remove */
285 { &vnop_link_desc, (vnop_t *)fifo_link }, /* link */
286 { &vnop_rename_desc, (vnop_t *)fifo_rename }, /* rename */
287 { &vnop_mkdir_desc, (vnop_t *)fifo_mkdir }, /* mkdir */
288 { &vnop_rmdir_desc, (vnop_t *)fifo_rmdir }, /* rmdir */
289 { &vnop_symlink_desc, (vnop_t *)fifo_symlink }, /* symlink */
290 { &vnop_readdir_desc, (vnop_t *)fifo_readdir }, /* readdir */
291 { &vnop_readlink_desc, (vnop_t *)fifo_readlink }, /* readlink */
292 { &vnop_inactive_desc, (vnop_t *)nfs_inactive }, /* inactive */
293 { &vnop_reclaim_desc, (vnop_t *)nfs_reclaim }, /* reclaim */
294 { &vnop_strategy_desc, (vnop_t *)fifo_strategy }, /* strategy */
295 { &vnop_pathconf_desc, (vnop_t *)fifo_pathconf }, /* pathconf */
296 { &vnop_advlock_desc, (vnop_t *)fifo_advlock }, /* advlock */
297 { &vnop_bwrite_desc, (vnop_t *)vn_bwrite }, /* bwrite */
298 { &vnop_pagein_desc, (vnop_t *)nfs_pagein }, /* Pagein */
299 { &vnop_pageout_desc, (vnop_t *)nfs_pageout }, /* Pageout */
300 { &vnop_blktooff_desc, (vnop_t *)nfs_blktooff }, /* blktooff */
301 { &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk }, /* offtoblk */
302 { &vnop_blockmap_desc, (vnop_t *)nfs_blockmap }, /* blockmap */
303 { NULL, NULL }
304 };
305 struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
306 { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
307 #ifdef __FreeBSD__
308 VNODEOP_SET(fifo_nfsv2nodeop_opv_desc);
309 #endif
310
311 static int nfs_mknodrpc(vnode_t dvp, vnode_t *vpp,
312 struct componentname *cnp,
313 struct vnode_attr *vap,
314 kauth_cred_t cred, proc_t p);
315 static int nfs_removerpc(vnode_t dvp, char *name, int namelen,
316 kauth_cred_t cred, proc_t proc);
317 static int nfs_renamerpc(vnode_t fdvp, char *fnameptr,
318 int fnamelen, vnode_t tdvp,
319 char *tnameptr, int tnamelen,
320 kauth_cred_t cred, proc_t proc);
321
322 /*
323 * Global variables
324 */
325 extern u_long nfs_xdrneg1;
326 extern u_long nfs_true, nfs_false;
327 extern struct nfsstats nfsstats;
328 extern nfstype nfsv3_type[9];
329 proc_t nfs_iodwant[NFS_MAXASYNCDAEMON];
330 struct nfsmount *nfs_iodmount[NFS_MAXASYNCDAEMON];
331
332 lck_grp_t *nfs_iod_lck_grp;
333 lck_grp_attr_t *nfs_iod_lck_grp_attr;
334 lck_attr_t *nfs_iod_lck_attr;
335 lck_mtx_t *nfs_iod_mutex;
336
337 int nfs_numasync = 0;
338 int nfs_ioddelwri = 0;
339
340 #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
341
342 static int nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
343 /* SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW,
344 &nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout");
345 */
346 #define NFSV3ACCESS_ALL (NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY \
347 | NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE \
348 | NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP)
349
350
351 /*
352 * the following are needed only by nfs_pageout to know how to handle errors
353 * see nfs_pageout comments on explanation of actions.
354 * the errors here are copied from errno.h and errors returned by servers
355 * are expected to match the same numbers here. If not, our actions maybe
356 * erroneous.
357 */
358 enum actiontype {NOACTION, DUMP, DUMPANDLOG, RETRY, RETRYWITHSLEEP, SEVER};
359
360 static int errorcount[ELAST+1]; /* better be zeros when initialized */
361
362 static const short errortooutcome[ELAST+1] = {
363 NOACTION,
364 DUMP, /* EPERM 1 Operation not permitted */
365 DUMP, /* ENOENT 2 No such file or directory */
366 DUMPANDLOG, /* ESRCH 3 No such process */
367 RETRY, /* EINTR 4 Interrupted system call */
368 DUMP, /* EIO 5 Input/output error */
369 DUMP, /* ENXIO 6 Device not configured */
370 DUMPANDLOG, /* E2BIG 7 Argument list too long */
371 DUMPANDLOG, /* ENOEXEC 8 Exec format error */
372 DUMPANDLOG, /* EBADF 9 Bad file descriptor */
373 DUMPANDLOG, /* ECHILD 10 No child processes */
374 DUMPANDLOG, /* EDEADLK 11 Resource deadlock avoided - was EAGAIN */
375 RETRY, /* ENOMEM 12 Cannot allocate memory */
376 DUMP, /* EACCES 13 Permission denied */
377 DUMPANDLOG, /* EFAULT 14 Bad address */
378 DUMPANDLOG, /* ENOTBLK 15 POSIX - Block device required */
379 RETRY, /* EBUSY 16 Device busy */
380 DUMP, /* EEXIST 17 File exists */
381 DUMP, /* EXDEV 18 Cross-device link */
382 DUMP, /* ENODEV 19 Operation not supported by device */
383 DUMP, /* ENOTDIR 20 Not a directory */
384 DUMP, /* EISDIR 21 Is a directory */
385 DUMP, /* EINVAL 22 Invalid argument */
386 DUMPANDLOG, /* ENFILE 23 Too many open files in system */
387 DUMPANDLOG, /* EMFILE 24 Too many open files */
388 DUMPANDLOG, /* ENOTTY 25 Inappropriate ioctl for device */
389 DUMPANDLOG, /* ETXTBSY 26 Text file busy - POSIX */
390 DUMP, /* EFBIG 27 File too large */
391 DUMP, /* ENOSPC 28 No space left on device */
392 DUMPANDLOG, /* ESPIPE 29 Illegal seek */
393 DUMP, /* EROFS 30 Read-only file system */
394 DUMP, /* EMLINK 31 Too many links */
395 RETRY, /* EPIPE 32 Broken pipe */
396 /* math software */
397 DUMPANDLOG, /* EDOM 33 Numerical argument out of domain */
398 DUMPANDLOG, /* ERANGE 34 Result too large */
399 RETRY, /* EAGAIN/EWOULDBLOCK 35 Resource temporarily unavailable */
400 DUMPANDLOG, /* EINPROGRESS 36 Operation now in progress */
401 DUMPANDLOG, /* EALREADY 37 Operation already in progress */
402 /* ipc/network software -- argument errors */
403 DUMPANDLOG, /* ENOTSOC 38 Socket operation on non-socket */
404 DUMPANDLOG, /* EDESTADDRREQ 39 Destination address required */
405 DUMPANDLOG, /* EMSGSIZE 40 Message too long */
406 DUMPANDLOG, /* EPROTOTYPE 41 Protocol wrong type for socket */
407 DUMPANDLOG, /* ENOPROTOOPT 42 Protocol not available */
408 DUMPANDLOG, /* EPROTONOSUPPORT 43 Protocol not supported */
409 DUMPANDLOG, /* ESOCKTNOSUPPORT 44 Socket type not supported */
410 DUMPANDLOG, /* ENOTSUP 45 Operation not supported */
411 DUMPANDLOG, /* EPFNOSUPPORT 46 Protocol family not supported */
412 DUMPANDLOG, /* EAFNOSUPPORT 47 Address family not supported by protocol family */
413 DUMPANDLOG, /* EADDRINUSE 48 Address already in use */
414 DUMPANDLOG, /* EADDRNOTAVAIL 49 Can't assign requested address */
415 /* ipc/network software -- operational errors */
416 RETRY, /* ENETDOWN 50 Network is down */
417 RETRY, /* ENETUNREACH 51 Network is unreachable */
418 RETRY, /* ENETRESET 52 Network dropped connection on reset */
419 RETRY, /* ECONNABORTED 53 Software caused connection abort */
420 RETRY, /* ECONNRESET 54 Connection reset by peer */
421 RETRY, /* ENOBUFS 55 No buffer space available */
422 RETRY, /* EISCONN 56 Socket is already connected */
423 RETRY, /* ENOTCONN 57 Socket is not connected */
424 RETRY, /* ESHUTDOWN 58 Can't send after socket shutdown */
425 RETRY, /* ETOOMANYREFS 59 Too many references: can't splice */
426 RETRY, /* ETIMEDOUT 60 Operation timed out */
427 RETRY, /* ECONNREFUSED 61 Connection refused */
428
429 DUMPANDLOG, /* ELOOP 62 Too many levels of symbolic links */
430 DUMP, /* ENAMETOOLONG 63 File name too long */
431 RETRY, /* EHOSTDOWN 64 Host is down */
432 RETRY, /* EHOSTUNREACH 65 No route to host */
433 DUMP, /* ENOTEMPTY 66 Directory not empty */
434 /* quotas & mush */
435 DUMPANDLOG, /* PROCLIM 67 Too many processes */
436 DUMPANDLOG, /* EUSERS 68 Too many users */
437 DUMPANDLOG, /* EDQUOT 69 Disc quota exceeded */
438 /* Network File System */
439 DUMP, /* ESTALE 70 Stale NFS file handle */
440 DUMP, /* EREMOTE 71 Too many levels of remote in path */
441 DUMPANDLOG, /* EBADRPC 72 RPC struct is bad */
442 DUMPANDLOG, /* ERPCMISMATCH 73 RPC version wrong */
443 DUMPANDLOG, /* EPROGUNAVAIL 74 RPC prog. not avail */
444 DUMPANDLOG, /* EPROGMISMATCH 75 Program version wrong */
445 DUMPANDLOG, /* EPROCUNAVAIL 76 Bad procedure for program */
446
447 DUMPANDLOG, /* ENOLCK 77 No locks available */
448 DUMPANDLOG, /* ENOSYS 78 Function not implemented */
449 DUMPANDLOG, /* EFTYPE 79 Inappropriate file type or format */
450 DUMPANDLOG, /* EAUTH 80 Authentication error */
451 DUMPANDLOG, /* ENEEDAUTH 81 Need authenticator */
452 /* Intelligent device errors */
453 DUMPANDLOG, /* EPWROFF 82 Device power is off */
454 DUMPANDLOG, /* EDEVERR 83 Device error, e.g. paper out */
455 DUMPANDLOG, /* EOVERFLOW 84 Value too large to be stored in data type */
456 /* Program loading errors */
457 DUMPANDLOG, /* EBADEXEC 85 Bad executable */
458 DUMPANDLOG, /* EBADARCH 86 Bad CPU type in executable */
459 DUMPANDLOG, /* ESHLIBVERS 87 Shared library version mismatch */
460 DUMPANDLOG, /* EBADMACHO 88 Malformed Macho file */
461 };
462
463
464 static short
465 nfs_pageouterrorhandler(int error)
466 {
467 if (error > ELAST)
468 return(DUMP);
469 else
470 return(errortooutcome[error]);
471 }
472
473 static int
474 nfs3_access_otw(vnode_t vp,
475 int wmode,
476 proc_t p,
477 kauth_cred_t cred)
478 {
479 const int v3 = 1;
480 u_long *tl;
481 int error = 0, attrflag;
482
483 mbuf_t mreq, mrep, md, mb, mb2;
484 caddr_t bpos, dpos, cp2;
485 register long t1, t2;
486 register caddr_t cp;
487 u_int32_t rmode;
488 struct nfsnode *np = VTONFS(vp);
489 u_int64_t xid;
490 struct timeval now;
491
492 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED);
493 if (error)
494 return (error);
495 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_ACCESS]);
496 nfsm_fhtom(vp, v3);
497 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
498 *tl = txdr_unsigned(wmode);
499 nfsm_request(vp, NFSPROC_ACCESS, p, cred, &xid);
500 if (mrep) {
501 nfsm_postop_attr_update(vp, 1, attrflag, &xid);
502 }
503 if (!error) {
504 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
505 rmode = fxdr_unsigned(u_int32_t, *tl);
506 np->n_mode = rmode;
507 np->n_modeuid = kauth_cred_getuid(cred);
508 microuptime(&now);
509 np->n_modestamp = now.tv_sec;
510 }
511 nfsm_reqdone;
512 return error;
513 }
514
515 /*
516 * nfs access vnode op.
517 * For nfs version 2, just return ok. File accesses may fail later.
518 * For nfs version 3, use the access rpc to check accessibility. If file modes
519 * are changed on the server, accesses might still fail later.
520 */
521 static int
522 nfs_access(ap)
523 struct vnop_access_args /* {
524 struct vnodeop_desc *a_desc;
525 vnode_t a_vp;
526 int a_mode;
527 vfs_context_t a_context;
528 } */ *ap;
529 {
530 vnode_t vp = ap->a_vp;
531 int error = 0, dorpc;
532 u_long mode, wmode;
533 int v3 = NFS_ISV3(vp);
534 struct nfsnode *np = VTONFS(vp);
535 struct timeval now;
536 kauth_cred_t cred;
537
538 /*
539 * For nfs v3, do an access rpc, otherwise you are stuck emulating
540 * ufs_access() locally using the vattr. This may not be correct,
541 * since the server may apply other access criteria such as
542 * client uid-->server uid mapping that we do not know about, but
543 * this is better than just returning anything that is lying about
544 * in the cache.
545 */
546 if (v3) {
547 /*
548 * Convert KAUTH primitives to NFS access rights.
549 */
550 mode = 0;
551 if (vnode_isdir(vp)) {
552 /* directory */
553 if (ap->a_action &
554 (KAUTH_VNODE_LIST_DIRECTORY |
555 KAUTH_VNODE_READ_EXTATTRIBUTES))
556 mode |= NFSV3ACCESS_READ;
557 if (ap->a_action & KAUTH_VNODE_SEARCH)
558 mode |= NFSV3ACCESS_LOOKUP;
559 if (ap->a_action &
560 (KAUTH_VNODE_ADD_FILE |
561 KAUTH_VNODE_ADD_SUBDIRECTORY))
562 mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
563 if (ap->a_action & KAUTH_VNODE_DELETE_CHILD)
564 mode |= NFSV3ACCESS_MODIFY;
565 } else {
566 /* file */
567 if (ap->a_action &
568 (KAUTH_VNODE_READ_DATA |
569 KAUTH_VNODE_READ_EXTATTRIBUTES))
570 mode |= NFSV3ACCESS_READ;
571 if (ap->a_action & KAUTH_VNODE_WRITE_DATA)
572 mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
573 if (ap->a_action & KAUTH_VNODE_APPEND_DATA)
574 mode |= NFSV3ACCESS_EXTEND;
575 if (ap->a_action & KAUTH_VNODE_EXECUTE)
576 mode |= NFSV3ACCESS_EXECUTE;
577 }
578 /* common */
579 if (ap->a_action & KAUTH_VNODE_DELETE)
580 mode |= NFSV3ACCESS_DELETE;
581 if (ap->a_action &
582 (KAUTH_VNODE_WRITE_ATTRIBUTES |
583 KAUTH_VNODE_WRITE_EXTATTRIBUTES |
584 KAUTH_VNODE_WRITE_SECURITY))
585 mode |= NFSV3ACCESS_MODIFY;
586 /* XXX this is pretty dubious */
587 if (ap->a_action & KAUTH_VNODE_CHANGE_OWNER)
588 mode |= NFSV3ACCESS_MODIFY;
589
590 /* if caching, always ask for every right */
591 if (nfsaccess_cache_timeout > 0) {
592 wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY |
593 NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE |
594 NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
595 } else
596 wmode = mode;
597
598 cred = vfs_context_ucred(ap->a_context);
599
600 /*
601 * Does our cached result allow us to give a definite yes to
602 * this request?
603 */
604 dorpc = 1;
605 if (NMODEVALID(np)) {
606 microuptime(&now);
607 if ((now.tv_sec < (np->n_modestamp + nfsaccess_cache_timeout)) &&
608 (kauth_cred_getuid(cred) == np->n_modeuid) &&
609 ((np->n_mode & mode) == mode)) {
610 /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_hits); */
611 dorpc = 0;
612 }
613 }
614 if (dorpc) {
615 /* Either a no, or a don't know. Go to the wire. */
616 /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_misses); */
617 error = nfs3_access_otw(vp, wmode, vfs_context_proc(ap->a_context), cred);
618 }
619 if (!error) {
620 /*
621 * If we asked for DELETE but didn't get it, the server
622 * may simply not support returning that bit (possible
623 * on UNIX systems). So, we'll assume that it is OK,
624 * and just let any subsequent delete action fail if it
625 * really isn't deletable.
626 */
627 if ((mode & NFSV3ACCESS_DELETE) &&
628 !(np->n_mode & NFSV3ACCESS_DELETE))
629 np->n_mode |= NFSV3ACCESS_DELETE;
630 if ((np->n_mode & mode) != mode)
631 error = EACCES;
632 }
633 } else {
634 /* v2 */
635 if ((ap->a_action & KAUTH_VNODE_WRITE_RIGHTS) && vfs_isrdonly(vnode_mount(vp))) {
636 error = EROFS;
637 } else {
638 error = 0;
639 }
640 }
641
642 return (error);
643 }
644
645 /*
646 * nfs open vnode op
647 * Check to see if the type is ok
648 * and that deletion is not in progress.
649 * For paged in text files, you will need to flush the page cache
650 * if consistency is lost.
651 */
652 /* ARGSUSED */
653
654 static int
655 nfs_open(ap)
656 struct vnop_open_args /* {
657 struct vnodeop_desc *a_desc;
658 vnode_t a_vp;
659 int a_mode;
660 vfs_context_t a_context;
661 } */ *ap;
662 {
663 vnode_t vp = ap->a_vp;
664 struct nfsnode *np = VTONFS(vp);
665 struct nfs_vattr nvattr;
666 kauth_cred_t cred;
667 proc_t p;
668 enum vtype vtype;
669 int error;
670
671 vtype = vnode_vtype(vp);
672 if (vtype != VREG && vtype != VDIR && vtype != VLNK) {
673 return (EACCES);
674 }
675
676 cred = vfs_context_ucred(ap->a_context);
677 p = vfs_context_proc(ap->a_context);
678
679 if (np->n_flag & NNEEDINVALIDATE) {
680 np->n_flag &= ~NNEEDINVALIDATE;
681 nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
682 }
683 if (np->n_flag & NMODIFIED) {
684 if ((error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1)) == EINTR)
685 return (error);
686 NATTRINVALIDATE(np);
687 if (vtype == VDIR)
688 np->n_direofoffset = 0;
689 error = nfs_getattr(vp, &nvattr, cred, p);
690 if (error)
691 return (error);
692 if (vtype == VDIR) {
693 /* if directory changed, purge any name cache entries */
694 if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=))
695 cache_purge(vp);
696 np->n_ncmtime = nvattr.nva_mtime;
697 }
698 np->n_mtime = nvattr.nva_mtime;
699 } else {
700 error = nfs_getattr(vp, &nvattr, cred, p);
701 if (error)
702 return (error);
703 if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, !=)) {
704 if (vtype == VDIR) {
705 np->n_direofoffset = 0;
706 nfs_invaldir(vp);
707 /* purge name cache entries */
708 if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=))
709 cache_purge(vp);
710 }
711 if ((error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1)) == EINTR)
712 return (error);
713 if (vtype == VDIR)
714 np->n_ncmtime = nvattr.nva_mtime;
715 np->n_mtime = nvattr.nva_mtime;
716 }
717 }
718 NATTRINVALIDATE(np); /* For Open/Close consistency */
719 return (0);
720 }
721
722 /*
723 * nfs close vnode op
724 * What an NFS client should do upon close after writing is a debatable issue.
725 * Most NFS clients push delayed writes to the server upon close, basically for
726 * two reasons:
727 * 1 - So that any write errors may be reported back to the client process
728 * doing the close system call. By far the two most likely errors are
729 * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
730 * 2 - To put a worst case upper bound on cache inconsistency between
731 * multiple clients for the file.
732 * There is also a consistency problem for Version 2 of the protocol w.r.t.
733 * not being able to tell if other clients are writing a file concurrently,
734 * since there is no way of knowing if the changed modify time in the reply
735 * is only due to the write for this client.
736 * (NFS Version 3 provides weak cache consistency data in the reply that
737 * should be sufficient to detect and handle this case.)
738 *
739 * The current code does the following:
740 * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
741 * for NFS Version 3 - flush dirty buffers to the server but don't invalidate
742 * them.
743 */
744 /* ARGSUSED */
745 static int
746 nfs_close(ap)
747 struct vnop_close_args /* {
748 struct vnodeop_desc *a_desc;
749 vnode_t a_vp;
750 int a_fflag;
751 vfs_context_t a_context;
752 } */ *ap;
753 {
754 vnode_t vp = ap->a_vp;
755 struct nfsnode *np = VTONFS(vp);
756 struct nfsmount *nmp;
757 kauth_cred_t cred;
758 proc_t p;
759 int error = 0;
760
761 cred = vfs_context_ucred(ap->a_context);
762 p = vfs_context_proc(ap->a_context);
763
764 if (vnode_vtype(vp) == VREG) {
765 #if DIAGNOSTIC
766 register struct sillyrename *sp = np->n_sillyrename;
767 if (sp)
768 kprintf("nfs_close: %s, dvp=%x, vp=%x, ap=%x, np=%x, sp=%x\n",
769 &sp->s_name[0], (unsigned)(sp->s_dvp), (unsigned)vp,
770 (unsigned)ap, (unsigned)np, (unsigned)sp);
771 #endif
772 nmp = VFSTONFS(vnode_mount(vp));
773 if (!nmp)
774 return (ENXIO);
775 if (np->n_flag & NNEEDINVALIDATE) {
776 np->n_flag &= ~NNEEDINVALIDATE;
777 nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
778 }
779 if (np->n_flag & NMODIFIED) {
780 if (NFS_ISV3(vp)) {
781 error = nfs_flush(vp, MNT_WAIT, cred, p, 0);
782 /*
783 * We cannot clear the NMODIFIED bit in np->n_flag due to
784 * potential races with other processes
785 * NMODIFIED is a hint
786 */
787 /* np->n_flag &= ~NMODIFIED; */
788 } else {
789 error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
790 }
791 NATTRINVALIDATE(np);
792 }
793 if (np->n_flag & NWRITEERR) {
794 np->n_flag &= ~NWRITEERR;
795 error = np->n_error;
796 }
797 }
798 return (error);
799 }
800
801
802 int
803 nfs_getattr_no_vnode(
804 mount_t mp,
805 u_char *fhp,
806 int fhsize,
807 kauth_cred_t cred,
808 proc_t p,
809 struct nfs_vattr *nvap,
810 u_int64_t *xidp)
811 {
812 mbuf_t mreq, mrep, md, mb, mb2;
813 caddr_t bpos, dpos;
814 int t2;
815 u_long *tl;
816 caddr_t cp;
817 struct nfsmount *nmp = VFSTONFS(mp);
818 int v3 = (nmp->nm_flag & NFSMNT_NFSV3);
819 int hsiz;
820 int error = 0;
821
822 // XXX fix this to use macros once the macros get cleaned up
823 //nfsm_reqhead(NFSX_FH(v3));
824 hsiz = NFSX_FH(v3);
825 mb = NULL;
826 if (hsiz >= nfs_mbuf_minclsize)
827 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
828 else
829 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
830 if (error)
831 return (error);
832 bpos = mbuf_data(mb);
833 mreq = mb;
834 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_GETATTR]);
835 //nfsm_fhtom(vp, v3);
836 if (v3) {
837 t2 = nfsm_rndup(fhsize) + NFSX_UNSIGNED;
838 if (t2 <= mbuf_trailingspace(mb)) {
839 nfsm_build(tl, u_long *, t2);
840 *tl++ = txdr_unsigned(fhsize);
841 *(tl + ((t2>>2) - 2)) = 0;
842 bcopy((caddr_t)fhp,(caddr_t)tl, fhsize);
843 } else if ((t2 = nfsm_strtmbuf(&mb, &bpos, (caddr_t)fhp, fhsize))) {
844 error = t2;
845 mbuf_freem(mreq);
846 goto nfsmout;
847 }
848 } else {
849 nfsm_build(cp, caddr_t, NFSX_V2FH);
850 bcopy((caddr_t)fhp, cp, NFSX_V2FH);
851 }
852 //nfsm_request(vp, NFSPROC_GETATTR, p, cred, xidp);
853 if ((error = nfs_request(NULL, mp, mreq, NFSPROC_GETATTR, p, cred, &mrep, &md, &dpos, xidp))) {
854 if (error & NFSERR_RETERR)
855 error &= ~NFSERR_RETERR;
856 else
857 goto nfsmout;
858 }
859 if (!error) {
860 //nfsm_loadattr(vp, nvap, xidp);
861 error = nfs_parsefattr(&md, &dpos, v3, nvap);
862 if (error) {
863 mbuf_freem(mrep);
864 goto nfsmout;
865 }
866 }
867 nfsm_reqdone;
868 return (error);
869 }
870
871 /*
872 * nfs getattr call from vfs.
873 */
874 int
875 nfs_getattr(
876 vnode_t vp,
877 struct nfs_vattr *nvap,
878 kauth_cred_t cred,
879 proc_t p)
880 {
881 struct nfsnode *np = VTONFS(vp);
882 caddr_t cp;
883 u_long *tl;
884 int t1, t2;
885 caddr_t bpos, dpos;
886 int error = 0;
887 mbuf_t mreq, mrep, md, mb, mb2;
888 int v3;
889 u_int64_t xid;
890 int avoidfloods;
891
892 FSDBG_TOP(513, np->n_size, np, np->n_vattr.nva_size, np->n_flag);
893
894 /*
895 * Update local times for special files.
896 */
897 if (np->n_flag & (NACC | NUPD))
898 np->n_flag |= NCHG;
899 /*
900 * First look in the cache.
901 */
902 if ((error = nfs_getattrcache(vp, nvap)) == 0) {
903 FSDBG_BOT(513, np->n_size, 0, np->n_vattr.nva_size, np->n_flag);
904 return (0);
905 }
906 if (error != ENOENT) {
907 FSDBG_BOT(513, np->n_size, error, np->n_vattr.nva_size,
908 np->n_flag);
909 return (error);
910 }
911
912 if (!VFSTONFS(vnode_mount(vp))) {
913 FSDBG_BOT(513, np->n_size, ENXIO, np->n_vattr.nva_size, np->n_flag);
914 return (ENXIO);
915 }
916 v3 = NFS_ISV3(vp);
917 error = 0;
918
919 /*
920 * Try to get both the attributes and access info by making an
921 * ACCESS call and seeing if it returns updated attributes.
922 * But don't bother if we aren't caching access info or if the
923 * attributes returned wouldn't be cached.
924 */
925 if (v3 && (nfsaccess_cache_timeout > 0) &&
926 (nfs_attrcachetimeout(vp) > 0)) {
927 /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_misses); */
928 if ((error = nfs3_access_otw(vp, NFSV3ACCESS_ALL, p, cred)))
929 return (error);
930 if ((error = nfs_getattrcache(vp, nvap)) == 0)
931 return (0);
932 if (error != ENOENT)
933 return (error);
934 error = 0;
935 }
936 avoidfloods = 0;
937 tryagain:
938 nfsm_reqhead(NFSX_FH(v3));
939 if (error) {
940 FSDBG_BOT(513, np->n_size, error, np->n_vattr.nva_size, np->n_flag);
941 return (error);
942 }
943 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_GETATTR]);
944 nfsm_fhtom(vp, v3);
945 nfsm_request(vp, NFSPROC_GETATTR, p, cred, &xid);
946 if (!error) {
947 nfsm_loadattr(vp, v3, nvap, &xid);
948 if (!xid) { /* out-of-order rpc - attributes were dropped */
949 mbuf_freem(mrep);
950 mrep = NULL;
951 FSDBG(513, -1, np, np->n_xid << 32, np->n_xid);
952 if (avoidfloods++ < 100)
953 goto tryagain;
954 /*
955 * avoidfloods>1 is bizarre. at 100 pull the plug
956 */
957 panic("nfs_getattr: getattr flood\n");
958 }
959 if (nfstimespeccmp(&np->n_mtime, &nvap->nva_mtime, !=)) {
960 enum vtype vtype = vnode_vtype(vp);
961 FSDBG(513, -1, np, -1, vp);
962 if (vtype == VDIR) {
963 nfs_invaldir(vp);
964 /* purge name cache entries */
965 if (nfstimespeccmp(&np->n_ncmtime, &nvap->nva_mtime, !=))
966 cache_purge(vp);
967 }
968 error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
969 FSDBG(513, -1, np, -2, error);
970 if (!error) {
971 if (vtype == VDIR)
972 np->n_ncmtime = nvap->nva_mtime;
973 np->n_mtime = nvap->nva_mtime;
974 }
975 }
976 }
977 nfsm_reqdone;
978
979 FSDBG_BOT(513, np->n_size, -1, np->n_vattr.nva_size, error);
980 return (error);
981 }
982
983
984 static int
985 nfs_vnop_getattr(
986 struct vnop_getattr_args /* {
987 struct vnodeop_desc *a_desc;
988 vnode_t a_vp;
989 struct vnode_attr *a_vap;
990 vfs_context_t a_context;
991 } */ *ap)
992 {
993 int error;
994 struct nfs_vattr nva;
995 struct vnode_attr *vap = ap->a_vap;
996
997 error = nfs_getattr(ap->a_vp, &nva,
998 vfs_context_ucred(ap->a_context),
999 vfs_context_proc(ap->a_context));
1000 if (error)
1001 return (error);
1002
1003 /* copy nva to *a_vap */
1004 VATTR_RETURN(vap, va_type, nva.nva_type);
1005 VATTR_RETURN(vap, va_mode, nva.nva_mode);
1006 VATTR_RETURN(vap, va_rdev, nva.nva_rdev);
1007 VATTR_RETURN(vap, va_uid, nva.nva_uid);
1008 VATTR_RETURN(vap, va_gid, nva.nva_gid);
1009 VATTR_RETURN(vap, va_nlink, nva.nva_nlink);
1010 VATTR_RETURN(vap, va_fileid, nva.nva_fileid);
1011 VATTR_RETURN(vap, va_data_size, nva.nva_size);
1012 VATTR_RETURN(vap, va_data_alloc, nva.nva_bytes);
1013 VATTR_RETURN(vap, va_iosize, nva.nva_blocksize); /* should this just be f_iosize? */
1014 VATTR_RETURN(vap, va_fsid, nva.nva_fsid);
1015 vap->va_access_time.tv_sec = nva.nva_atime.tv_sec;
1016 vap->va_access_time.tv_nsec = nva.nva_atime.tv_nsec;
1017 VATTR_SET_SUPPORTED(vap, va_access_time);
1018 vap->va_modify_time.tv_sec = nva.nva_mtime.tv_sec;
1019 vap->va_modify_time.tv_nsec = nva.nva_mtime.tv_nsec;
1020 VATTR_SET_SUPPORTED(vap, va_modify_time);
1021 vap->va_change_time.tv_sec = nva.nva_ctime.tv_sec;
1022 vap->va_change_time.tv_nsec = nva.nva_ctime.tv_nsec;
1023 VATTR_SET_SUPPORTED(vap, va_change_time);
1024
1025 return (error);
1026 }
1027
1028 /*
1029 * nfs setattr call.
1030 */
1031 static int
1032 nfs_setattr(ap)
1033 struct vnop_setattr_args /* {
1034 struct vnodeop_desc *a_desc;
1035 vnode_t a_vp;
1036 struct vnode_attr *a_vap;
1037 vfs_context_t a_context;
1038 } */ *ap;
1039 {
1040 vnode_t vp = ap->a_vp;
1041 struct nfsnode *np = VTONFS(vp);
1042 struct nfsmount *nmp;
1043 struct vnode_attr *vap = ap->a_vap;
1044 int error = 0;
1045 int biosize;
1046 u_quad_t tsize;
1047 kauth_cred_t cred;
1048 proc_t p;
1049
1050 #ifndef nolint
1051 tsize = (u_quad_t)0;
1052 #endif
1053 nmp = VFSTONFS(vnode_mount(vp));
1054 if (!nmp)
1055 return (ENXIO);
1056 biosize = nmp->nm_biosize;
1057
1058 /* Setting of flags is not supported. */
1059 if (VATTR_IS_ACTIVE(vap, va_flags))
1060 return (ENOTSUP);
1061
1062 cred = vfs_context_ucred(ap->a_context);
1063 p = vfs_context_proc(ap->a_context);
1064
1065 VATTR_SET_SUPPORTED(vap, va_mode);
1066 VATTR_SET_SUPPORTED(vap, va_uid);
1067 VATTR_SET_SUPPORTED(vap, va_gid);
1068 VATTR_SET_SUPPORTED(vap, va_data_size);
1069 VATTR_SET_SUPPORTED(vap, va_access_time);
1070 VATTR_SET_SUPPORTED(vap, va_modify_time);
1071
1072 /* Disallow write attempts if the filesystem is mounted read-only. */
1073 if ((VATTR_IS_ACTIVE(vap, va_flags) || VATTR_IS_ACTIVE(vap, va_mode) ||
1074 VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid) ||
1075 VATTR_IS_ACTIVE(vap, va_access_time) ||
1076 VATTR_IS_ACTIVE(vap, va_modify_time)) &&
1077 vnode_vfsisrdonly(vp))
1078 return (EROFS);
1079
1080 if (VATTR_IS_ACTIVE(vap, va_data_size)) {
1081 switch (vnode_vtype(vp)) {
1082 case VDIR:
1083 return (EISDIR);
1084 case VCHR:
1085 case VBLK:
1086 case VSOCK:
1087 case VFIFO:
1088 if (!VATTR_IS_ACTIVE(vap, va_modify_time) &&
1089 !VATTR_IS_ACTIVE(vap, va_access_time) &&
1090 !VATTR_IS_ACTIVE(vap, va_mode) &&
1091 !VATTR_IS_ACTIVE(vap, va_uid) &&
1092 !VATTR_IS_ACTIVE(vap, va_gid))
1093 return (0);
1094 VATTR_CLEAR_ACTIVE(vap, va_data_size);
1095 break;
1096 default:
1097 /*
1098 * Disallow write attempts if the filesystem is
1099 * mounted read-only.
1100 */
1101 if (vnode_vfsisrdonly(vp))
1102 return (EROFS);
1103 FSDBG_TOP(512, np->n_size, vap->va_data_size,
1104 np->n_vattr.nva_size, np->n_flag);
1105 if (np->n_flag & NMODIFIED) {
1106 if (vap->va_data_size == 0)
1107 error = nfs_vinvalbuf(vp, 0, cred, p, 1);
1108 else
1109 error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1110 if (error) {
1111 printf("nfs_setattr: nfs_vinvalbuf %d\n", error);
1112 FSDBG_BOT(512, np->n_size, vap->va_data_size,
1113 np->n_vattr.nva_size, -1);
1114 return (error);
1115 }
1116 } else if (np->n_size > vap->va_data_size) { /* shrinking? */
1117 daddr64_t obn, bn;
1118 int neweofoff, mustwrite;
1119 struct nfsbuf *bp;
1120
1121 obn = (np->n_size - 1) / biosize;
1122 bn = vap->va_data_size / biosize;
1123 for ( ; obn >= bn; obn--) {
1124 if (!nfs_buf_is_incore(vp, obn))
1125 continue;
1126 error = nfs_buf_get(vp, obn, biosize, 0, NBLK_READ, &bp);
1127 if (error)
1128 continue;
1129 if (obn != bn) {
1130 FSDBG(512, bp, bp->nb_flags, 0, obn);
1131 SET(bp->nb_flags, NB_INVAL);
1132 nfs_buf_release(bp, 1);
1133 continue;
1134 }
1135 mustwrite = 0;
1136 neweofoff = vap->va_data_size - NBOFF(bp);
1137 /* check for any dirty data before the new EOF */
1138 if (bp->nb_dirtyend && bp->nb_dirtyoff < neweofoff) {
1139 /* clip dirty range to EOF */
1140 if (bp->nb_dirtyend > neweofoff)
1141 bp->nb_dirtyend = neweofoff;
1142 mustwrite++;
1143 }
1144 bp->nb_dirty &= (1 << round_page_32(neweofoff)/PAGE_SIZE) - 1;
1145 if (bp->nb_dirty)
1146 mustwrite++;
1147 if (!mustwrite) {
1148 FSDBG(512, bp, bp->nb_flags, 0, obn);
1149 SET(bp->nb_flags, NB_INVAL);
1150 nfs_buf_release(bp, 1);
1151 continue;
1152 }
1153 /* gotta write out dirty data before invalidating */
1154 /* (NB_STABLE indicates that data writes should be FILESYNC) */
1155 /* (NB_NOCACHE indicates buffer should be discarded) */
1156 CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL | NB_ASYNC | NB_READ));
1157 SET(bp->nb_flags, NB_STABLE | NB_NOCACHE);
1158 if (bp->nb_wcred == NOCRED) {
1159 kauth_cred_ref(cred);
1160 bp->nb_wcred = cred;
1161 }
1162 error = nfs_buf_write(bp);
1163 // Note: bp has been released
1164 if (error) {
1165 FSDBG(512, bp, 0xd00dee, 0xbad, error);
1166 np->n_error = error;
1167 np->n_flag |= NWRITEERR;
1168 /*
1169 * There was a write error and we need to
1170 * invalidate attrs and flush buffers in
1171 * order to sync up with the server.
1172 * (if this write was extending the file,
1173 * we may no longer know the correct size)
1174 */
1175 NATTRINVALIDATE(np);
1176 nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
1177 error = 0;
1178 }
1179 }
1180 }
1181 tsize = np->n_size;
1182 np->n_size = np->n_vattr.nva_size = vap->va_data_size;
1183 ubc_setsize(vp, (off_t)vap->va_data_size); /* XXX error? */
1184 }
1185 } else if ((VATTR_IS_ACTIVE(vap, va_modify_time) ||
1186 VATTR_IS_ACTIVE(vap, va_access_time)) &&
1187 (np->n_flag & NMODIFIED) && (vnode_vtype(vp) == VREG)) {
1188 error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1189 if (error == EINTR)
1190 return (error);
1191 }
1192 if (VATTR_IS_ACTIVE(vap, va_mode)) {
1193 NMODEINVALIDATE(np);
1194 }
1195 error = nfs_setattrrpc(vp, vap, cred, p);
1196 FSDBG_BOT(512, np->n_size, vap->va_data_size, np->n_vattr.nva_size, error);
1197 if (error && VATTR_IS_ACTIVE(vap, va_data_size)) {
1198 /* make every effort to resync file size w/ server... */
1199 int err; /* preserve "error" for return */
1200
1201 np->n_size = np->n_vattr.nva_size = tsize;
1202 ubc_setsize(vp, (off_t)np->n_size); /* XXX check error */
1203 vap->va_data_size = tsize;
1204 err = nfs_setattrrpc(vp, vap, cred, p);
1205 printf("nfs_setattr: nfs_setattrrpc %d %d\n", error, err);
1206 }
1207 return (error);
1208 }
1209
1210 /*
1211 * Do an nfs setattr rpc.
1212 */
1213 static int
1214 nfs_setattrrpc(vp, vap, cred, procp)
1215 vnode_t vp;
1216 struct vnode_attr *vap;
1217 kauth_cred_t cred;
1218 proc_t procp;
1219 {
1220 register struct nfsv2_sattr *sp;
1221 register caddr_t cp;
1222 register long t1, t2;
1223 caddr_t bpos, dpos, cp2;
1224 u_long *tl;
1225 int error = 0, wccpostattr = 0;
1226 mbuf_t mreq, mrep, md, mb, mb2;
1227 int v3;
1228 u_int64_t xid;
1229 struct timeval now;
1230
1231 if (!VFSTONFS(vnode_mount(vp)))
1232 return (ENXIO);
1233 v3 = NFS_ISV3(vp);
1234
1235 nfsm_reqhead(NFSX_FH(v3) + NFSX_SATTR(v3));
1236 if (error)
1237 return (error);
1238 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SETATTR]);
1239 nfsm_fhtom(vp, v3);
1240 if (v3) {
1241 if (VATTR_IS_ACTIVE(vap, va_mode)) {
1242 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1243 *tl++ = nfs_true;
1244 *tl = txdr_unsigned(vap->va_mode);
1245 } else {
1246 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1247 *tl = nfs_false;
1248 }
1249 if (VATTR_IS_ACTIVE(vap, va_uid)) {
1250 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1251 *tl++ = nfs_true;
1252 *tl = txdr_unsigned(vap->va_uid);
1253 } else {
1254 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1255 *tl = nfs_false;
1256 }
1257 if (VATTR_IS_ACTIVE(vap, va_gid)) {
1258 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1259 *tl++ = nfs_true;
1260 *tl = txdr_unsigned(vap->va_gid);
1261 } else {
1262 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1263 *tl = nfs_false;
1264 }
1265 if (VATTR_IS_ACTIVE(vap, va_data_size)) {
1266 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
1267 *tl++ = nfs_true;
1268 txdr_hyper(&vap->va_data_size, tl);
1269 } else {
1270 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1271 *tl = nfs_false;
1272 }
1273 microtime(&now);
1274 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1275 if (vap->va_access_time.tv_sec != now.tv_sec) {
1276 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
1277 *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
1278 txdr_nfsv3time(&vap->va_access_time, tl);
1279 } else {
1280 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1281 *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
1282 }
1283 } else {
1284 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1285 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
1286 }
1287 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1288 if (vap->va_modify_time.tv_sec != now.tv_sec) {
1289 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
1290 *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
1291 txdr_nfsv3time(&vap->va_modify_time, tl);
1292 } else {
1293 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1294 *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
1295 }
1296 } else {
1297 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1298 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
1299 }
1300 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1301 *tl = nfs_false;
1302 } else {
1303 struct timespec neg1time = { -1, -1 };
1304 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
1305 if (VATTR_IS_ACTIVE(vap, va_mode))
1306 sp->sa_mode = vtonfsv2_mode(vnode_vtype(vp), vap->va_mode);
1307 else
1308 sp->sa_mode = nfs_xdrneg1;
1309 if (VATTR_IS_ACTIVE(vap, va_uid))
1310 sp->sa_uid = txdr_unsigned(vap->va_uid);
1311 else
1312 sp->sa_uid = nfs_xdrneg1;
1313 if (VATTR_IS_ACTIVE(vap, va_gid))
1314 sp->sa_gid = txdr_unsigned(vap->va_gid);
1315 else
1316 sp->sa_gid = nfs_xdrneg1;
1317 if (VATTR_IS_ACTIVE(vap, va_data_size))
1318 sp->sa_size = txdr_unsigned(vap->va_data_size);
1319 else
1320 sp->sa_size = nfs_xdrneg1;
1321 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1322 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
1323 } else {
1324 txdr_nfsv2time(&neg1time, &sp->sa_atime);
1325 }
1326 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1327 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
1328 } else {
1329 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
1330 }
1331 }
1332 nfsm_request(vp, NFSPROC_SETATTR, procp, cred, &xid);
1333 if (v3) {
1334 struct timespec premtime = { 0, 0 };
1335 if (mrep) {
1336 nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
1337 }
1338 /* if file hadn't changed, update cached mtime */
1339 if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==)) {
1340 VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
1341 }
1342 /* if directory hadn't changed, update namecache mtime */
1343 if ((vnode_vtype(vp) == VDIR) &&
1344 nfstimespeccmp(&VTONFS(vp)->n_ncmtime, &premtime, ==)) {
1345 VTONFS(vp)->n_ncmtime = VTONFS(vp)->n_vattr.nva_mtime;
1346 }
1347 if (!wccpostattr)
1348 NATTRINVALIDATE(VTONFS(vp));
1349 } else {
1350 if (mrep) {
1351 nfsm_loadattr(vp, v3, NULL, &xid);
1352 }
1353 }
1354 nfsm_reqdone;
1355 return (error);
1356 }
1357
1358 /*
1359 * nfs lookup call, one step at a time...
1360 * First look in cache
1361 * If not found, unlock the directory nfsnode and do the rpc
1362 */
1363 static int
1364 nfs_lookup(ap)
1365 struct vnop_lookup_args /* {
1366 struct vnodeop_desc *a_desc;
1367 vnode_t a_dvp;
1368 vnode_t *a_vpp;
1369 struct componentname *a_cnp;
1370 vfs_context_t a_context;
1371 } */ *ap;
1372 {
1373 struct componentname *cnp = ap->a_cnp;
1374 vnode_t dvp = ap->a_dvp;
1375 vnode_t *vpp = ap->a_vpp;
1376 int flags = cnp->cn_flags;
1377 vnode_t newvp;
1378 u_long *tl;
1379 caddr_t cp;
1380 long t1, t2;
1381 caddr_t bpos, dpos, cp2;
1382 mbuf_t mreq, mrep, md, mb, mb2;
1383 long len;
1384 u_char *fhp;
1385 struct nfsnode *dnp, *np;
1386 int wantparent, error, attrflag, dattrflag, fhsize, fhisdvp;
1387 int v3 = NFS_ISV3(dvp);
1388 u_int64_t xid, dxid;
1389 struct nfs_vattr nvattr;
1390 kauth_cred_t cred;
1391 proc_t p;
1392 int ngflags;
1393
1394 *vpp = NULLVP;
1395
1396 cred = vfs_context_ucred(ap->a_context);
1397 p = vfs_context_proc(ap->a_context);
1398
1399 wantparent = flags & (LOCKPARENT|WANTPARENT);
1400 dnp = VTONFS(dvp);
1401
1402 error = nfs_getattr(dvp, &nvattr, cred, p);
1403 if (error)
1404 goto error_return;
1405 if (nfstimespeccmp(&dnp->n_ncmtime, &nvattr.nva_mtime, !=)) {
1406 /*
1407 * This directory has changed on us.
1408 * Purge any name cache entries.
1409 */
1410 cache_purge(dvp);
1411 dnp->n_ncmtime = nvattr.nva_mtime;
1412 }
1413
1414 error = cache_lookup(dvp, vpp, cnp);
1415 switch (error) {
1416 case ENOENT:
1417 /* negative cache entry same as cache miss */
1418 error = 0;
1419 /* FALLTHROUGH */
1420 case 0:
1421 /* cache miss */
1422 break;
1423 case -1:
1424 /* cache hit, not really an error */
1425 {
1426 struct vnop_access_args naa;
1427
1428 OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_hits);
1429
1430 /* check for directory access */
1431 naa.a_vp = dvp;
1432 naa.a_action = KAUTH_VNODE_SEARCH;
1433 naa.a_context = ap->a_context;
1434
1435 /* compute actual success/failure based on accessibility */
1436 error = nfs_access(&naa);
1437 }
1438 /* FALLTHROUGH */
1439 default:
1440 /* unexpected error from cache_lookup */
1441 goto error_return;
1442 }
1443
1444 /* check for lookup of "." */
1445 if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
1446 /* skip lookup, we know who we are */
1447 fhisdvp = 1;
1448 fhp = NULL;
1449 fhsize = 0;
1450 mrep = NULL;
1451 goto found;
1452 }
1453
1454 /* do we know this name is too long? */
1455 if (v3) {
1456 /* For NFSv3: need uniform pathconf info to test pc_namemax */
1457 struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1458 if (!nmp) {
1459 error = ENXIO;
1460 goto error_return;
1461 }
1462 if (((nmp->nm_state & (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) ==
1463 (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) &&
1464 (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS) &&
1465 (cnp->cn_namelen > (long)nmp->nm_fsinfo.namemax)) {
1466 error = ENAMETOOLONG;
1467 goto error_return;
1468 }
1469 } else if (cnp->cn_namelen > NFS_MAXNAMLEN) {
1470 error = ENAMETOOLONG;
1471 goto error_return;
1472 }
1473
1474 error = 0;
1475 newvp = NULLVP;
1476
1477 OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_misses);
1478 len = cnp->cn_namelen;
1479 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
1480 if (error)
1481 goto error_return;
1482 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
1483 nfsm_fhtom(dvp, v3);
1484 nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
1485 /* nfsm_request for NFSv2 causes you to goto to nfsmout upon errors */
1486 nfsm_request(dvp, NFSPROC_LOOKUP, p, cred, &xid);
1487
1488 if (error) {
1489 if (mrep) {
1490 nfsm_postop_attr_update(dvp, v3, dattrflag, &xid);
1491 mbuf_freem(mrep);
1492 }
1493 goto nfsmout;
1494 }
1495
1496 /* get the filehandle */
1497 nfsm_getfh(fhp, fhsize, v3);
1498 /* is the file handle the same as this directory's file handle? */
1499 fhisdvp = NFS_CMPFH(dnp, fhp, fhsize);
1500
1501 /* get attributes */
1502 if (v3) {
1503 dxid = xid;
1504 nfsm_postop_attr_get(v3, attrflag, &nvattr);
1505 nfsm_postop_attr_update(dvp, v3, dattrflag, &dxid);
1506 if (!attrflag && (!fhisdvp || !dattrflag)) {
1507 /* We need valid attributes in order */
1508 /* to call nfs_nget/vnode_create(). */
1509 error = nfs_getattr_no_vnode(vnode_mount(dvp),
1510 fhp, fhsize, cred, p, &nvattr, &xid);
1511 if (error) {
1512 mbuf_freem(mrep);
1513 goto error_return;
1514 }
1515 }
1516 } else {
1517 nfsm_attr_get(v3, &nvattr);
1518 }
1519
1520 found:
1521
1522 /*
1523 * Handle RENAME case...
1524 */
1525 if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
1526 if (fhisdvp) {
1527 mbuf_freem(mrep);
1528 error = EISDIR;
1529 goto error_return;
1530 }
1531 error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
1532 &nvattr, &xid, 0, &np);
1533 if (error) {
1534 mbuf_freem(mrep);
1535 goto error_return;
1536 }
1537 *vpp = NFSTOV(np);
1538 mbuf_freem(mrep);
1539
1540 goto error_return;
1541 }
1542
1543 if ((cnp->cn_flags & MAKEENTRY) &&
1544 (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))
1545 ngflags = NG_MAKEENTRY;
1546 else
1547 ngflags = 0;
1548
1549 if (fhisdvp) {
1550 error = vnode_get(dvp);
1551 if (error) {
1552 mbuf_freem(mrep);
1553 goto error_return;
1554 }
1555 newvp = dvp;
1556 /* test fhp to see if we have valid attributes in nvattr */
1557 if (fhp && (dnp->n_xid <= xid)) {
1558 error = nfs_loadattrcache(dnp, &nvattr, &xid, 0);
1559 if (error) {
1560 vnode_put(dvp);
1561 mbuf_freem(mrep);
1562 goto error_return;
1563 }
1564 }
1565 } else {
1566 error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
1567 &nvattr, &xid, ngflags, &np);
1568 if (error) {
1569 mbuf_freem(mrep);
1570 goto error_return;
1571 }
1572 newvp = NFSTOV(np);
1573 }
1574 *vpp = newvp;
1575 // if (error == 0 && *vpp != NULL && *vpp != dvp)
1576 // nfs_unlock(VTONFS(*vpp));
1577
1578 nfsm_reqdone;
1579 if (error) {
1580 if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
1581 (flags & ISLASTCN) && error == ENOENT) {
1582 if (vnode_mount(dvp) && vnode_vfsisrdonly(dvp))
1583 error = EROFS;
1584 else
1585 error = EJUSTRETURN;
1586 }
1587 }
1588 error_return:
1589 if (error && *vpp) {
1590 vnode_put(*vpp);
1591 *vpp = NULLVP;
1592 }
1593 return (error);
1594 }
1595
1596 /*
1597 * nfs read call.
1598 * Just call nfs_bioread() to do the work.
1599 */
1600 static int
1601 nfs_read(ap)
1602 struct vnop_read_args /* {
1603 struct vnodeop_desc *a_desc;
1604 vnode_t a_vp;
1605 struct uio *a_uio;
1606 int a_ioflag;
1607 vfs_context_t a_context;
1608 } */ *ap;
1609 {
1610 if (vnode_vtype(ap->a_vp) != VREG)
1611 return (EPERM);
1612 return (nfs_bioread(ap->a_vp, ap->a_uio, ap->a_ioflag,
1613 vfs_context_ucred(ap->a_context),
1614 vfs_context_proc(ap->a_context)));
1615 }
1616
1617
1618 /*
1619 * nfs readlink call
1620 */
1621 static int
1622 nfs_readlink(ap)
1623 struct vnop_readlink_args /* {
1624 struct vnodeop_desc *a_desc;
1625 vnode_t a_vp;
1626 struct uio *a_uio;
1627 vfs_context_t a_context;
1628 } */ *ap;
1629 {
1630 if (vnode_vtype(ap->a_vp) != VLNK)
1631 return (EPERM);
1632 return (nfs_bioread(ap->a_vp, ap->a_uio, 0,
1633 vfs_context_ucred(ap->a_context),
1634 vfs_context_proc(ap->a_context)));
1635 }
1636
1637 /*
1638 * Do a readlink rpc.
1639 * Called by nfs_doio() from below the buffer cache.
1640 */
1641 int
1642 nfs_readlinkrpc(
1643 vnode_t vp,
1644 struct uio *uiop,
1645 kauth_cred_t cred,
1646 proc_t p)
1647 {
1648 register u_long *tl;
1649 register caddr_t cp;
1650 register long t1, t2;
1651 caddr_t bpos, dpos, cp2;
1652 int error = 0, len, attrflag;
1653 mbuf_t mreq, mrep, md, mb, mb2;
1654 int v3;
1655 u_int64_t xid;
1656
1657 if (!VFSTONFS(vnode_mount(vp)))
1658 return (ENXIO);
1659 v3 = NFS_ISV3(vp);
1660
1661 nfsm_reqhead(NFSX_FH(v3));
1662 if (error)
1663 return (error);
1664 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READLINK]);
1665 nfsm_fhtom(vp, v3);
1666 nfsm_request(vp, NFSPROC_READLINK, p, cred, &xid);
1667 if (v3 && mrep)
1668 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
1669 if (!error) {
1670 nfsm_strsiz(len, NFS_MAXPATHLEN, v3);
1671 if (len >= NFS_MAXPATHLEN) {
1672 struct nfsnode *np = VTONFS(vp);
1673 #if DIAGNOSTIC
1674 if (!np)
1675 panic("nfs_readlinkrpc: null np");
1676 #endif
1677 if (np->n_size && np->n_size < NFS_MAXPATHLEN)
1678 len = np->n_size;
1679 }
1680 nfsm_mtouio(uiop, len);
1681 }
1682 nfsm_reqdone;
1683 return (error);
1684 }
1685
1686 /*
1687 * nfs read rpc call
1688 * Ditto above
1689 */
1690 int
1691 nfs_readrpc(
1692 vnode_t vp,
1693 struct uio *uiop,
1694 kauth_cred_t cred,
1695 proc_t p)
1696 {
1697 register u_long *tl;
1698 register caddr_t cp;
1699 register long t1, t2;
1700 caddr_t bpos, dpos, cp2;
1701 mbuf_t mreq, mrep, md, mb, mb2;
1702 struct nfsmount *nmp;
1703 int error = 0, len, retlen, tsiz, eof = 0, attrflag;
1704 int v3, nmrsize;
1705 u_int64_t xid;
1706
1707 FSDBG_TOP(536, vp, uiop->uio_offset, uio_uio_resid(uiop), 0);
1708 nmp = VFSTONFS(vnode_mount(vp));
1709 if (!nmp)
1710 return (ENXIO);
1711 v3 = NFS_ISV3(vp);
1712 nmrsize = nmp->nm_rsize;
1713
1714 // LP64todo - fix this
1715 tsiz = uio_uio_resid(uiop);
1716 if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
1717 FSDBG_BOT(536, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
1718 return (EFBIG);
1719 }
1720 while (tsiz > 0) {
1721 len = (tsiz > nmrsize) ? nmrsize : tsiz;
1722 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED * 3);
1723 if (error)
1724 break;
1725 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READ]);
1726 nfsm_fhtom(vp, v3);
1727 nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
1728 if (v3) {
1729 txdr_hyper(&uiop->uio_offset, tl);
1730 *(tl + 2) = txdr_unsigned(len);
1731 } else {
1732 *tl++ = txdr_unsigned(uiop->uio_offset);
1733 *tl++ = txdr_unsigned(len);
1734 *tl = 0;
1735 }
1736 FSDBG(536, vp, uiop->uio_offset, len, 0);
1737 nfsm_request(vp, NFSPROC_READ, p, cred, &xid);
1738 if (v3) {
1739 if (mrep) {
1740 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
1741 }
1742 if (error) {
1743 mbuf_freem(mrep);
1744 goto nfsmout;
1745 }
1746 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
1747 eof = fxdr_unsigned(int, *(tl + 1));
1748 } else {
1749 if (mrep) {
1750 nfsm_loadattr(vp, v3, NULL, &xid);
1751 }
1752 }
1753 if (mrep) {
1754 nfsm_strsiz(retlen, nmrsize, 0);
1755 nfsm_mtouio(uiop, retlen);
1756 mbuf_freem(mrep);
1757 } else {
1758 retlen = 0;
1759 }
1760 tsiz -= retlen;
1761 if (v3) {
1762 if (eof || retlen == 0)
1763 tsiz = 0;
1764 } else if (retlen < len)
1765 tsiz = 0;
1766 }
1767 nfsmout:
1768 FSDBG_BOT(536, vp, eof, uio_uio_resid(uiop), error);
1769 return (error);
1770 }
1771
1772 /*
1773 * nfs write call
1774 */
1775 int
1776 nfs_writerpc(
1777 vnode_t vp,
1778 struct uio *uiop,
1779 kauth_cred_t cred,
1780 proc_t p,
1781 int *iomode,
1782 int *must_commit)
1783 {
1784 register u_long *tl;
1785 register caddr_t cp;
1786 register int t1, t2, backup;
1787 caddr_t bpos, dpos, cp2;
1788 mbuf_t mreq, mrep, md, mb, mb2;
1789 struct nfsmount *nmp;
1790 int error = 0, len, tsiz, updatemtime = 0, wccpostattr = 0, rlen, commit;
1791 int v3, committed = NFSV3WRITE_FILESYNC;
1792 u_int64_t xid;
1793 mount_t mp;
1794
1795 #if DIAGNOSTIC
1796 if (uiop->uio_iovcnt != 1)
1797 panic("nfs_writerpc: iovcnt > 1");
1798 #endif
1799 FSDBG_TOP(537, vp, uiop->uio_offset, uio_uio_resid(uiop), *iomode);
1800 nmp = VFSTONFS(vnode_mount(vp));
1801 if (!nmp)
1802 return (ENXIO);
1803 v3 = NFS_ISV3(vp);
1804 *must_commit = 0;
1805 // LP64todo - fix this
1806 tsiz = uio_uio_resid(uiop);
1807 if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
1808 FSDBG_BOT(537, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
1809 return (EFBIG);
1810 }
1811 while (tsiz > 0) {
1812 nmp = VFSTONFS(vnode_mount(vp));
1813 if (!nmp) {
1814 error = ENXIO;
1815 break;
1816 }
1817 len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
1818 nfsm_reqhead(NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
1819 if (error)
1820 break;
1821 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_WRITE]);
1822 nfsm_fhtom(vp, v3);
1823 if (v3) {
1824 nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
1825 txdr_hyper(&uiop->uio_offset, tl);
1826 tl += 2;
1827 *tl++ = txdr_unsigned(len);
1828 *tl++ = txdr_unsigned(*iomode);
1829 } else {
1830 nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
1831 *++tl = txdr_unsigned(uiop->uio_offset);
1832 tl += 2;
1833 }
1834 *tl = txdr_unsigned(len);
1835 FSDBG(537, vp, uiop->uio_offset, len, 0);
1836 nfsm_uiotom(uiop, len);
1837 nfsm_request(vp, NFSPROC_WRITE, p, cred, &xid);
1838 nmp = VFSTONFS(vnode_mount(vp));
1839 if (!nmp)
1840 error = ENXIO;
1841 if (v3) {
1842 if (mrep) {
1843 struct timespec premtime;
1844 nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
1845 if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==))
1846 updatemtime = 1;
1847 }
1848 if (!error) {
1849 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
1850 NFSX_V3WRITEVERF);
1851 rlen = fxdr_unsigned(int, *tl++);
1852 if (rlen <= 0) {
1853 error = NFSERR_IO;
1854 break;
1855 } else if (rlen < len) {
1856 backup = len - rlen;
1857 uio_iov_base_add(uiop, -backup);
1858 uio_iov_len_add(uiop, backup);
1859 uiop->uio_offset -= backup;
1860 uio_uio_resid_add(uiop, backup);
1861 len = rlen;
1862 }
1863 commit = fxdr_unsigned(int, *tl++);
1864
1865 /*
1866 * Return the lowest committment level
1867 * obtained by any of the RPCs.
1868 */
1869 if (committed == NFSV3WRITE_FILESYNC)
1870 committed = commit;
1871 else if (committed == NFSV3WRITE_DATASYNC &&
1872 commit == NFSV3WRITE_UNSTABLE)
1873 committed = commit;
1874 if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) {
1875 bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
1876 NFSX_V3WRITEVERF);
1877 nmp->nm_state |= NFSSTA_HASWRITEVERF;
1878 } else if (bcmp((caddr_t)tl,
1879 (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) {
1880 *must_commit = 1;
1881 bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
1882 NFSX_V3WRITEVERF);
1883 }
1884 }
1885 } else {
1886 if (mrep) {
1887 nfsm_loadattr(vp, v3, NULL, &xid);
1888 }
1889 }
1890
1891 if (updatemtime)
1892 VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
1893 mbuf_freem(mrep);
1894 /*
1895 * we seem to have a case where we end up looping on shutdown
1896 * and taking down nfs servers. For V3, error cases, there is
1897 * no way to terminate loop, if the len was 0, meaning,
1898 * nmp->nm_wsize was trashed. FreeBSD has this fix in it.
1899 * Let's try it.
1900 */
1901 if (error)
1902 break;
1903 tsiz -= len;
1904 }
1905 nfsmout:
1906 if ((mp = vnode_mount(vp)) && (vfs_flags(mp) & MNT_ASYNC))
1907 committed = NFSV3WRITE_FILESYNC;
1908 *iomode = committed;
1909 if (error)
1910 uio_uio_resid_set(uiop, tsiz);
1911 FSDBG_BOT(537, vp, committed, uio_uio_resid(uiop), error);
1912 return (error);
1913 }
1914
1915 /*
1916 * nfs mknod rpc
1917 * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1918 * mode set to specify the file type and the size field for rdev.
1919 */
1920 static int
1921 nfs_mknodrpc(
1922 vnode_t dvp,
1923 vnode_t *vpp,
1924 struct componentname *cnp,
1925 struct vnode_attr *vap,
1926 kauth_cred_t cred,
1927 proc_t p)
1928 {
1929 register struct nfsv2_sattr *sp;
1930 register u_long *tl;
1931 register caddr_t cp;
1932 register long t1, t2;
1933 vnode_t newvp = (vnode_t)0;
1934 struct nfsnode *np = (struct nfsnode *)0;
1935 struct nfs_vattr nvattr;
1936 char *cp2;
1937 caddr_t bpos, dpos;
1938 int error = 0, wccpostattr = 0, gotvp = 0;
1939 struct timespec premtime = { 0, 0 };
1940 mbuf_t mreq, mrep, md, mb, mb2;
1941 u_long rdev;
1942 u_int64_t xid;
1943 int v3 = NFS_ISV3(dvp);
1944 int gotuid, gotgid;
1945
1946 if (!VATTR_IS_ACTIVE(vap, va_type))
1947 return (EINVAL);
1948 if (vap->va_type == VCHR || vap->va_type == VBLK) {
1949 if (!VATTR_IS_ACTIVE(vap, va_rdev))
1950 return (EINVAL);
1951 rdev = txdr_unsigned(vap->va_rdev);
1952 } else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
1953 rdev = 0xffffffff;
1954 else {
1955 return (ENOTSUP);
1956 }
1957 nfsm_reqhead(NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
1958 nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
1959 if (error)
1960 return (error);
1961
1962 VATTR_SET_SUPPORTED(vap, va_mode);
1963 VATTR_SET_SUPPORTED(vap, va_uid);
1964 VATTR_SET_SUPPORTED(vap, va_gid);
1965 VATTR_SET_SUPPORTED(vap, va_data_size);
1966 VATTR_SET_SUPPORTED(vap, va_access_time);
1967 VATTR_SET_SUPPORTED(vap, va_modify_time);
1968 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
1969 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
1970
1971 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKNOD]);
1972 nfsm_fhtom(dvp, v3);
1973 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
1974 if (v3) {
1975 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1976 *tl++ = vtonfsv3_type(vap->va_type);
1977 nfsm_v3sattr(vap);
1978 if (vap->va_type == VCHR || vap->va_type == VBLK) {
1979 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1980 *tl++ = txdr_unsigned(major(vap->va_rdev));
1981 *tl = txdr_unsigned(minor(vap->va_rdev));
1982 }
1983 } else {
1984 struct timespec neg1time = { -1, -1 };
1985 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
1986 sp->sa_mode = vtonfsv2_mode(vap->va_type,
1987 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
1988 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
1989 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
1990 sp->sa_size = rdev;
1991 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1992 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
1993 } else {
1994 txdr_nfsv2time(&neg1time, &sp->sa_atime);
1995 }
1996 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1997 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
1998 } else {
1999 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2000 }
2001 }
2002 nfsm_request(dvp, NFSPROC_MKNOD, p, cred, &xid);
2003 /* XXX no EEXIST kludge here? */
2004 if (!error) {
2005 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2006 if (!gotvp) {
2007 error = nfs_lookitup(dvp, cnp->cn_nameptr,
2008 cnp->cn_namelen, cred, p, &np);
2009 if (!error)
2010 newvp = NFSTOV(np);
2011 }
2012 }
2013 if (v3 && mrep)
2014 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2015 if (!error && (gotuid || gotgid) &&
2016 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2017 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2018 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2019 /* clear ID bits if server didn't use them (or we can't tell) */
2020 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2021 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2022 }
2023 nfsm_reqdone;
2024 if (error) {
2025 if (newvp)
2026 vnode_put(newvp);
2027 } else {
2028 *vpp = newvp;
2029 }
2030 VTONFS(dvp)->n_flag |= NMODIFIED;
2031 /* if directory hadn't changed, update namecache mtime */
2032 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2033 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2034 if (!wccpostattr)
2035 NATTRINVALIDATE(VTONFS(dvp));
2036 return (error);
2037 }
2038
2039 /*
2040 * nfs mknod vop
2041 * just call nfs_mknodrpc() to do the work.
2042 */
2043 /* ARGSUSED */
2044 static int
2045 nfs_mknod(ap)
2046 struct vnop_mknod_args /* {
2047 struct vnodeop_desc *a_desc;
2048 vnode_t a_dvp;
2049 vnode_t *a_vpp;
2050 struct componentname *a_cnp;
2051 struct vnode_attr *a_vap;
2052 vfs_context_t a_context;
2053 } */ *ap;
2054 {
2055 int error;
2056
2057 error = nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap,
2058 vfs_context_ucred(ap->a_context),
2059 vfs_context_proc(ap->a_context));
2060
2061 return (error);
2062 }
2063
2064 static u_long create_verf;
2065 /*
2066 * nfs file create call
2067 */
2068 static int
2069 nfs_create(ap)
2070 struct vnop_create_args /* {
2071 struct vnodeop_desc *a_desc;
2072 vnode_t a_dvp;
2073 vnode_t *a_vpp;
2074 struct componentname *a_cnp;
2075 struct vnode_attr *a_vap;
2076 vfs_context_t a_context;
2077 } */ *ap;
2078 {
2079 vnode_t dvp = ap->a_dvp;
2080 struct vnode_attr *vap = ap->a_vap;
2081 struct componentname *cnp = ap->a_cnp;
2082 struct nfs_vattr nvattr;
2083 struct nfsv2_sattr *sp;
2084 u_long *tl;
2085 caddr_t cp;
2086 long t1, t2;
2087 struct nfsnode *np = (struct nfsnode *)0;
2088 vnode_t newvp = (vnode_t)0;
2089 caddr_t bpos, dpos, cp2;
2090 int error = 0, wccpostattr = 0, gotvp = 0, fmode = 0;
2091 struct timespec premtime = { 0, 0 };
2092 mbuf_t mreq, mrep, md, mb, mb2;
2093 int v3 = NFS_ISV3(dvp);
2094 int gotuid, gotgid;
2095 u_int64_t xid;
2096 kauth_cred_t cred;
2097 proc_t p;
2098
2099 cred = vfs_context_ucred(ap->a_context);
2100 p = vfs_context_proc(ap->a_context);
2101
2102 if (!VATTR_IS_ACTIVE(vap, va_type))
2103 return (EINVAL);
2104
2105 /*
2106 * Oops, not for me..
2107 */
2108 if (vap->va_type == VSOCK)
2109 return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap, cred, p));
2110
2111 VATTR_SET_SUPPORTED(vap, va_mode);
2112 VATTR_SET_SUPPORTED(vap, va_uid);
2113 VATTR_SET_SUPPORTED(vap, va_gid);
2114 VATTR_SET_SUPPORTED(vap, va_data_size);
2115 VATTR_SET_SUPPORTED(vap, va_access_time);
2116 VATTR_SET_SUPPORTED(vap, va_modify_time);
2117 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
2118 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
2119
2120 if (vap->va_vaflags & VA_EXCLUSIVE)
2121 fmode |= O_EXCL;
2122 again:
2123 nfsm_reqhead(NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
2124 nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
2125 if (error)
2126 return (error);
2127 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_CREATE]);
2128 nfsm_fhtom(dvp, v3);
2129 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2130 if (v3) {
2131 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
2132 if (fmode & O_EXCL) {
2133 *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
2134 nfsm_build(tl, u_long *, NFSX_V3CREATEVERF);
2135 if (!TAILQ_EMPTY(&in_ifaddrhead))
2136 *tl++ = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr.s_addr;
2137 else
2138 *tl++ = create_verf;
2139 *tl = ++create_verf;
2140 } else {
2141 *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
2142 nfsm_v3sattr(vap);
2143 }
2144 } else {
2145 struct timespec neg1time = { -1, -1 };
2146 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
2147 sp->sa_mode = vtonfsv2_mode(vap->va_type,
2148 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
2149 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
2150 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
2151 sp->sa_size = 0;
2152 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
2153 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
2154 } else {
2155 txdr_nfsv2time(&neg1time, &sp->sa_atime);
2156 }
2157 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
2158 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
2159 } else {
2160 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2161 }
2162 }
2163 nfsm_request(dvp, NFSPROC_CREATE, p, cred, &xid);
2164 if (!error) {
2165 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2166 if (!gotvp) {
2167 error = nfs_lookitup(dvp, cnp->cn_nameptr,
2168 cnp->cn_namelen, cred, p, &np);
2169 if (!error)
2170 newvp = NFSTOV(np);
2171 }
2172 }
2173 if (v3 && mrep)
2174 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2175 nfsm_reqdone;
2176 if (error) {
2177 if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
2178 fmode &= ~O_EXCL;
2179 goto again;
2180 }
2181 if (newvp)
2182 vnode_put(newvp);
2183 } else if (v3 && (fmode & O_EXCL)) {
2184 error = nfs_setattrrpc(newvp, vap, cred, p);
2185 if (error && (gotuid || gotgid)) {
2186 /* it's possible the server didn't like our attempt to set IDs. */
2187 /* so, let's try it again without those */
2188 VATTR_CLEAR_ACTIVE(vap, va_uid);
2189 VATTR_CLEAR_ACTIVE(vap, va_gid);
2190 error = nfs_setattrrpc(newvp, vap, cred, p);
2191 }
2192 if (error)
2193 vnode_put(newvp);
2194 }
2195 if (!error) {
2196 *ap->a_vpp = newvp;
2197 }
2198 VTONFS(dvp)->n_flag |= NMODIFIED;
2199 /* if directory hadn't changed, update namecache mtime */
2200 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2201 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2202 if (!wccpostattr)
2203 NATTRINVALIDATE(VTONFS(dvp));
2204 if (!error && (gotuid || gotgid) &&
2205 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2206 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2207 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2208 /* clear ID bits if server didn't use them (or we can't tell) */
2209 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2210 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2211 }
2212 return (error);
2213 }
2214
2215 /*
2216 * nfs file remove call
2217 * To try and make nfs semantics closer to ufs semantics, a file that has
2218 * other processes using the vnode is renamed instead of removed and then
2219 * removed later on the last close.
2220 * - If vnode_isinuse()
2221 * If a rename is not already in the works
2222 * call nfs_sillyrename() to set it up
2223 * else
2224 * do the remove rpc
2225 */
2226 static int
2227 nfs_remove(ap)
2228 struct vnop_remove_args /* {
2229 struct vnodeop_desc *a_desc;
2230 vnode_t a_dvp;
2231 vnode_t a_vp;
2232 struct componentname *a_cnp;
2233 int a_flags;
2234 vfs_context_t a_context;
2235 } */ *ap;
2236 {
2237 vnode_t vp = ap->a_vp;
2238 vnode_t dvp = ap->a_dvp;
2239 struct componentname *cnp = ap->a_cnp;
2240 struct nfsnode *np = VTONFS(vp);
2241 int error = 0, gofree = 0;
2242 struct nfs_vattr nvattr;
2243 kauth_cred_t cred;
2244 proc_t p;
2245
2246 cred = vfs_context_ucred(ap->a_context);
2247 p = vfs_context_proc(ap->a_context);
2248
2249 gofree = vnode_isinuse(vp, 0) ? 0 : 1;
2250 if ((ap->a_flags & VNODE_REMOVE_NODELETEBUSY) && !gofree) {
2251 /* Caller requested Carbon delete semantics, but file is busy */
2252 return (EBUSY);
2253 }
2254 if (gofree || (np->n_sillyrename &&
2255 nfs_getattr(vp, &nvattr, cred, p) == 0 &&
2256 nvattr.nva_nlink > 1)) {
2257 /*
2258 * Purge the name cache so that the chance of a lookup for
2259 * the name succeeding while the remove is in progress is
2260 * minimized.
2261 */
2262 cache_purge(vp);
2263 /*
2264 * throw away biocache buffers, mainly to avoid
2265 * unnecessary delayed writes later.
2266 */
2267 error = nfs_vinvalbuf(vp, 0, cred, p, 1);
2268 np->n_size = 0;
2269 ubc_setsize(vp, (off_t)0); /* XXX check error */
2270 /* Do the rpc */
2271 if (error != EINTR)
2272 error = nfs_removerpc(dvp, cnp->cn_nameptr,
2273 cnp->cn_namelen, cred, p);
2274 /*
2275 * Kludge City: If the first reply to the remove rpc is lost..
2276 * the reply to the retransmitted request will be ENOENT
2277 * since the file was in fact removed
2278 * Therefore, we cheat and return success.
2279 */
2280 if (error == ENOENT)
2281 error = 0;
2282 if (!error) {
2283 /*
2284 * remove nfsnode from hash now so we can't accidentally find it
2285 * again if another object gets created with the same filehandle
2286 * before this vnode gets reclaimed
2287 */
2288 lck_mtx_lock(nfs_node_hash_mutex);
2289 LIST_REMOVE(np, n_hash);
2290 np->n_flag &= ~NHASHED;
2291 lck_mtx_unlock(nfs_node_hash_mutex);
2292 }
2293 if (!error && !np->n_sillyrename) {
2294 /* clear flags now: won't get nfs_inactive for recycled vnode */
2295 /* clear all flags other than these */
2296 np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
2297 vnode_recycle(vp);
2298 }
2299 } else if (!np->n_sillyrename) {
2300 error = nfs_sillyrename(dvp, vp, cnp, cred, p);
2301 }
2302 NATTRINVALIDATE(np);
2303
2304 return (error);
2305 }
2306
2307 /*
2308 * nfs file remove rpc called from nfs_inactive
2309 */
2310 int
2311 nfs_removeit(struct sillyrename *sp)
2312 {
2313 return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, NULL));
2314 }
2315
2316 /*
2317 * Nfs remove rpc, called from nfs_remove() and nfs_removeit().
2318 */
2319 static int
2320 nfs_removerpc(dvp, name, namelen, cred, proc)
2321 vnode_t dvp;
2322 char *name;
2323 int namelen;
2324 kauth_cred_t cred;
2325 proc_t proc;
2326 {
2327 register u_long *tl;
2328 register caddr_t cp;
2329 register long t1, t2;
2330 caddr_t bpos, dpos, cp2;
2331 int error = 0, wccpostattr = 0;
2332 struct timespec premtime = { 0, 0 };
2333 mbuf_t mreq, mrep, md, mb, mb2;
2334 int v3;
2335 u_int64_t xid;
2336
2337 if (!VFSTONFS(vnode_mount(dvp)))
2338 return (ENXIO);
2339 v3 = NFS_ISV3(dvp);
2340
2341 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
2342 if (error)
2343 return (error);
2344 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_REMOVE]);
2345 nfsm_fhtom(dvp, v3);
2346 nfsm_strtom(name, namelen, NFS_MAXNAMLEN, v3);
2347 nfsm_request(dvp, NFSPROC_REMOVE, proc, cred, &xid);
2348 if (v3 && mrep)
2349 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2350 nfsm_reqdone;
2351 VTONFS(dvp)->n_flag |= NMODIFIED;
2352 /* if directory hadn't changed, update namecache mtime */
2353 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2354 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2355 if (!wccpostattr)
2356 NATTRINVALIDATE(VTONFS(dvp));
2357 return (error);
2358 }
2359
2360 /*
2361 * nfs file rename call
2362 */
2363 static int
2364 nfs_rename(ap)
2365 struct vnop_rename_args /* {
2366 struct vnodeop_desc *a_desc;
2367 vnode_t a_fdvp;
2368 vnode_t a_fvp;
2369 struct componentname *a_fcnp;
2370 vnode_t a_tdvp;
2371 vnode_t a_tvp;
2372 struct componentname *a_tcnp;
2373 vfs_context_t a_context;
2374 } */ *ap;
2375 {
2376 vnode_t fvp = ap->a_fvp;
2377 vnode_t tvp = ap->a_tvp;
2378 vnode_t fdvp = ap->a_fdvp;
2379 vnode_t tdvp = ap->a_tdvp;
2380 struct componentname *tcnp = ap->a_tcnp;
2381 struct componentname *fcnp = ap->a_fcnp;
2382 int error, inuse=0;
2383 mount_t fmp, tdmp, tmp;
2384 struct nfsnode *tnp;
2385 kauth_cred_t cred;
2386 proc_t p;
2387
2388 cred = vfs_context_ucred(ap->a_context);
2389 p = vfs_context_proc(ap->a_context);
2390
2391 tnp = tvp ? VTONFS(tvp) : NULL;
2392
2393 /* Check for cross-device rename */
2394 fmp = vnode_mount(fvp);
2395 tmp = tvp ? vnode_mount(tvp) : NULL;
2396 tdmp = vnode_mount(tdvp);
2397 if ((fmp != tdmp) || (tvp && (fmp != tmp))) {
2398 error = EXDEV;
2399 goto out;
2400 }
2401
2402 /*
2403 * If the tvp exists and is in use, sillyrename it before doing the
2404 * rename of the new file over it.
2405 * XXX Can't sillyrename a directory.
2406 * Don't sillyrename if source and target are same vnode (hard
2407 * links or case-variants)
2408 */
2409 if (tvp && tvp != fvp) {
2410 inuse = vnode_isinuse(tvp, 0);
2411 }
2412 if (inuse && !tnp->n_sillyrename && vnode_vtype(tvp) != VDIR) {
2413 if ((error = nfs_sillyrename(tdvp, tvp, tcnp, cred, p))) {
2414 /* sillyrename failed. Instead of pressing on, return error */
2415 goto out; /* should not be ENOENT. */
2416 } else {
2417 /* sillyrename succeeded.*/
2418 tvp = NULL;
2419 }
2420 }
2421
2422 error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
2423 tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, cred, p);
2424
2425 /*
2426 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
2427 */
2428 if (error == ENOENT)
2429 error = 0;
2430
2431 if (!error && tvp && tvp != fvp && !tnp->n_sillyrename) {
2432 /*
2433 * remove nfsnode from hash now so we can't accidentally find it
2434 * again if another object gets created with the same filehandle
2435 * before this vnode gets reclaimed
2436 */
2437 lck_mtx_lock(nfs_node_hash_mutex);
2438 LIST_REMOVE(tnp, n_hash);
2439 tnp->n_flag &= ~NHASHED;
2440 lck_mtx_unlock(nfs_node_hash_mutex);
2441 }
2442
2443 /* purge the old name cache entries and enter the new one */
2444 cache_purge(fvp);
2445 if (tvp) {
2446 cache_purge(tvp);
2447 if (!error && !tnp->n_sillyrename) {
2448 /* clear flags now: won't get nfs_inactive for recycled vnode */
2449 /* clear all flags other than these */
2450 tnp->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
2451 vnode_recycle(tvp);
2452 }
2453 }
2454 if (!error)
2455 cache_enter(tdvp, fvp, tcnp);
2456
2457 out:
2458 /*
2459 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
2460 */
2461 if (error == ENOENT)
2462 error = 0;
2463 return (error);
2464 }
2465
2466 /*
2467 * Do an nfs rename rpc. Called from nfs_rename() and nfs_sillyrename().
2468 */
2469 static int
2470 nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
2471 vnode_t fdvp;
2472 char *fnameptr;
2473 int fnamelen;
2474 vnode_t tdvp;
2475 char *tnameptr;
2476 int tnamelen;
2477 kauth_cred_t cred;
2478 proc_t proc;
2479 {
2480 register u_long *tl;
2481 register caddr_t cp;
2482 register long t1, t2;
2483 caddr_t bpos, dpos, cp2;
2484 int error = 0, fwccpostattr = 0, twccpostattr = 0;
2485 struct timespec fpremtime = { 0, 0 }, tpremtime = { 0, 0 };
2486 mbuf_t mreq, mrep, md, mb, mb2;
2487 int v3;
2488 u_int64_t xid;
2489
2490 if (!VFSTONFS(vnode_mount(fdvp)))
2491 return (ENXIO);
2492 v3 = NFS_ISV3(fdvp);
2493
2494 nfsm_reqhead((NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
2495 nfsm_rndup(tnamelen));
2496 if (error)
2497 return (error);
2498 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RENAME]);
2499 nfsm_fhtom(fdvp, v3);
2500 nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN, v3);
2501 nfsm_fhtom(tdvp, v3);
2502 nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN, v3);
2503 nfsm_request(fdvp, NFSPROC_RENAME, proc, cred, &xid);
2504 if (v3 && mrep) {
2505 u_int64_t txid = xid;
2506
2507 nfsm_wcc_data(fdvp, &fpremtime, fwccpostattr, &xid);
2508 nfsm_wcc_data(tdvp, &tpremtime, twccpostattr, &txid);
2509 }
2510 nfsm_reqdone;
2511 VTONFS(fdvp)->n_flag |= NMODIFIED;
2512 /* if directory hadn't changed, update namecache mtime */
2513 if (nfstimespeccmp(&VTONFS(fdvp)->n_ncmtime, &fpremtime, ==))
2514 VTONFS(fdvp)->n_ncmtime = VTONFS(fdvp)->n_vattr.nva_mtime;
2515 if (!fwccpostattr)
2516 NATTRINVALIDATE(VTONFS(fdvp));
2517 VTONFS(tdvp)->n_flag |= NMODIFIED;
2518 /* if directory hadn't changed, update namecache mtime */
2519 if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &tpremtime, ==))
2520 VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
2521 if (!twccpostattr)
2522 NATTRINVALIDATE(VTONFS(tdvp));
2523 return (error);
2524 }
2525
2526 /*
2527 * nfs hard link create call
2528 */
2529 static int
2530 nfs_link(ap)
2531 struct vnop_link_args /* {
2532 struct vnodeop_desc *a_desc;
2533 vnode_t a_vp;
2534 vnode_t a_tdvp;
2535 struct componentname *a_cnp;
2536 vfs_context_t a_context;
2537 } */ *ap;
2538 {
2539 vnode_t vp = ap->a_vp;
2540 vnode_t tdvp = ap->a_tdvp;
2541 struct componentname *cnp = ap->a_cnp;
2542 u_long *tl;
2543 caddr_t cp;
2544 long t1, t2;
2545 caddr_t bpos, dpos, cp2;
2546 int error = 0, wccpostattr = 0, attrflag = 0;
2547 struct timespec premtime = { 0, 0 };
2548 mbuf_t mreq, mrep, md, mb, mb2;
2549 int v3;
2550 u_int64_t xid;
2551 kauth_cred_t cred;
2552 proc_t p;
2553
2554 if (vnode_mount(vp) != vnode_mount(tdvp)) {
2555 return (EXDEV);
2556 }
2557
2558 cred = vfs_context_ucred(ap->a_context);
2559 p = vfs_context_proc(ap->a_context);
2560
2561 v3 = NFS_ISV3(vp);
2562
2563 /*
2564 * Push all writes to the server, so that the attribute cache
2565 * doesn't get "out of sync" with the server.
2566 * XXX There should be a better way!
2567 */
2568 nfs_flush(vp, MNT_WAIT, cred, p, 0);
2569
2570 nfsm_reqhead(NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
2571 if (error)
2572 return (error);
2573 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LINK]);
2574 nfsm_fhtom(vp, v3);
2575 nfsm_fhtom(tdvp, v3);
2576 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2577 nfsm_request(vp, NFSPROC_LINK, p, cred, &xid);
2578 if (v3 && mrep) {
2579 u_int64_t txid = xid;
2580
2581 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
2582 nfsm_wcc_data(tdvp, &premtime, wccpostattr, &txid);
2583 }
2584 nfsm_reqdone;
2585
2586 VTONFS(tdvp)->n_flag |= NMODIFIED;
2587 if (!attrflag)
2588 NATTRINVALIDATE(VTONFS(vp));
2589 /* if directory hadn't changed, update namecache mtime */
2590 if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &premtime, ==))
2591 VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
2592 if (!wccpostattr)
2593 NATTRINVALIDATE(VTONFS(tdvp));
2594 /*
2595 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2596 */
2597 if (error == EEXIST)
2598 error = 0;
2599 return (error);
2600 }
2601
2602 /*
2603 * nfs symbolic link create call
2604 */
2605 static int
2606 nfs_symlink(ap)
2607 struct vnop_symlink_args /* {
2608 struct vnodeop_desc *a_desc;
2609 vnode_t a_dvp;
2610 vnode_t *a_vpp;
2611 struct componentname *a_cnp;
2612 struct vnode_attr *a_vap;
2613 char *a_target;
2614 vfs_context_t a_context;
2615 } */ *ap;
2616 {
2617 vnode_t dvp = ap->a_dvp;
2618 struct vnode_attr *vap = ap->a_vap;
2619 struct componentname *cnp = ap->a_cnp;
2620 struct nfs_vattr nvattr;
2621 struct nfsv2_sattr *sp;
2622 u_long *tl;
2623 caddr_t cp;
2624 long t1, t2;
2625 caddr_t bpos, dpos, cp2;
2626 int slen, error = 0, wccpostattr = 0, gotvp = 0;
2627 struct timespec premtime = { 0, 0 };
2628 mbuf_t mreq, mrep, md, mb, mb2;
2629 vnode_t newvp = (vnode_t)0;
2630 int v3 = NFS_ISV3(dvp);
2631 int gotuid, gotgid;
2632 u_int64_t xid;
2633 kauth_cred_t cred;
2634 proc_t p;
2635 struct nfsnode *np = NULL;
2636
2637 cred = vfs_context_ucred(ap->a_context);
2638 p = vfs_context_proc(ap->a_context);
2639
2640 slen = strlen(ap->a_target);
2641 nfsm_reqhead(NFSX_FH(v3) + 2*NFSX_UNSIGNED +
2642 nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
2643 if (error)
2644 return (error);
2645
2646 VATTR_SET_SUPPORTED(vap, va_mode);
2647 VATTR_SET_SUPPORTED(vap, va_uid);
2648 VATTR_SET_SUPPORTED(vap, va_gid);
2649 VATTR_SET_SUPPORTED(vap, va_data_size);
2650 VATTR_SET_SUPPORTED(vap, va_access_time);
2651 VATTR_SET_SUPPORTED(vap, va_modify_time);
2652 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
2653 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
2654
2655 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SYMLINK]);
2656 nfsm_fhtom(dvp, v3);
2657 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2658 if (v3) {
2659 nfsm_v3sattr(vap);
2660 }
2661 nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN, v3);
2662 if (!v3) {
2663 struct timespec neg1time = { -1, -1 };
2664 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
2665 sp->sa_mode = vtonfsv2_mode(VLNK,
2666 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
2667 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
2668 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
2669 sp->sa_size = nfs_xdrneg1;
2670 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
2671 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
2672 } else {
2673 txdr_nfsv2time(&neg1time, &sp->sa_atime);
2674 }
2675 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
2676 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
2677 } else {
2678 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2679 }
2680 }
2681 nfsm_request(dvp, NFSPROC_SYMLINK, p, cred, &xid);
2682 if (v3 && mrep) {
2683 u_int64_t dxid = xid;
2684
2685 if (!error)
2686 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2687 nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
2688 }
2689 nfsm_reqdone;
2690
2691 VTONFS(dvp)->n_flag |= NMODIFIED;
2692 /* if directory hadn't changed, update namecache mtime */
2693 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2694 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2695 if (!wccpostattr)
2696 NATTRINVALIDATE(VTONFS(dvp));
2697
2698 /*
2699 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
2700 * if we can succeed in looking up the symlink.
2701 */
2702 if ((error == EEXIST) || (!error && !gotvp)) {
2703 if (newvp) {
2704 vnode_put(newvp);
2705 newvp = NULL;
2706 }
2707 error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cred, p, &np);
2708 if (!error) {
2709 newvp = NFSTOV(np);
2710 if (vnode_vtype(newvp) != VLNK)
2711 error = EEXIST;
2712 }
2713 }
2714 if (!error && (gotuid || gotgid) &&
2715 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2716 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2717 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2718 /* clear ID bits if server didn't use them (or we can't tell) */
2719 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2720 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2721 }
2722 if (error) {
2723 if (newvp)
2724 vnode_put(newvp);
2725 } else {
2726 *ap->a_vpp = newvp;
2727 }
2728 return (error);
2729 }
2730
2731 /*
2732 * nfs make dir call
2733 */
2734 static int
2735 nfs_mkdir(ap)
2736 struct vnop_mkdir_args /* {
2737 struct vnodeop_desc *a_desc;
2738 vnode_t a_dvp;
2739 vnode_t *a_vpp;
2740 struct componentname *a_cnp;
2741 struct vnode_attr *a_vap;
2742 vfs_context_t a_context;
2743 } */ *ap;
2744 {
2745 vnode_t dvp = ap->a_dvp;
2746 struct vnode_attr *vap = ap->a_vap;
2747 struct componentname *cnp = ap->a_cnp;
2748 struct nfs_vattr nvattr;
2749 struct nfsv2_sattr *sp;
2750 u_long *tl;
2751 caddr_t cp;
2752 long t1, t2;
2753 int len;
2754 struct nfsnode *np = (struct nfsnode *)0;
2755 vnode_t newvp = (vnode_t)0;
2756 caddr_t bpos, dpos, cp2;
2757 int error = 0, wccpostattr = 0;
2758 struct timespec premtime = { 0, 0 };
2759 int gotvp = 0;
2760 mbuf_t mreq, mrep, md, mb, mb2;
2761 int v3 = NFS_ISV3(dvp);
2762 int gotuid, gotgid;
2763 u_int64_t xid, dxid;
2764 kauth_cred_t cred;
2765 proc_t p;
2766
2767 cred = vfs_context_ucred(ap->a_context);
2768 p = vfs_context_proc(ap->a_context);
2769
2770 len = cnp->cn_namelen;
2771 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
2772 if (error)
2773 return (error);
2774
2775 VATTR_SET_SUPPORTED(vap, va_mode);
2776 VATTR_SET_SUPPORTED(vap, va_uid);
2777 VATTR_SET_SUPPORTED(vap, va_gid);
2778 VATTR_SET_SUPPORTED(vap, va_data_size);
2779 VATTR_SET_SUPPORTED(vap, va_access_time);
2780 VATTR_SET_SUPPORTED(vap, va_modify_time);
2781 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
2782 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
2783
2784 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKDIR]);
2785 nfsm_fhtom(dvp, v3);
2786 nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
2787 if (v3) {
2788 nfsm_v3sattr(vap);
2789 } else {
2790 struct timespec neg1time = { -1, -1 };
2791 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
2792 sp->sa_mode = vtonfsv2_mode(VDIR,
2793 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
2794 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
2795 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
2796 sp->sa_size = nfs_xdrneg1;
2797 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
2798 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
2799 } else {
2800 txdr_nfsv2time(&neg1time, &sp->sa_atime);
2801 }
2802 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
2803 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
2804 } else {
2805 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2806 }
2807 }
2808 nfsm_request(dvp, NFSPROC_MKDIR, p, cred, &xid);
2809 dxid = xid;
2810 if (!error)
2811 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2812 if (v3 && mrep)
2813 nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
2814 nfsm_reqdone;
2815 VTONFS(dvp)->n_flag |= NMODIFIED;
2816 /* if directory hadn't changed, update namecache mtime */
2817 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2818 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2819 if (!wccpostattr)
2820 NATTRINVALIDATE(VTONFS(dvp));
2821 /*
2822 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
2823 * if we can succeed in looking up the directory.
2824 */
2825 if (error == EEXIST || (!error && !gotvp)) {
2826 if (newvp) {
2827 vnode_put(newvp);
2828 newvp = NULL;
2829 }
2830 error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cred, p, &np);
2831 if (!error) {
2832 newvp = NFSTOV(np);
2833 if (vnode_vtype(newvp) != VDIR)
2834 error = EEXIST;
2835 }
2836 }
2837 if (!error && (gotuid || gotgid) &&
2838 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2839 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2840 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2841 /* clear ID bits if server didn't use them (or we can't tell) */
2842 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2843 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2844 }
2845 if (error) {
2846 if (newvp)
2847 vnode_put(newvp);
2848 } else {
2849 *ap->a_vpp = newvp;
2850 }
2851 return (error);
2852 }
2853
2854 /*
2855 * nfs remove directory call
2856 */
2857 static int
2858 nfs_rmdir(ap)
2859 struct vnop_rmdir_args /* {
2860 struct vnodeop_desc *a_desc;
2861 vnode_t a_dvp;
2862 vnode_t a_vp;
2863 struct componentname *a_cnp;
2864 vfs_context_t a_context;
2865 } */ *ap;
2866 {
2867 vnode_t vp = ap->a_vp;
2868 vnode_t dvp = ap->a_dvp;
2869 struct componentname *cnp = ap->a_cnp;
2870 u_long *tl;
2871 caddr_t cp;
2872 long t1, t2;
2873 caddr_t bpos, dpos, cp2;
2874 int error = 0, wccpostattr = 0;
2875 struct timespec premtime = { 0, 0 };
2876 mbuf_t mreq, mrep, md, mb, mb2;
2877 int v3 = NFS_ISV3(dvp);
2878 u_int64_t xid;
2879 kauth_cred_t cred;
2880 proc_t p;
2881
2882 cred = vfs_context_ucred(ap->a_context);
2883 p = vfs_context_proc(ap->a_context);
2884
2885 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
2886 if (error)
2887 return (error);
2888 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RMDIR]);
2889 nfsm_fhtom(dvp, v3);
2890 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2891 nfsm_request(dvp, NFSPROC_RMDIR, p, cred, &xid);
2892 if (v3 && mrep)
2893 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2894 nfsm_reqdone;
2895 VTONFS(dvp)->n_flag |= NMODIFIED;
2896 /* if directory hadn't changed, update namecache mtime */
2897 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2898 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2899 if (!wccpostattr)
2900 NATTRINVALIDATE(VTONFS(dvp));
2901 cache_purge(vp);
2902 /*
2903 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2904 */
2905 if (error == ENOENT)
2906 error = 0;
2907 if (!error) {
2908 /*
2909 * remove nfsnode from hash now so we can't accidentally find it
2910 * again if another object gets created with the same filehandle
2911 * before this vnode gets reclaimed
2912 */
2913 lck_mtx_lock(nfs_node_hash_mutex);
2914 LIST_REMOVE(VTONFS(vp), n_hash);
2915 VTONFS(vp)->n_flag &= ~NHASHED;
2916 lck_mtx_unlock(nfs_node_hash_mutex);
2917 }
2918 return (error);
2919 }
2920
2921 /*
2922 * nfs readdir call
2923 */
2924 static int
2925 nfs_readdir(ap)
2926 struct vnop_readdir_args /* {
2927 struct vnodeop_desc *a_desc;
2928 vnode_t a_vp;
2929 struct uio *a_uio;
2930 int *a_eofflag;
2931 int *a_ncookies;
2932 u_long **a_cookies;
2933 vfs_context_t a_context;
2934 } */ *ap;
2935 {
2936 vnode_t vp = ap->a_vp;
2937 struct nfsnode *np = VTONFS(vp);
2938 struct uio *uio = ap->a_uio;
2939 int tresid, error;
2940 struct nfs_vattr nvattr;
2941 kauth_cred_t cred;
2942 proc_t p;
2943
2944 if (vnode_vtype(vp) != VDIR)
2945 return (EPERM);
2946
2947 cred = vfs_context_ucred(ap->a_context);
2948 p = vfs_context_proc(ap->a_context);
2949
2950 /*
2951 * First, check for hit on the EOF offset cache
2952 */
2953 if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
2954 (np->n_flag & NMODIFIED) == 0) {
2955 if (!nfs_getattr(vp, &nvattr, cred, p)) {
2956 if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, ==)) {
2957 OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_hits);
2958 return (0);
2959 }
2960 if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=)) {
2961 /* directory changed, purge any name cache entries */
2962 cache_purge(vp);
2963 }
2964 }
2965 }
2966
2967 /*
2968 * Call nfs_bioread() to do the real work.
2969 */
2970 // LP64todo - fix this
2971 tresid = uio_uio_resid(uio);
2972 error = nfs_bioread(vp, uio, 0, cred, p);
2973
2974 if (!error && uio_uio_resid(uio) == tresid)
2975 OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_misses);
2976 return (error);
2977 }
2978
2979 /*
2980 * Readdir rpc call.
2981 * Called from below the buffer cache by nfs_doio().
2982 */
2983 int
2984 nfs_readdirrpc(
2985 vnode_t vp,
2986 struct uio *uiop,
2987 kauth_cred_t cred,
2988 proc_t p)
2989 {
2990 register int len, skiplen, left;
2991 register struct dirent *dp;
2992 register u_long *tl;
2993 register caddr_t cp;
2994 register long t1, t2;
2995 register nfsuint64 *cookiep;
2996 caddr_t bpos, dpos, cp2;
2997 mbuf_t mreq, mrep, md, mb, mb2;
2998 nfsuint64 cookie;
2999 struct nfsmount *nmp;
3000 struct nfsnode *dnp = VTONFS(vp);
3001 u_quad_t fileno;
3002 int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
3003 int attrflag;
3004 int v3, nmreaddirsize;
3005 u_int64_t xid;
3006
3007 #ifndef nolint
3008 dp = (struct dirent *)0;
3009 #endif
3010 #if DIAGNOSTIC
3011 if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) ||
3012 (uio_uio_resid(uiop) & (NFS_DIRBLKSIZ - 1)))
3013 panic("nfs_readdirrpc: bad uio");
3014 #endif
3015 nmp = VFSTONFS(vnode_mount(vp));
3016 if (!nmp)
3017 return (ENXIO);
3018 v3 = NFS_ISV3(vp);
3019 nmreaddirsize = nmp->nm_readdirsize;
3020
3021 /*
3022 * If there is no cookie, assume directory was stale.
3023 */
3024 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
3025 if (cookiep)
3026 cookie = *cookiep;
3027 else
3028 return (NFSERR_BAD_COOKIE);
3029 /*
3030 * Loop around doing readdir rpc's of size nm_readdirsize
3031 * truncated to a multiple of DIRBLKSIZ.
3032 * The stopping criteria is EOF or buffer full.
3033 */
3034 while (more_dirs && bigenough) {
3035 nfsm_reqhead(NFSX_FH(v3) + NFSX_READDIR(v3));
3036 if (error)
3037 goto nfsmout;
3038 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIR]);
3039 nfsm_fhtom(vp, v3);
3040 if (v3) {
3041 nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
3042 *tl++ = cookie.nfsuquad[0];
3043 *tl++ = cookie.nfsuquad[1];
3044 *tl++ = dnp->n_cookieverf.nfsuquad[0];
3045 *tl++ = dnp->n_cookieverf.nfsuquad[1];
3046 } else {
3047 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
3048 *tl++ = cookie.nfsuquad[0];
3049 }
3050 *tl = txdr_unsigned(nmreaddirsize);
3051 nfsm_request(vp, NFSPROC_READDIR, p, cred, &xid);
3052 if (v3) {
3053 if (mrep) {
3054 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
3055 }
3056 if (!error) {
3057 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3058 dnp->n_cookieverf.nfsuquad[0] = *tl++;
3059 dnp->n_cookieverf.nfsuquad[1] = *tl;
3060 } else {
3061 mbuf_freem(mrep);
3062 goto nfsmout;
3063 }
3064 } else if (!mrep) {
3065 // XXX assert error?
3066 goto nfsmout;
3067 }
3068 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3069 more_dirs = fxdr_unsigned(int, *tl);
3070
3071 /* loop thru the dir entries, doctoring them to 4bsd form */
3072 while (more_dirs && bigenough) {
3073 if (v3) {
3074 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3075 fxdr_hyper(tl, &fileno);
3076 len = fxdr_unsigned(int, *(tl + 2));
3077 } else {
3078 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3079 fileno = fxdr_unsigned(u_quad_t, *tl++);
3080 len = fxdr_unsigned(int, *tl);
3081 }
3082 /* Note: v3 supports longer names, but struct dirent doesn't */
3083 /* so we just truncate the names to fit */
3084 if (len <= 0) {
3085 error = EBADRPC;
3086 mbuf_freem(mrep);
3087 goto nfsmout;
3088 }
3089 if (len > MAXNAMLEN) {
3090 skiplen = len - MAXNAMLEN;
3091 len = MAXNAMLEN;
3092 } else {
3093 skiplen = 0;
3094 }
3095 tlen = nfsm_rndup(len);
3096 if (tlen == len)
3097 tlen += 4; /* To ensure null termination */
3098 left = DIRBLKSIZ - blksiz;
3099 if ((tlen + (int)DIRHDSIZ) > left) {
3100 dp->d_reclen += left;
3101 uio_iov_base_add(uiop, left);
3102 uio_iov_len_add(uiop, -left);
3103 uiop->uio_offset += left;
3104 uio_uio_resid_add(uiop, -left);
3105 blksiz = 0;
3106 }
3107 if ((tlen + (int)DIRHDSIZ) > uio_uio_resid(uiop))
3108 bigenough = 0;
3109 if (bigenough) {
3110 // LP64todo - fix this!
3111 dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3112 dp->d_fileno = (int)fileno;
3113 dp->d_namlen = len;
3114 dp->d_reclen = tlen + DIRHDSIZ;
3115 dp->d_type = DT_UNKNOWN;
3116 blksiz += dp->d_reclen;
3117 if (blksiz == DIRBLKSIZ)
3118 blksiz = 0;
3119 uiop->uio_offset += DIRHDSIZ;
3120 #if LP64KERN
3121 uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
3122 uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
3123 #else
3124 uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
3125 uio_iov_len_add(uiop, -((int)DIRHDSIZ));
3126 #endif
3127 uio_iov_base_add(uiop, DIRHDSIZ);
3128 nfsm_mtouio(uiop, len);
3129 // LP64todo - fix this!
3130 cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3131 tlen -= len;
3132 *cp = '\0'; /* null terminate */
3133 uio_iov_base_add(uiop, tlen);
3134 uio_iov_len_add(uiop, -tlen);
3135 uiop->uio_offset += tlen;
3136 uio_uio_resid_add(uiop, -tlen);
3137 } else {
3138 nfsm_adv(nfsm_rndup(len));
3139 }
3140 if (skiplen)
3141 nfsm_adv(nfsm_rndup(skiplen));
3142 if (v3) {
3143 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3144 } else {
3145 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3146 }
3147 if (bigenough) {
3148 cookie.nfsuquad[0] = *tl++;
3149 if (v3)
3150 cookie.nfsuquad[1] = *tl++;
3151 } else if (v3)
3152 tl += 2;
3153 else
3154 tl++;
3155 more_dirs = fxdr_unsigned(int, *tl);
3156 }
3157 /*
3158 * If at end of rpc data, get the eof boolean
3159 */
3160 if (!more_dirs) {
3161 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3162 more_dirs = (fxdr_unsigned(int, *tl) == 0);
3163 }
3164 mbuf_freem(mrep);
3165 }
3166 /*
3167 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3168 * by increasing d_reclen for the last record.
3169 */
3170 if (blksiz > 0) {
3171 left = DIRBLKSIZ - blksiz;
3172 dp->d_reclen += left;
3173 uio_iov_base_add(uiop, left);
3174 uio_iov_len_add(uiop, -left);
3175 uiop->uio_offset += left;
3176 uio_uio_resid_add(uiop, -left);
3177 }
3178
3179 /*
3180 * We are now either at the end of the directory or have filled the
3181 * block.
3182 */
3183 if (bigenough)
3184 dnp->n_direofoffset = uiop->uio_offset;
3185 else {
3186 if (uio_uio_resid(uiop) > 0)
3187 printf("EEK! readdirrpc resid > 0\n");
3188 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
3189 if (cookiep)
3190 *cookiep = cookie;
3191 }
3192 nfsmout:
3193 return (error);
3194 }
3195
3196 /*
3197 * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc().
3198 */
3199 int
3200 nfs_readdirplusrpc(
3201 vnode_t vp,
3202 struct uio *uiop,
3203 kauth_cred_t cred,
3204 proc_t p)
3205 {
3206 int len, skiplen, left;
3207 struct dirent *dp;
3208 u_long *tl;
3209 caddr_t cp;
3210 long t1, t2;
3211 vnode_t newvp;
3212 nfsuint64 *cookiep;
3213 caddr_t bpos, dpos, cp2;
3214 mbuf_t mreq, mrep, md, mb, mb2;
3215 struct componentname cn, *cnp = &cn;
3216 nfsuint64 cookie;
3217 struct nfsmount *nmp;
3218 struct nfsnode *dnp = VTONFS(vp), *np;
3219 u_char *fhp;
3220 u_quad_t fileno;
3221 int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
3222 int attrflag, fhsize, nmreaddirsize, nmrsize;
3223 u_int64_t xid, savexid;
3224 struct nfs_vattr nvattr;
3225
3226 #ifndef nolint
3227 dp = (struct dirent *)0;
3228 #endif
3229 #if DIAGNOSTIC
3230 if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
3231 (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
3232 panic("nfs_readdirplusrpc: bad uio");
3233 #endif
3234 nmp = VFSTONFS(vnode_mount(vp));
3235 if (!nmp)
3236 return (ENXIO);
3237 nmreaddirsize = nmp->nm_readdirsize;
3238 nmrsize = nmp->nm_rsize;
3239
3240 bzero(cnp, sizeof(*cnp));
3241 newvp = NULLVP;
3242
3243 /*
3244 * If there is no cookie, assume directory was stale.
3245 */
3246 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
3247 if (cookiep)
3248 cookie = *cookiep;
3249 else
3250 return (NFSERR_BAD_COOKIE);
3251 /*
3252 * Loop around doing readdir rpc's of size nm_readdirsize
3253 * truncated to a multiple of DIRBLKSIZ.
3254 * The stopping criteria is EOF or buffer full.
3255 */
3256 while (more_dirs && bigenough) {
3257 nfsm_reqhead(NFSX_FH(1) + 6 * NFSX_UNSIGNED);
3258 if (error)
3259 goto nfsmout;
3260 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIRPLUS]);
3261 nfsm_fhtom(vp, 1);
3262 nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED);
3263 *tl++ = cookie.nfsuquad[0];
3264 *tl++ = cookie.nfsuquad[1];
3265 *tl++ = dnp->n_cookieverf.nfsuquad[0];
3266 *tl++ = dnp->n_cookieverf.nfsuquad[1];
3267 *tl++ = txdr_unsigned(nmreaddirsize);
3268 *tl = txdr_unsigned(nmrsize);
3269 nfsm_request(vp, NFSPROC_READDIRPLUS, p, cred, &xid);
3270 savexid = xid;
3271 if (mrep) {
3272 nfsm_postop_attr_update(vp, 1, attrflag, &xid);
3273 }
3274 if (error) {
3275 mbuf_freem(mrep);
3276 goto nfsmout;
3277 }
3278 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3279 dnp->n_cookieverf.nfsuquad[0] = *tl++;
3280 dnp->n_cookieverf.nfsuquad[1] = *tl++;
3281 more_dirs = fxdr_unsigned(int, *tl);
3282
3283 /* loop thru the dir entries, doctoring them to 4bsd form */
3284 while (more_dirs && bigenough) {
3285 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3286 fxdr_hyper(tl, &fileno);
3287 len = fxdr_unsigned(int, *(tl + 2));
3288 /* Note: v3 supports longer names, but struct dirent doesn't */
3289 /* so we just truncate the names to fit */
3290 if (len <= 0) {
3291 error = EBADRPC;
3292 mbuf_freem(mrep);
3293 goto nfsmout;
3294 }
3295 if (len > MAXNAMLEN) {
3296 skiplen = len - MAXNAMLEN;
3297 len = MAXNAMLEN;
3298 } else {
3299 skiplen = 0;
3300 }
3301 tlen = nfsm_rndup(len);
3302 if (tlen == len)
3303 tlen += 4; /* To ensure null termination*/
3304 left = DIRBLKSIZ - blksiz;
3305 if ((tlen + (int)DIRHDSIZ) > left) {
3306 dp->d_reclen += left;
3307 uio_iov_base_add(uiop, left);
3308 uio_iov_len_add(uiop, -left);
3309 uiop->uio_offset += left;
3310 uio_uio_resid_add(uiop, -left);
3311 blksiz = 0;
3312 }
3313 if ((tlen + (int)DIRHDSIZ) > uio_uio_resid(uiop))
3314 bigenough = 0;
3315 if (bigenough) {
3316 // LP64todo - fix this!
3317 dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3318 dp->d_fileno = (int)fileno;
3319 dp->d_namlen = len;
3320 dp->d_reclen = tlen + DIRHDSIZ;
3321 dp->d_type = DT_UNKNOWN;
3322 blksiz += dp->d_reclen;
3323 if (blksiz == DIRBLKSIZ)
3324 blksiz = 0;
3325 uiop->uio_offset += DIRHDSIZ;
3326 #if LP64KERN
3327 uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
3328 uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
3329 #else
3330 uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
3331 uio_iov_len_add(uiop, -((int)DIRHDSIZ));
3332 #endif
3333 uio_iov_base_add(uiop, DIRHDSIZ);
3334 // LP64todo - fix this!
3335 cnp->cn_nameptr = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3336 cnp->cn_namelen = len;
3337 nfsm_mtouio(uiop, len);
3338 cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3339 tlen -= len;
3340 *cp = '\0';
3341 uio_iov_base_add(uiop, tlen);
3342 uio_iov_len_add(uiop, -tlen);
3343 uiop->uio_offset += tlen;
3344 uio_uio_resid_add(uiop, -tlen);
3345 } else {
3346 nfsm_adv(nfsm_rndup(len));
3347 }
3348 if (skiplen)
3349 nfsm_adv(nfsm_rndup(skiplen));
3350 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3351 if (bigenough) {
3352 cookie.nfsuquad[0] = *tl++;
3353 cookie.nfsuquad[1] = *tl++;
3354 } else
3355 tl += 2;
3356
3357 /*
3358 * Since the attributes are before the file handle
3359 * (sigh), we must skip over the attributes and then
3360 * come back and get them.
3361 */
3362 attrflag = fxdr_unsigned(int, *tl);
3363 if (attrflag) {
3364 /* grab attributes */
3365 nfsm_attr_get(1, &nvattr);
3366 dp->d_type = IFTODT(VTTOIF(nvattr.nva_type));
3367 /* check for file handle */
3368 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3369 doit = fxdr_unsigned(int, *tl);
3370 if (doit) {
3371 nfsm_getfh(fhp, fhsize, 1);
3372 if (NFS_CMPFH(dnp, fhp, fhsize)) {
3373 error = vnode_ref(vp);
3374 if (error) {
3375 doit = 0;
3376 } else {
3377 newvp = vp;
3378 np = dnp;
3379 }
3380 } else if (!bigenough ||
3381 (cnp->cn_namelen == 2 &&
3382 cnp->cn_nameptr[1] == '.' &&
3383 cnp->cn_nameptr[0] == '.')) {
3384 /*
3385 * XXXmacko I don't think this ".." thing is a problem anymore.
3386 * don't doit if we can't guarantee
3387 * that this entry is NOT ".." because
3388 * we would have to drop the lock on
3389 * the directory before getting the
3390 * lock on the ".." vnode... and we
3391 * don't want to drop the dvp lock in
3392 * the middle of a readdirplus.
3393 */
3394 doit = 0;
3395 } else {
3396 cnp->cn_hash = 0;
3397
3398 error = nfs_nget(vnode_mount(vp), vp, cnp,
3399 fhp, fhsize, &nvattr, &xid,
3400 NG_MAKEENTRY, &np);
3401 if (error)
3402 doit = 0;
3403 else
3404 newvp = NFSTOV(np);
3405 }
3406 }
3407 /* update attributes if not already updated */
3408 if (doit && bigenough && (np->n_xid <= savexid)) {
3409 xid = savexid;
3410 nfs_loadattrcache(np, &nvattr, &xid, 0);
3411 /* any error can be ignored */
3412 }
3413 } else {
3414 /* Just skip over the file handle */
3415 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3416 i = fxdr_unsigned(int, *tl);
3417 nfsm_adv(nfsm_rndup(i));
3418 }
3419 if (newvp != NULLVP) {
3420 if (newvp == vp)
3421 vnode_rele(newvp);
3422 else
3423 vnode_put(newvp);
3424 newvp = NULLVP;
3425 }
3426 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3427 more_dirs = fxdr_unsigned(int, *tl);
3428 }
3429 /*
3430 * If at end of rpc data, get the eof boolean
3431 */
3432 if (!more_dirs) {
3433 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3434 more_dirs = (fxdr_unsigned(int, *tl) == 0);
3435 }
3436 mbuf_freem(mrep);
3437 }
3438 /*
3439 * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
3440 * by increasing d_reclen for the last record.
3441 */
3442 if (blksiz > 0) {
3443 left = DIRBLKSIZ - blksiz;
3444 dp->d_reclen += left;
3445 uio_iov_base_add(uiop, left);
3446 uio_iov_len_add(uiop, -left);
3447 uiop->uio_offset += left;
3448 uio_uio_resid_add(uiop, -left);
3449 }
3450
3451 /*
3452 * We are now either at the end of the directory or have filled the
3453 * block.
3454 */
3455 if (bigenough)
3456 dnp->n_direofoffset = uiop->uio_offset;
3457 else {
3458 if (uio_uio_resid(uiop) > 0)
3459 printf("EEK! readdirplusrpc resid > 0\n");
3460 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
3461 if (cookiep)
3462 *cookiep = cookie;
3463 }
3464 nfsmout:
3465 return (error);
3466 }
3467
3468 /*
3469 * Silly rename. To make the NFS filesystem that is stateless look a little
3470 * more like the "ufs" a remove of an active vnode is translated to a rename
3471 * to a funny looking filename that is removed by nfs_inactive on the
3472 * nfsnode. There is the potential for another process on a different client
3473 * to create the same funny name between the nfs_lookitup() fails and the
3474 * nfs_rename() completes, but...
3475 */
3476
3477 /* format of "random" names and next name to try */
3478 /* (note: shouldn't exceed size of sillyrename.s_name) */
3479 static char sillyrename_name[] = ".nfsAAA%04x4.4";
3480
3481 static int
3482 nfs_sillyrename(
3483 vnode_t dvp,
3484 vnode_t vp,
3485 struct componentname *cnp,
3486 kauth_cred_t cred,
3487 proc_t p)
3488 {
3489 register struct sillyrename *sp;
3490 struct nfsnode *np;
3491 int error;
3492 short pid;
3493 kauth_cred_t tmpcred;
3494 int i, j, k;
3495
3496 cache_purge(vp);
3497 np = VTONFS(vp);
3498 #if DIAGNOSTIC
3499 if (vnode_vtype(vp) == VDIR)
3500 panic("nfs_sillyrename: dir");
3501 #endif
3502 MALLOC_ZONE(sp, struct sillyrename *,
3503 sizeof (struct sillyrename), M_NFSREQ, M_WAITOK);
3504 if (!sp)
3505 return (ENOMEM);
3506 kauth_cred_ref(cred);
3507 sp->s_cred = cred;
3508 sp->s_dvp = dvp;
3509 error = vnode_ref(dvp);
3510 if (error)
3511 goto bad_norele;
3512
3513 /* Fudge together a funny name */
3514 pid = proc_pid(p);
3515 sp->s_namlen = sprintf(sp->s_name, sillyrename_name, pid);
3516
3517 /* Try lookitups until we get one that isn't there */
3518 i = j = k = 0;
3519 while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, NULL) == 0) {
3520 if (sp->s_name[4]++ >= 'z')
3521 sp->s_name[4] = 'A';
3522 if (++i > ('z' - 'A' + 1)) {
3523 i = 0;
3524 if (sp->s_name[5]++ >= 'z')
3525 sp->s_name[5] = 'A';
3526 if (++j > ('z' - 'A' + 1)) {
3527 j = 0;
3528 if (sp->s_name[6]++ >= 'z')
3529 sp->s_name[6] = 'A';
3530 if (++k > ('z' - 'A' + 1)) {
3531 error = EINVAL;
3532 goto bad;
3533 }
3534 }
3535 }
3536 }
3537 /* make note of next "random" name to try */
3538 if ((sillyrename_name[4] = (sp->s_name[4] + 1)) > 'z') {
3539 sillyrename_name[4] = 'A';
3540 if ((sillyrename_name[5] = (sp->s_name[5] + 1)) > 'z') {
3541 sillyrename_name[5] = 'A';
3542 if ((sillyrename_name[6] = (sp->s_name[6] + 1)) > 'z')
3543 sillyrename_name[6] = 'A';
3544 }
3545 }
3546 /* now, do the rename */
3547 error = nfs_renamerpc(dvp, cnp->cn_nameptr, cnp->cn_namelen,
3548 dvp, sp->s_name, sp->s_namlen, sp->s_cred, p);
3549 if (error)
3550 goto bad;
3551 error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, &np);
3552 #if DIAGNOSTIC
3553 kprintf("sillyrename: %s, vp=%x, np=%x, dvp=%x\n",
3554 &sp->s_name[0], (unsigned)vp, (unsigned)np, (unsigned)dvp);
3555 #endif
3556 np->n_sillyrename = sp;
3557 return (0);
3558 bad:
3559 vnode_rele(sp->s_dvp);
3560 bad_norele:
3561 tmpcred = sp->s_cred;
3562 sp->s_cred = NOCRED;
3563 kauth_cred_rele(tmpcred);
3564 FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
3565 return (error);
3566 }
3567
3568 /*
3569 * Look up a file name and optionally either update the file handle or
3570 * allocate an nfsnode, depending on the value of npp.
3571 * npp == NULL --> just do the lookup
3572 * *npp == NULL --> allocate a new nfsnode and make sure attributes are
3573 * handled too
3574 * *npp != NULL --> update the file handle in the vnode
3575 */
3576 static int
3577 nfs_lookitup(dvp, name, len, cred, procp, npp)
3578 vnode_t dvp;
3579 char *name;
3580 int len;
3581 kauth_cred_t cred;
3582 proc_t procp;
3583 struct nfsnode **npp;
3584 {
3585 u_long *tl;
3586 caddr_t cp;
3587 long t1, t2;
3588 vnode_t newvp = (vnode_t)0;
3589 struct nfsnode *np, *dnp = VTONFS(dvp);
3590 caddr_t bpos, dpos, cp2;
3591 int error = 0, fhlen, attrflag;
3592 mbuf_t mreq, mrep, md, mb, mb2;
3593 u_char *nfhp;
3594 int v3;
3595 u_int64_t xid, dxid, savedxid;
3596 struct nfs_vattr nvattr;
3597
3598 if (!VFSTONFS(vnode_mount(dvp)))
3599 return (ENXIO);
3600 v3 = NFS_ISV3(dvp);
3601
3602 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
3603 if (error)
3604 return (error);
3605 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
3606 nfsm_fhtom(dvp, v3);
3607 nfsm_strtom(name, len, NFS_MAXNAMLEN, v3);
3608 nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred, &xid);
3609 if (npp && !error) {
3610 savedxid = xid;
3611 nfsm_getfh(nfhp, fhlen, v3);
3612 /* get attributes */
3613 if (v3) {
3614 nfsm_postop_attr_get(v3, attrflag, &nvattr);
3615 if (!attrflag) {
3616 /* We need valid attributes in order */
3617 /* to call nfs_nget/vnode_create(). */
3618 error = nfs_getattr_no_vnode(vnode_mount(dvp),
3619 nfhp, fhlen, cred, procp, &nvattr, &xid);
3620 if (error) {
3621 mbuf_freem(mrep);
3622 goto nfsmout;
3623 }
3624 }
3625 dxid = savedxid;
3626 nfsm_postop_attr_update(dvp, v3, attrflag, &dxid);
3627 } else {
3628 nfsm_attr_get(v3, &nvattr);
3629 }
3630 if (*npp) {
3631 np = *npp;
3632 if (fhlen != np->n_fhsize) {
3633 u_char *oldbuf = (np->n_fhsize > NFS_SMALLFH) ? np->n_fhp : NULL;
3634 if (fhlen > NFS_SMALLFH) {
3635 MALLOC_ZONE(np->n_fhp, u_char *, fhlen, M_NFSBIGFH, M_WAITOK);
3636 if (!np->n_fhp) {
3637 np->n_fhp = oldbuf;
3638 error = ENOMEM;
3639 mbuf_freem(mrep);
3640 goto nfsmout;
3641 }
3642 } else {
3643 np->n_fhp = &np->n_fh[0];
3644 }
3645 if (oldbuf) {
3646 FREE_ZONE(oldbuf, np->n_fhsize, M_NFSBIGFH);
3647 }
3648 }
3649 bcopy(nfhp, np->n_fhp, fhlen);
3650 np->n_fhsize = fhlen;
3651 newvp = NFSTOV(np);
3652 error = nfs_loadattrcache(np, &nvattr, &xid, 0);
3653 if (error) {
3654 mbuf_freem(mrep);
3655 goto nfsmout;
3656 }
3657 } else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
3658 newvp = dvp;
3659 if (dnp->n_xid <= savedxid) {
3660 dxid = savedxid;
3661 error = nfs_loadattrcache(dnp, &nvattr, &dxid, 0);
3662 if (error) {
3663 mbuf_freem(mrep);
3664 goto nfsmout;
3665 }
3666 }
3667 } else {
3668 struct componentname cn, *cnp = &cn;
3669 bzero(cnp, sizeof(*cnp));
3670 cnp->cn_nameptr = name;
3671 cnp->cn_namelen = len;
3672
3673 error = nfs_nget(vnode_mount(dvp), dvp, cnp, nfhp, fhlen,
3674 &nvattr, &xid, NG_MAKEENTRY, &np);
3675 if (error) {
3676 mbuf_freem(mrep);
3677 return (error);
3678 }
3679 newvp = NFSTOV(np);
3680 }
3681 }
3682 nfsm_reqdone;
3683 if (npp && *npp == NULL) {
3684 if (error) {
3685 if (newvp) {
3686 if (newvp == dvp)
3687 vnode_rele(newvp);
3688 else
3689 vnode_put(newvp);
3690 }
3691 } else
3692 *npp = np;
3693 }
3694 return (error);
3695 }
3696
3697 /*
3698 * Nfs Version 3 commit rpc
3699 */
3700 int
3701 nfs_commit(vp, offset, count, cred, procp)
3702 vnode_t vp;
3703 u_quad_t offset;
3704 u_int32_t count;
3705 kauth_cred_t cred;
3706 proc_t procp;
3707 {
3708 caddr_t cp;
3709 u_long *tl;
3710 int t1, t2;
3711 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3712 caddr_t bpos, dpos, cp2;
3713 int error = 0, wccpostattr = 0;
3714 struct timespec premtime = { 0, 0 };
3715 mbuf_t mreq, mrep, md, mb, mb2;
3716 u_int64_t xid;
3717
3718 FSDBG(521, vp, offset, count, nmp->nm_state);
3719 if (!nmp)
3720 return (ENXIO);
3721 if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0)
3722 return (0);
3723 nfsm_reqhead(NFSX_FH(1));
3724 if (error)
3725 return (error);
3726 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_COMMIT]);
3727 nfsm_fhtom(vp, 1);
3728 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
3729 txdr_hyper(&offset, tl);
3730 tl += 2;
3731 *tl = txdr_unsigned(count);
3732 nfsm_request(vp, NFSPROC_COMMIT, procp, cred, &xid);
3733 if (mrep) {
3734 nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
3735 /* XXX can we do anything useful with the wcc info? */
3736 }
3737 if (!error) {
3738 nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF);
3739 if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl,
3740 NFSX_V3WRITEVERF)) {
3741 bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
3742 NFSX_V3WRITEVERF);
3743 error = NFSERR_STALEWRITEVERF;
3744 }
3745 }
3746 nfsm_reqdone;
3747 return (error);
3748 }
3749
3750 static int
3751 nfs_blockmap(
3752 __unused struct vnop_blockmap_args /* {
3753 struct vnodeop_desc *a_desc;
3754 vnode_t a_vp;
3755 off_t a_foffset;
3756 size_t a_size;
3757 daddr64_t *a_bpn;
3758 size_t *a_run;
3759 void *a_poff;
3760 int a_flags;
3761 } */ *ap)
3762 {
3763 return (ENOTSUP);
3764 }
3765
3766 /*
3767 * Mmap a file
3768 *
3769 * NB Currently unsupported.
3770 */
3771 /*ARGSUSED*/
3772 static int
3773 nfs_mmap(
3774 __unused struct vnop_mmap_args /* {
3775 struct vnodeop_desc *a_desc;
3776 vnode_t a_vp;
3777 int a_fflags;
3778 kauth_cred_t a_cred;
3779 proc_t a_p;
3780 } */ *ap)
3781 {
3782
3783 return (EINVAL);
3784 }
3785
3786 /*
3787 * fsync vnode op. Just call nfs_flush() with commit == 1.
3788 */
3789 /* ARGSUSED */
3790 static int
3791 nfs_fsync(ap)
3792 struct vnop_fsync_args /* {
3793 struct vnodeop_desc *a_desc;
3794 vnode_t a_vp;
3795 int a_waitfor;
3796 vfs_context_t a_context;
3797 } */ *ap;
3798 {
3799 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
3800 proc_t p = vfs_context_proc(ap->a_context);
3801 struct nfsnode *np = VTONFS(ap->a_vp);
3802 int error;
3803
3804 np->n_flag |= NWRBUSY;
3805 error = nfs_flush(ap->a_vp, ap->a_waitfor, cred, p, 0);
3806 np->n_flag &= ~NWRBUSY;
3807 return (error);
3808 }
3809
3810 int
3811 nfs_flushcommits(vnode_t vp, proc_t p, int nowait)
3812 {
3813 struct nfsnode *np = VTONFS(vp);
3814 struct nfsbuf *bp;
3815 struct nfsbuflists blist, commitlist;
3816 int error = 0, retv, wcred_set, flags;
3817 u_quad_t off, endoff, toff;
3818 u_int32_t count;
3819 kauth_cred_t wcred = NULL;
3820
3821 FSDBG_TOP(557, vp, np, 0, 0);
3822
3823 /*
3824 * A nb_flags == (NB_DELWRI | NB_NEEDCOMMIT) block has been written to the
3825 * server, but nas not been committed to stable storage on the server
3826 * yet. The byte range is worked out for as many nfsbufs as we can handle
3827 * and the commit rpc is done.
3828 */
3829 if (!LIST_EMPTY(&np->n_dirtyblkhd))
3830 np->n_flag |= NMODIFIED;
3831
3832 off = (u_quad_t)-1;
3833 endoff = 0;
3834 wcred_set = 0;
3835 LIST_INIT(&commitlist);
3836
3837 if (!VFSTONFS(vnode_mount(vp))) {
3838 error = ENXIO;
3839 goto done;
3840 }
3841 if (!NFS_ISV3(vp)) {
3842 error = EINVAL;
3843 goto done;
3844 }
3845
3846 flags = NBI_DIRTY;
3847 if (nowait)
3848 flags |= NBI_NOWAIT;
3849 lck_mtx_lock(nfs_buf_mutex);
3850 if (!nfs_buf_iterprepare(np, &blist, flags)) {
3851 while ((bp = LIST_FIRST(&blist))) {
3852 LIST_REMOVE(bp, nb_vnbufs);
3853 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
3854 error = nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0);
3855 if (error)
3856 continue;
3857 if (((bp->nb_flags & (NB_DELWRI | NB_NEEDCOMMIT))
3858 != (NB_DELWRI | NB_NEEDCOMMIT))) {
3859 nfs_buf_drop(bp);
3860 continue;
3861 }
3862 nfs_buf_remfree(bp);
3863 lck_mtx_unlock(nfs_buf_mutex);
3864 /*
3865 * we need a upl to see if the page has been
3866 * dirtied (think mmap) since the unstable write, and
3867 * also to prevent vm from paging it during our commit rpc
3868 */
3869 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
3870 retv = nfs_buf_upl_setup(bp);
3871 if (retv) {
3872 /* unable to create upl */
3873 /* vm object must no longer exist */
3874 /* this could be fatal if we need */
3875 /* to write the data again, we'll see... */
3876 printf("nfs_flushcommits: upl create failed %d\n", retv);
3877 bp->nb_valid = bp->nb_dirty = 0;
3878 }
3879 }
3880 nfs_buf_upl_check(bp);
3881 lck_mtx_lock(nfs_buf_mutex);
3882
3883 FSDBG(557, bp, bp->nb_flags, bp->nb_valid, bp->nb_dirty);
3884 FSDBG(557, bp->nb_validoff, bp->nb_validend,
3885 bp->nb_dirtyoff, bp->nb_dirtyend);
3886
3887 /*
3888 * We used to check for dirty pages here; if there were any
3889 * we'd abort the commit and force the entire buffer to be
3890 * written again.
3891 *
3892 * Instead of doing that, we now go ahead and commit the dirty
3893 * range, and then leave the buffer around with dirty pages
3894 * that will be written out later.
3895 */
3896
3897 /*
3898 * Work out if all buffers are using the same cred
3899 * so we can deal with them all with one commit.
3900 *
3901 * XXX creds in bp's must be obtained by kauth_cred_ref on
3902 * the same original cred in order for them to be equal.
3903 */
3904 if (wcred_set == 0) {
3905 wcred = bp->nb_wcred;
3906 if (wcred == NOCRED)
3907 panic("nfs: needcommit w/out wcred");
3908 wcred_set = 1;
3909 } else if ((wcred_set == 1) && wcred != bp->nb_wcred) {
3910 wcred_set = -1;
3911 }
3912 SET(bp->nb_flags, NB_WRITEINPROG);
3913
3914 /*
3915 * A list of these buffers is kept so that the
3916 * second loop knows which buffers have actually
3917 * been committed. This is necessary, since there
3918 * may be a race between the commit rpc and new
3919 * uncommitted writes on the file.
3920 */
3921 LIST_REMOVE(bp, nb_vnbufs);
3922 LIST_INSERT_HEAD(&commitlist, bp, nb_vnbufs);
3923 toff = NBOFF(bp) + bp->nb_dirtyoff;
3924 if (toff < off)
3925 off = toff;
3926 toff += (u_quad_t)(bp->nb_dirtyend - bp->nb_dirtyoff);
3927 if (toff > endoff)
3928 endoff = toff;
3929 }
3930 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
3931 }
3932 lck_mtx_unlock(nfs_buf_mutex);
3933
3934 if (LIST_EMPTY(&commitlist)) {
3935 error = ENOBUFS;
3936 goto done;
3937 }
3938
3939 /*
3940 * Commit data on the server, as required.
3941 * If all bufs are using the same wcred, then use that with
3942 * one call for all of them, otherwise commit each one
3943 * separately.
3944 */
3945 if (wcred_set == 1) {
3946 /*
3947 * Note, it's possible the commit range could be >2^32-1.
3948 * If it is, we'll send one commit that covers the whole file.
3949 */
3950 if ((endoff - off) > 0xffffffff)
3951 count = 0;
3952 else
3953 count = (endoff - off);
3954 retv = nfs_commit(vp, off, count, wcred, p);
3955 } else {
3956 retv = 0;
3957 LIST_FOREACH(bp, &commitlist, nb_vnbufs) {
3958 toff = NBOFF(bp) + bp->nb_dirtyoff;
3959 count = bp->nb_dirtyend - bp->nb_dirtyoff;
3960 retv = nfs_commit(vp, toff, count, bp->nb_wcred, p);
3961 if (retv)
3962 break;
3963 }
3964 }
3965 if (retv == NFSERR_STALEWRITEVERF)
3966 nfs_clearcommit(vnode_mount(vp));
3967
3968 /*
3969 * Now, either mark the blocks I/O done or mark the
3970 * blocks dirty, depending on whether the commit
3971 * succeeded.
3972 */
3973 while ((bp = LIST_FIRST(&commitlist))) {
3974 LIST_REMOVE(bp, nb_vnbufs);
3975 FSDBG(557, bp, retv, bp->nb_flags, bp->nb_dirty);
3976 CLR(bp->nb_flags, (NB_NEEDCOMMIT | NB_WRITEINPROG));
3977 np->n_needcommitcnt--;
3978 CHECK_NEEDCOMMITCNT(np);
3979
3980 if (retv) {
3981 /* move back to dirty list */
3982 lck_mtx_lock(nfs_buf_mutex);
3983 LIST_INSERT_HEAD(&VTONFS(vp)->n_dirtyblkhd, bp, nb_vnbufs);
3984 lck_mtx_unlock(nfs_buf_mutex);
3985 nfs_buf_release(bp, 1);
3986 continue;
3987 }
3988
3989 vnode_startwrite(vp);
3990 if (ISSET(bp->nb_flags, NB_DELWRI)) {
3991 OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
3992 NFSBUFCNTCHK(0);
3993 wakeup(&nfs_nbdwrite);
3994 }
3995 CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
3996 /* if block still has dirty pages, we don't want it to */
3997 /* be released in nfs_buf_iodone(). So, don't set NB_ASYNC. */
3998 if (!bp->nb_dirty)
3999 SET(bp->nb_flags, NB_ASYNC);
4000
4001 /* move to clean list */
4002 lck_mtx_lock(nfs_buf_mutex);
4003 LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
4004 lck_mtx_unlock(nfs_buf_mutex);
4005
4006 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
4007
4008 nfs_buf_iodone(bp);
4009 if (bp->nb_dirty) {
4010 /* throw it back in as a delayed write buffer */
4011 CLR(bp->nb_flags, NB_DONE);
4012 nfs_buf_write_delayed(bp, p);
4013 }
4014 }
4015
4016 done:
4017 FSDBG_BOT(557, vp, np, 0, error);
4018 return (error);
4019 }
4020
4021 /*
4022 * Flush all the blocks associated with a vnode.
4023 * Walk through the buffer pool and push any dirty pages
4024 * associated with the vnode.
4025 */
4026 int
4027 nfs_flush(
4028 vnode_t vp,
4029 int waitfor,
4030 __unused kauth_cred_t cred,
4031 proc_t p,
4032 int ignore_writeerr)
4033 {
4034 struct nfsnode *np = VTONFS(vp);
4035 struct nfsbuf *bp;
4036 struct nfsbuflists blist;
4037 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4038 int error = 0, error2, slptimeo = 0, slpflag = 0;
4039 int flags, passone = 1;
4040
4041 FSDBG_TOP(517, vp, np, waitfor, 0);
4042
4043 if (!nmp) {
4044 error = ENXIO;
4045 goto done;
4046 }
4047 if (nmp->nm_flag & NFSMNT_INT)
4048 slpflag = PCATCH;
4049
4050 /*
4051 * On the first pass, start async/unstable writes on all
4052 * delayed write buffers. Then wait for all writes to complete
4053 * and call nfs_flushcommits() to commit any uncommitted buffers.
4054 * On all subsequent passes, start STABLE writes on any remaining
4055 * dirty buffers. Then wait for all writes to complete.
4056 */
4057 again:
4058 lck_mtx_lock(nfs_buf_mutex);
4059 FSDBG(518, LIST_FIRST(&np->n_dirtyblkhd), np->n_flag, 0, 0);
4060 if (!LIST_EMPTY(&np->n_dirtyblkhd))
4061 np->n_flag |= NMODIFIED;
4062 if (!VFSTONFS(vnode_mount(vp))) {
4063 lck_mtx_unlock(nfs_buf_mutex);
4064 error = ENXIO;
4065 goto done;
4066 }
4067
4068 /* Start/do any write(s) that are required. */
4069 if (!nfs_buf_iterprepare(np, &blist, NBI_DIRTY)) {
4070 while ((bp = LIST_FIRST(&blist))) {
4071 LIST_REMOVE(bp, nb_vnbufs);
4072 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
4073 flags = (passone || (waitfor != MNT_WAIT)) ? NBAC_NOWAIT : 0;
4074 if (flags != NBAC_NOWAIT)
4075 nfs_buf_refget(bp);
4076 while ((error = nfs_buf_acquire(bp, flags, slpflag, slptimeo))) {
4077 FSDBG(524, bp, flags, bp->nb_lflags, bp->nb_flags);
4078 if (error == EBUSY)
4079 break;
4080 if (error) {
4081 error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
4082 if (error2) {
4083 if (flags != NBAC_NOWAIT)
4084 nfs_buf_refrele(bp);
4085 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
4086 lck_mtx_unlock(nfs_buf_mutex);
4087 error = error2;
4088 goto done;
4089 }
4090 if (slpflag == PCATCH) {
4091 slpflag = 0;
4092 slptimeo = 2 * hz;
4093 }
4094 }
4095 }
4096 if (flags != NBAC_NOWAIT)
4097 nfs_buf_refrele(bp);
4098 if (error == EBUSY)
4099 continue;
4100 if (!bp->nb_vp) {
4101 /* buffer is no longer valid */
4102 nfs_buf_drop(bp);
4103 continue;
4104 }
4105 if (!ISSET(bp->nb_flags, NB_DELWRI))
4106 panic("nfs_flush: not dirty");
4107 FSDBG(525, bp, passone, bp->nb_lflags, bp->nb_flags);
4108 if ((passone || (waitfor != MNT_WAIT)) &&
4109 ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
4110 nfs_buf_drop(bp);
4111 continue;
4112 }
4113 nfs_buf_remfree(bp);
4114 lck_mtx_unlock(nfs_buf_mutex);
4115 if (ISSET(bp->nb_flags, NB_ERROR)) {
4116 np->n_error = bp->nb_error ? bp->nb_error : EIO;
4117 np->n_flag |= NWRITEERR;
4118 nfs_buf_release(bp, 1);
4119 lck_mtx_lock(nfs_buf_mutex);
4120 continue;
4121 }
4122 SET(bp->nb_flags, NB_ASYNC);
4123 if (!passone) {
4124 /* NB_STABLE forces this to be written FILESYNC */
4125 SET(bp->nb_flags, NB_STABLE);
4126 }
4127 nfs_buf_write(bp);
4128 lck_mtx_lock(nfs_buf_mutex);
4129 }
4130 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
4131 }
4132 lck_mtx_unlock(nfs_buf_mutex);
4133
4134 if (waitfor == MNT_WAIT) {
4135 while ((error = vnode_waitforwrites(vp, 0, slpflag, slptimeo, "nfsflush"))) {
4136 error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
4137 if (error2) {
4138 error = error2;
4139 goto done;
4140 }
4141 if (slpflag == PCATCH) {
4142 slpflag = 0;
4143 slptimeo = 2 * hz;
4144 }
4145 }
4146 }
4147
4148 if (NFS_ISV3(vp)) {
4149 /* loop while it looks like there are still buffers to be */
4150 /* commited and nfs_flushcommits() seems to be handling them. */
4151 while (np->n_needcommitcnt)
4152 if (nfs_flushcommits(vp, p, 0))
4153 break;
4154 }
4155
4156 if (passone) {
4157 passone = 0;
4158 goto again;
4159 }
4160
4161 if (waitfor == MNT_WAIT) {
4162 if (!LIST_EMPTY(&np->n_dirtyblkhd))
4163 goto again;
4164 /* if we have no dirty blocks, we can clear the modified flag */
4165 np->n_flag &= ~NMODIFIED;
4166 }
4167
4168 FSDBG(526, np->n_flag, np->n_error, 0, 0);
4169 if (!ignore_writeerr && (np->n_flag & NWRITEERR)) {
4170 error = np->n_error;
4171 np->n_flag &= ~NWRITEERR;
4172 }
4173 done:
4174 FSDBG_BOT(517, vp, np, error, 0);
4175 return (error);
4176 }
4177
4178 /*
4179 * Do an nfs pathconf rpc.
4180 */
4181 int
4182 nfs_pathconfrpc(
4183 vnode_t vp,
4184 struct nfsv3_pathconf *pc,
4185 kauth_cred_t cred,
4186 proc_t procp)
4187 {
4188 mbuf_t mreq, mrep, md, mb, mb2;
4189 caddr_t bpos, dpos, cp, cp2;
4190 int32_t t1, t2;
4191 u_long *tl;
4192 u_int64_t xid;
4193 int attrflag, error = 0;
4194 struct nfsv3_pathconf *mpc;
4195
4196 /* fetch pathconf info from server */
4197 nfsm_reqhead(NFSX_FH(1));
4198 if (error)
4199 return (error);
4200 nfsm_fhtom(vp, 1);
4201 nfsm_request(vp, NFSPROC_PATHCONF, procp, cred, &xid);
4202 nfsm_postop_attr_update(vp, 1, attrflag, &xid);
4203 if (!error) {
4204 nfsm_dissect(mpc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
4205 pc->pc_linkmax = fxdr_unsigned(long, mpc->pc_linkmax);
4206 pc->pc_namemax = fxdr_unsigned(long, mpc->pc_namemax);
4207 pc->pc_chownrestricted = fxdr_unsigned(long, mpc->pc_chownrestricted);
4208 pc->pc_notrunc = fxdr_unsigned(long, mpc->pc_notrunc);
4209 pc->pc_caseinsensitive = fxdr_unsigned(long, mpc->pc_caseinsensitive);
4210 pc->pc_casepreserving = fxdr_unsigned(long, mpc->pc_casepreserving);
4211 }
4212 nfsm_reqdone;
4213
4214 return (error);
4215 }
4216
4217 void
4218 nfs_pathconf_cache(struct nfsmount *nmp, struct nfsv3_pathconf *pc)
4219 {
4220 nmp->nm_state |= NFSSTA_GOTPATHCONF;
4221 nmp->nm_fsinfo.linkmax = pc->pc_linkmax;
4222 nmp->nm_fsinfo.namemax = pc->pc_namemax;
4223 nmp->nm_fsinfo.pcflags = 0;
4224 if (pc->pc_notrunc)
4225 nmp->nm_fsinfo.pcflags |= NFSPCINFO_NOTRUNC;
4226 if (pc->pc_chownrestricted)
4227 nmp->nm_fsinfo.pcflags |= NFSPCINFO_CHOWN_RESTRICTED;
4228 if (pc->pc_caseinsensitive)
4229 nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_INSENSITIVE;
4230 if (pc->pc_casepreserving)
4231 nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_PRESERVING;
4232 }
4233
4234 /*
4235 * Return POSIX pathconf information applicable to nfs.
4236 *
4237 * The NFS V2 protocol doesn't support this, so just return EINVAL
4238 * for V2.
4239 */
4240 /* ARGSUSED */
4241 static int
4242 nfs_pathconf(ap)
4243 struct vnop_pathconf_args /* {
4244 struct vnodeop_desc *a_desc;
4245 vnode_t a_vp;
4246 int a_name;
4247 register_t *a_retval;
4248 vfs_context_t a_context;
4249 } */ *ap;
4250 {
4251 vnode_t vp = ap->a_vp;
4252 struct nfsmount *nmp;
4253 struct nfsv3_pathconf pc;
4254 int error = 0, cached;
4255
4256 nmp = VFSTONFS(vnode_mount(vp));
4257 if (!nmp)
4258 return (ENXIO);
4259 if (!NFS_ISV3(vp))
4260 return (EINVAL);
4261
4262 switch (ap->a_name) {
4263 case _PC_LINK_MAX:
4264 case _PC_NAME_MAX:
4265 case _PC_CHOWN_RESTRICTED:
4266 case _PC_NO_TRUNC:
4267 case _PC_CASE_SENSITIVE:
4268 case _PC_CASE_PRESERVING:
4269 break;
4270 default:
4271 /* don't bother contacting the server if we know the answer */
4272 return (EINVAL);
4273 }
4274
4275 if (!(nmp->nm_state & NFSSTA_GOTPATHCONF)) {
4276 /* no pathconf info cached */
4277 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
4278 proc_t p = vfs_context_proc(ap->a_context);
4279 error = nfs_pathconfrpc(vp, &pc, cred, p);
4280 if (error)
4281 return (error);
4282 nmp = VFSTONFS(vnode_mount(vp));
4283 if (!nmp)
4284 return (ENXIO);
4285 if (!(nmp->nm_state & NFSSTA_GOTFSINFO)) {
4286 nfs_fsinfo(nmp, vp, cred, p);
4287 nmp = VFSTONFS(vnode_mount(vp));
4288 if (!nmp)
4289 return (ENXIO);
4290 }
4291 if ((nmp->nm_state & NFSSTA_GOTFSINFO) &&
4292 (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS)) {
4293 /* all files have the same pathconf info, */
4294 /* so cache a copy of the results */
4295 nfs_pathconf_cache(nmp, &pc);
4296 }
4297 }
4298
4299 cached = (nmp->nm_state & NFSSTA_GOTPATHCONF);
4300
4301 switch (ap->a_name) {
4302 case _PC_LINK_MAX:
4303 *ap->a_retval = cached ? nmp->nm_fsinfo.linkmax : pc.pc_linkmax;
4304 break;
4305 case _PC_NAME_MAX:
4306 *ap->a_retval = cached ? nmp->nm_fsinfo.namemax : pc.pc_namemax;
4307 break;
4308 case _PC_CHOWN_RESTRICTED:
4309 if (cached)
4310 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CHOWN_RESTRICTED) ? 1 : 0;
4311 else
4312 *ap->a_retval = pc.pc_chownrestricted;
4313 break;
4314 case _PC_NO_TRUNC:
4315 if (cached)
4316 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_NOTRUNC) ? 1 : 0;
4317 else
4318 *ap->a_retval = pc.pc_notrunc;
4319 break;
4320 case _PC_CASE_SENSITIVE:
4321 if (cached)
4322 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_INSENSITIVE) ? 0 : 1;
4323 else
4324 *ap->a_retval = !pc.pc_caseinsensitive;
4325 break;
4326 case _PC_CASE_PRESERVING:
4327 if (cached)
4328 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_PRESERVING) ? 1 : 0;
4329 else
4330 *ap->a_retval = pc.pc_casepreserving;
4331 break;
4332 default:
4333 error = EINVAL;
4334 }
4335
4336 return (error);
4337 }
4338
4339 /*
4340 * NFS advisory byte-level locks (client)
4341 */
4342 static int
4343 nfs_advlock(ap)
4344 struct vnop_advlock_args /* {
4345 struct vnodeop_desc *a_desc;
4346 vnode_t a_vp;
4347 caddr_t a_id;
4348 int a_op;
4349 struct flock *a_fl;
4350 int a_flags;
4351 vfs_context_t a_context;
4352 } */ *ap;
4353 {
4354 return (nfs_dolock(ap));
4355 }
4356
4357 /*
4358 * write (or commit) the given NFS buffer
4359 */
4360 int
4361 nfs_buf_write(struct nfsbuf *bp)
4362 {
4363 int oldflags = bp->nb_flags, rv = 0;
4364 vnode_t vp = bp->nb_vp;
4365 struct nfsnode *np = VTONFS(vp);
4366 kauth_cred_t cr;
4367 proc_t p = current_proc(); // XXX
4368
4369 FSDBG_TOP(553, bp, NBOFF(bp), bp->nb_flags, 0);
4370
4371 if (!ISSET(bp->nb_lflags, NBL_BUSY))
4372 panic("nfs_buf_write: buffer is not busy???");
4373
4374 CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
4375 if (ISSET(oldflags, NB_DELWRI)) {
4376 OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
4377 NFSBUFCNTCHK(0);
4378 wakeup(&nfs_nbdwrite);
4379 }
4380
4381 /* move to clean list */
4382 if (ISSET(oldflags, (NB_ASYNC|NB_DELWRI))) {
4383 lck_mtx_lock(nfs_buf_mutex);
4384 if (bp->nb_vnbufs.le_next != NFSNOLIST)
4385 LIST_REMOVE(bp, nb_vnbufs);
4386 LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
4387 lck_mtx_unlock(nfs_buf_mutex);
4388 }
4389 vnode_startwrite(vp);
4390
4391 if (p && p->p_stats)
4392 p->p_stats->p_ru.ru_oublock++;
4393
4394 /*
4395 * For async requests when nfsiod(s) are running, queue the request by
4396 * calling nfs_asyncio(), otherwise just all nfs_doio() to do the request.
4397 */
4398 if (ISSET(bp->nb_flags, NB_ASYNC))
4399 p = NULL;
4400 if (ISSET(bp->nb_flags, NB_READ))
4401 cr = bp->nb_rcred;
4402 else
4403 cr = bp->nb_wcred;
4404 if (!ISSET(bp->nb_flags, NB_ASYNC) || nfs_asyncio(bp, NOCRED))
4405 rv = nfs_doio(bp, cr, p);
4406
4407 if ((oldflags & NB_ASYNC) == 0) {
4408 rv = nfs_buf_iowait(bp);
4409 /* move to clean list */
4410 if (oldflags & NB_DELWRI) {
4411 lck_mtx_lock(nfs_buf_mutex);
4412 if (bp->nb_vnbufs.le_next != NFSNOLIST)
4413 LIST_REMOVE(bp, nb_vnbufs);
4414 LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
4415 lck_mtx_unlock(nfs_buf_mutex);
4416 }
4417 oldflags = bp->nb_flags;
4418 FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
4419 if (cr) {
4420 kauth_cred_ref(cr);
4421 }
4422 nfs_buf_release(bp, 1);
4423 if (ISSET(oldflags, NB_ERROR) && !(np->n_flag & NFLUSHINPROG)) {
4424 /*
4425 * There was a write error and we need to
4426 * invalidate attrs and flush buffers in
4427 * order to sync up with the server.
4428 * (if this write was extending the file,
4429 * we may no longer know the correct size)
4430 *
4431 * But we couldn't call vinvalbuf while holding
4432 * the buffer busy. So we call vinvalbuf() after
4433 * releasing the buffer.
4434 */
4435 nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cr, p, 1);
4436 }
4437 if (cr)
4438 kauth_cred_rele(cr);
4439 return (rv);
4440 }
4441
4442 FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
4443 return (rv);
4444 }
4445
4446 /*
4447 * Read wrapper for special devices.
4448 */
4449 static int
4450 nfsspec_read(ap)
4451 struct vnop_read_args /* {
4452 struct vnodeop_desc *a_desc;
4453 vnode_t a_vp;
4454 struct uio *a_uio;
4455 int a_ioflag;
4456 vfs_context_t a_context;
4457 } */ *ap;
4458 {
4459 register struct nfsnode *np = VTONFS(ap->a_vp);
4460 struct timeval now;
4461
4462 /*
4463 * Set access flag.
4464 */
4465 np->n_flag |= NACC;
4466 microtime(&now);
4467 np->n_atim.tv_sec = now.tv_sec;
4468 np->n_atim.tv_nsec = now.tv_usec * 1000;
4469 return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap));
4470 }
4471
4472 /*
4473 * Write wrapper for special devices.
4474 */
4475 static int
4476 nfsspec_write(ap)
4477 struct vnop_write_args /* {
4478 struct vnodeop_desc *a_desc;
4479 vnode_t a_vp;
4480 struct uio *a_uio;
4481 int a_ioflag;
4482 vfs_context_t a_context;
4483 } */ *ap;
4484 {
4485 register struct nfsnode *np = VTONFS(ap->a_vp);
4486 struct timeval now;
4487
4488 /*
4489 * Set update flag.
4490 */
4491 np->n_flag |= NUPD;
4492 microtime(&now);
4493 np->n_mtim.tv_sec = now.tv_sec;
4494 np->n_mtim.tv_nsec = now.tv_usec * 1000;
4495 return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap));
4496 }
4497
4498 /*
4499 * Close wrapper for special devices.
4500 *
4501 * Update the times on the nfsnode then do device close.
4502 */
4503 static int
4504 nfsspec_close(ap)
4505 struct vnop_close_args /* {
4506 struct vnodeop_desc *a_desc;
4507 vnode_t a_vp;
4508 int a_fflag;
4509 vfs_context_t a_context;
4510 } */ *ap;
4511 {
4512 vnode_t vp = ap->a_vp;
4513 struct nfsnode *np = VTONFS(vp);
4514 struct vnode_attr vattr;
4515 mount_t mp;
4516
4517 if (np->n_flag & (NACC | NUPD)) {
4518 np->n_flag |= NCHG;
4519 if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
4520 VATTR_INIT(&vattr);
4521 if (np->n_flag & NACC) {
4522 vattr.va_access_time = np->n_atim;
4523 VATTR_SET_ACTIVE(&vattr, va_access_time);
4524 }
4525 if (np->n_flag & NUPD) {
4526 vattr.va_modify_time = np->n_mtim;
4527 VATTR_SET_ACTIVE(&vattr, va_modify_time);
4528 }
4529 vnode_setattr(vp, &vattr, ap->a_context);
4530 }
4531 }
4532 return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap));
4533 }
4534
4535 extern vnop_t **fifo_vnodeop_p;
4536
4537 /*
4538 * Read wrapper for fifos.
4539 */
4540 static int
4541 nfsfifo_read(ap)
4542 struct vnop_read_args /* {
4543 struct vnodeop_desc *a_desc;
4544 vnode_t a_vp;
4545 struct uio *a_uio;
4546 int a_ioflag;
4547 vfs_context_t a_context;
4548 } */ *ap;
4549 {
4550 register struct nfsnode *np = VTONFS(ap->a_vp);
4551 struct timeval now;
4552
4553 /*
4554 * Set access flag.
4555 */
4556 np->n_flag |= NACC;
4557 microtime(&now);
4558 np->n_atim.tv_sec = now.tv_sec;
4559 np->n_atim.tv_nsec = now.tv_usec * 1000;
4560 return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_read), ap));
4561 }
4562
4563 /*
4564 * Write wrapper for fifos.
4565 */
4566 static int
4567 nfsfifo_write(ap)
4568 struct vnop_write_args /* {
4569 struct vnodeop_desc *a_desc;
4570 vnode_t a_vp;
4571 struct uio *a_uio;
4572 int a_ioflag;
4573 vfs_context_t a_context;
4574 } */ *ap;
4575 {
4576 register struct nfsnode *np = VTONFS(ap->a_vp);
4577 struct timeval now;
4578
4579 /*
4580 * Set update flag.
4581 */
4582 np->n_flag |= NUPD;
4583 microtime(&now);
4584 np->n_mtim.tv_sec = now.tv_sec;
4585 np->n_mtim.tv_nsec = now.tv_usec * 1000;
4586 return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_write), ap));
4587 }
4588
4589 /*
4590 * Close wrapper for fifos.
4591 *
4592 * Update the times on the nfsnode then do fifo close.
4593 */
4594 static int
4595 nfsfifo_close(ap)
4596 struct vnop_close_args /* {
4597 struct vnodeop_desc *a_desc;
4598 vnode_t a_vp;
4599 int a_fflag;
4600 vfs_context_t a_context;
4601 } */ *ap;
4602 {
4603 vnode_t vp = ap->a_vp;
4604 struct nfsnode *np = VTONFS(vp);
4605 struct vnode_attr vattr;
4606 struct timeval now;
4607 mount_t mp;
4608
4609 if (np->n_flag & (NACC | NUPD)) {
4610 microtime(&now);
4611 if (np->n_flag & NACC) {
4612 np->n_atim.tv_sec = now.tv_sec;
4613 np->n_atim.tv_nsec = now.tv_usec * 1000;
4614 }
4615 if (np->n_flag & NUPD) {
4616 np->n_mtim.tv_sec = now.tv_sec;
4617 np->n_mtim.tv_nsec = now.tv_usec * 1000;
4618 }
4619 np->n_flag |= NCHG;
4620 if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
4621 VATTR_INIT(&vattr);
4622 if (np->n_flag & NACC) {
4623 vattr.va_access_time = np->n_atim;
4624 VATTR_SET_ACTIVE(&vattr, va_access_time);
4625 }
4626 if (np->n_flag & NUPD) {
4627 vattr.va_modify_time = np->n_mtim;
4628 VATTR_SET_ACTIVE(&vattr, va_modify_time);
4629 }
4630 vnode_setattr(vp, &vattr, ap->a_context);
4631 }
4632 }
4633 return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_close), ap));
4634 }
4635
4636 /*ARGSUSED*/
4637 static int
4638 nfs_ioctl(
4639 __unused struct vnop_ioctl_args /* {
4640 struct vnodeop_desc *a_desc;
4641 vnode_t a_vp;
4642 u_long a_command;
4643 caddr_t a_data;
4644 int a_fflag;
4645 kauth_cred_t a_cred;
4646 proc_t a_p;
4647 } */ *ap)
4648 {
4649
4650 /*
4651 * XXX we were once bogusly enoictl() which returned this (ENOTTY).
4652 * Probably we should return ENODEV.
4653 */
4654 return (ENOTTY);
4655 }
4656
4657 /*ARGSUSED*/
4658 static int
4659 nfs_select(
4660 __unused struct vnop_select_args /* {
4661 struct vnodeop_desc *a_desc;
4662 vnode_t a_vp;
4663 int a_which;
4664 int a_fflags;
4665 kauth_cred_t a_cred;
4666 void *a_wql;
4667 proc_t a_p;
4668 } */ *ap)
4669 {
4670
4671 /*
4672 * We were once bogusly seltrue() which returns 1. Is this right?
4673 */
4674 return (1);
4675 }
4676
4677 /*
4678 * Vnode op for pagein using getblk_pages
4679 * derived from nfs_bioread()
4680 * No read aheads are started from pagein operation
4681 */
4682 static int
4683 nfs_pagein(ap)
4684 struct vnop_pagein_args /* {
4685 struct vnodeop_desc *a_desc;
4686 vnode_t a_vp;
4687 upl_t a_pl;
4688 vm_offset_t a_pl_offset;
4689 off_t a_f_offset;
4690 size_t a_size;
4691 int a_flags;
4692 vfs_context_t a_context;
4693 } */ *ap;
4694 {
4695 vnode_t vp = ap->a_vp;
4696 upl_t pl = ap->a_pl;
4697 size_t size= ap->a_size;
4698 off_t f_offset = ap->a_f_offset;
4699 vm_offset_t pl_offset = ap->a_pl_offset;
4700 int flags = ap->a_flags;
4701 kauth_cred_t cred;
4702 proc_t p;
4703 struct nfsnode *np = VTONFS(vp);
4704 int biosize, xsize, iosize;
4705 struct nfsmount *nmp;
4706 int error = 0;
4707 vm_offset_t ioaddr;
4708 struct uio auio;
4709 struct iovec_32 aiov;
4710 struct uio * uio = &auio;
4711 int nofreeupl = flags & UPL_NOCOMMIT;
4712 upl_page_info_t *plinfo;
4713
4714 FSDBG(322, vp, f_offset, size, flags);
4715 if (pl == (upl_t)NULL)
4716 panic("nfs_pagein: no upl");
4717
4718 if (UBCINVALID(vp)) {
4719 printf("nfs_pagein: invalid vnode 0x%x", (int)vp);
4720 if (!nofreeupl)
4721 (void) ubc_upl_abort(pl, 0);
4722 return (EPERM);
4723 }
4724 UBCINFOCHECK("nfs_pagein", vp);
4725
4726 if (size <= 0) {
4727 printf("nfs_pagein: invalid size %d", size);
4728 if (!nofreeupl)
4729 (void) ubc_upl_abort(pl, 0);
4730 return (EINVAL);
4731 }
4732 if (f_offset < 0 || f_offset >= (off_t)np->n_size || (f_offset & PAGE_MASK_64)) {
4733 if (!nofreeupl)
4734 ubc_upl_abort_range(pl, pl_offset, size,
4735 UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
4736 return (EINVAL);
4737 }
4738
4739 cred = ubc_getcred(vp);
4740 if (cred == NOCRED)
4741 cred = vfs_context_ucred(ap->a_context);
4742 p = vfs_context_proc(ap->a_context);
4743
4744 auio.uio_offset = f_offset;
4745 #if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
4746 auio.uio_segflg = UIO_SYSSPACE;
4747 #else
4748 auio.uio_segflg = UIO_SYSSPACE32;
4749 #endif
4750 auio.uio_rw = UIO_READ;
4751 auio.uio_procp = p;
4752
4753 nmp = VFSTONFS(vnode_mount(vp));
4754 if (!nmp) {
4755 if (!nofreeupl)
4756 ubc_upl_abort_range(pl, pl_offset, size,
4757 UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
4758 return (ENXIO);
4759 }
4760 biosize = nmp->nm_biosize;
4761 if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
4762 nfs_fsinfo(nmp, vp, cred, p);
4763
4764 plinfo = ubc_upl_pageinfo(pl);
4765 ubc_upl_map(pl, &ioaddr);
4766 ioaddr += pl_offset;
4767 xsize = size;
4768
4769 do {
4770 /*
4771 * It would be nice to be able to issue all these requests
4772 * in parallel instead of waiting for each one to complete
4773 * before sending the next one.
4774 * XXX Should we align these requests to block boundaries?
4775 */
4776 iosize = min(biosize, xsize);
4777 aiov.iov_len = iosize;
4778 aiov.iov_base = (uintptr_t)ioaddr;
4779 auio.uio_iovs.iov32p = &aiov;
4780 auio.uio_iovcnt = 1;
4781 uio_uio_resid_set(&auio, iosize);
4782
4783 FSDBG(322, uio->uio_offset, uio_uio_resid(uio), ioaddr, xsize);
4784 /*
4785 * With UBC we get here only when the file data is not in the VM
4786 * page cache, so go ahead and read in.
4787 */
4788 #ifdef UPL_DEBUG
4789 upl_ubc_alias_set(pl, current_thread(), 2);
4790 #endif /* UPL_DEBUG */
4791 OSAddAtomic(1, (SInt32*)&nfsstats.pageins);
4792
4793 error = nfs_readrpc(vp, uio, cred, p);
4794
4795 if (!error) {
4796 if (uio_uio_resid(uio)) {
4797 /*
4798 * If uio_resid > 0, there is a hole in the file
4799 * and no writes after the hole have been pushed
4800 * to the server yet... or we're at the EOF
4801 * Just zero fill the rest of the valid area.
4802 */
4803 // LP64todo - fix this
4804 int zcnt = uio_uio_resid(uio);
4805 int zoff = iosize - zcnt;
4806 bzero((char *)ioaddr + zoff, zcnt);
4807
4808 FSDBG(324, uio->uio_offset, zoff, zcnt, ioaddr);
4809 uio->uio_offset += zcnt;
4810 }
4811 ioaddr += iosize;
4812 xsize -= iosize;
4813 } else {
4814 FSDBG(322, uio->uio_offset, uio_uio_resid(uio), error, -1);
4815 }
4816
4817 nmp = VFSTONFS(vnode_mount(vp));
4818 } while (error == 0 && xsize > 0);
4819
4820 ubc_upl_unmap(pl);
4821
4822 if (!nofreeupl) {
4823 if (error)
4824 ubc_upl_abort_range(pl, pl_offset, size,
4825 UPL_ABORT_ERROR |
4826 UPL_ABORT_FREE_ON_EMPTY);
4827 else
4828 ubc_upl_commit_range(pl, pl_offset, size,
4829 UPL_COMMIT_CLEAR_DIRTY |
4830 UPL_COMMIT_FREE_ON_EMPTY);
4831 }
4832 return (error);
4833 }
4834
4835
4836 /*
4837 * Vnode op for pageout using UPL
4838 * Derived from nfs_write()
4839 * File size changes are not permitted in pageout.
4840 */
4841 static int
4842 nfs_pageout(ap)
4843 struct vnop_pageout_args /* {
4844 struct vnodeop_desc *a_desc;
4845 vnode_t a_vp;
4846 upl_t a_pl;
4847 vm_offset_t a_pl_offset;
4848 off_t a_f_offset;
4849 size_t a_size;
4850 int a_flags;
4851 vfs_context_t a_context;
4852 } */ *ap;
4853 {
4854 vnode_t vp = ap->a_vp;
4855 upl_t pl = ap->a_pl;
4856 size_t size= ap->a_size;
4857 off_t f_offset = ap->a_f_offset;
4858 vm_offset_t pl_offset = ap->a_pl_offset;
4859 int flags = ap->a_flags;
4860 struct nfsnode *np = VTONFS(vp);
4861 kauth_cred_t cred;
4862 proc_t p;
4863 struct nfsbuf *bp;
4864 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4865 daddr64_t lbn;
4866 int error = 0, iomode, must_commit;
4867 off_t off;
4868 vm_offset_t ioaddr;
4869 struct uio auio;
4870 struct iovec_32 aiov;
4871 int nofreeupl = flags & UPL_NOCOMMIT;
4872 size_t biosize, iosize, pgsize, xsize;
4873
4874 FSDBG(323, f_offset, size, pl, pl_offset);
4875
4876 if (pl == (upl_t)NULL)
4877 panic("nfs_pageout: no upl");
4878
4879 if (UBCINVALID(vp)) {
4880 printf("nfs_pageout: invalid vnode 0x%x", (int)vp);
4881 if (!nofreeupl)
4882 ubc_upl_abort(pl, 0);
4883 return (EIO);
4884 }
4885 UBCINFOCHECK("nfs_pageout", vp);
4886
4887 if (size <= 0) {
4888 printf("nfs_pageout: invalid size %d", size);
4889 if (!nofreeupl)
4890 ubc_upl_abort(pl, 0);
4891 return (EINVAL);
4892 }
4893
4894 if (!nmp) {
4895 if (!nofreeupl)
4896 ubc_upl_abort(pl, UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY);
4897 return (ENXIO);
4898 }
4899 biosize = nmp->nm_biosize;
4900
4901 /*
4902 * Check to see whether the buffer is incore.
4903 * If incore and not busy, invalidate it from the cache.
4904 */
4905 for (iosize = 0; iosize < size; iosize += xsize) {
4906 off = f_offset + iosize;
4907 /* need make sure we do things on block boundaries */
4908 xsize = biosize - (off % biosize);
4909 if (off + xsize > f_offset + size)
4910 xsize = f_offset + size - off;
4911 lbn = ubc_offtoblk(vp, off);
4912 lck_mtx_lock(nfs_buf_mutex);
4913 if ((bp = nfs_buf_incore(vp, lbn))) {
4914 FSDBG(323, off, bp, bp->nb_lflags, bp->nb_flags);
4915 if (nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0)) {
4916 lck_mtx_unlock(nfs_buf_mutex);
4917 /* no panic. just tell vm we are busy */
4918 if (!nofreeupl)
4919 ubc_upl_abort(pl, 0);
4920 return (EBUSY);
4921 }
4922 if (bp->nb_dirtyend > 0) {
4923 /*
4924 * if there's a dirty range in the buffer, check
4925 * to see if it extends beyond the pageout region
4926 *
4927 * if the dirty region lies completely within the
4928 * pageout region, we just invalidate the buffer
4929 * because it's all being written out now anyway.
4930 *
4931 * if any of the dirty region lies outside the
4932 * pageout region, we'll try to clip the dirty
4933 * region to eliminate the portion that's being
4934 * paged out. If that's not possible, because
4935 * the dirty region extends before and after the
4936 * pageout region, then we'll just return EBUSY.
4937 */
4938 off_t boff, start, end;
4939 boff = NBOFF(bp);
4940 start = off;
4941 end = off + xsize;
4942 /* clip end to EOF */
4943 if (end > (off_t)np->n_size)
4944 end = np->n_size;
4945 start -= boff;
4946 end -= boff;
4947 if ((bp->nb_dirtyoff < start) &&
4948 (bp->nb_dirtyend > end)) {
4949 /* not gonna be able to clip the dirty region */
4950 FSDBG(323, vp, bp, 0xd00deebc, EBUSY);
4951 nfs_buf_drop(bp);
4952 lck_mtx_unlock(nfs_buf_mutex);
4953 if (!nofreeupl)
4954 ubc_upl_abort(pl, 0);
4955 return (EBUSY);
4956 }
4957 if ((bp->nb_dirtyoff < start) ||
4958 (bp->nb_dirtyend > end)) {
4959 /* clip dirty region, if necessary */
4960 if (bp->nb_dirtyoff < start)
4961 bp->nb_dirtyend = min(bp->nb_dirtyend, start);
4962 if (bp->nb_dirtyend > end)
4963 bp->nb_dirtyoff = max(bp->nb_dirtyoff, end);
4964 FSDBG(323, bp, bp->nb_dirtyoff, bp->nb_dirtyend, 0xd00dee00);
4965 /* we're leaving this block dirty */
4966 nfs_buf_drop(bp);
4967 lck_mtx_unlock(nfs_buf_mutex);
4968 continue;
4969 }
4970 }
4971 nfs_buf_remfree(bp);
4972 lck_mtx_unlock(nfs_buf_mutex);
4973 SET(bp->nb_flags, NB_INVAL);
4974 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
4975 CLR(bp->nb_flags, NB_NEEDCOMMIT);
4976 np->n_needcommitcnt--;
4977 CHECK_NEEDCOMMITCNT(np);
4978 }
4979 nfs_buf_release(bp, 1);
4980 } else {
4981 lck_mtx_unlock(nfs_buf_mutex);
4982 }
4983 }
4984
4985 cred = ubc_getcred(vp);
4986 if (cred == NOCRED)
4987 cred = vfs_context_ucred(ap->a_context);
4988 p = vfs_context_proc(ap->a_context);
4989
4990 if (np->n_flag & NWRITEERR) {
4991 np->n_flag &= ~NWRITEERR;
4992 if (!nofreeupl)
4993 ubc_upl_abort_range(pl, pl_offset, size,
4994 UPL_ABORT_FREE_ON_EMPTY);
4995 return (np->n_error);
4996 }
4997 if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
4998 nfs_fsinfo(nmp, vp, cred, p);
4999
5000 if (f_offset < 0 || f_offset >= (off_t)np->n_size ||
5001 f_offset & PAGE_MASK_64 || size & PAGE_MASK_64) {
5002 if (!nofreeupl)
5003 ubc_upl_abort_range(pl, pl_offset, size,
5004 UPL_ABORT_FREE_ON_EMPTY);
5005 return (EINVAL);
5006 }
5007
5008 ubc_upl_map(pl, &ioaddr);
5009 ioaddr += pl_offset;
5010
5011 if ((u_quad_t)f_offset + size > np->n_size)
5012 xsize = np->n_size - f_offset;
5013 else
5014 xsize = size;
5015
5016 pgsize = round_page_64(xsize);
5017 if (size > pgsize) {
5018 if (!nofreeupl)
5019 ubc_upl_abort_range(pl, pl_offset + pgsize,
5020 size - pgsize,
5021 UPL_ABORT_FREE_ON_EMPTY);
5022 }
5023
5024 /*
5025 * check for partial page and clear the
5026 * contents past end of the file before
5027 * releasing it in the VM page cache
5028 */
5029 if ((u_quad_t)f_offset < np->n_size && (u_quad_t)f_offset + size > np->n_size) {
5030 size_t io = np->n_size - f_offset;
5031 bzero((caddr_t)(ioaddr + io), size - io);
5032 FSDBG(321, np->n_size, f_offset, f_offset + io, size - io);
5033 }
5034
5035 auio.uio_offset = f_offset;
5036 #if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
5037 auio.uio_segflg = UIO_SYSSPACE;
5038 #else
5039 auio.uio_segflg = UIO_SYSSPACE32;
5040 #endif
5041 auio.uio_rw = UIO_READ;
5042 auio.uio_procp = p;
5043
5044 do {
5045 /*
5046 * It would be nice to be able to issue all these requests
5047 * in parallel instead of waiting for each one to complete
5048 * before sending the next one.
5049 * XXX Should we align these requests to block boundaries?
5050 */
5051 iosize = min(biosize, xsize);
5052 uio_uio_resid_set(&auio, iosize);
5053 aiov.iov_len = iosize;
5054 aiov.iov_base = (uintptr_t)ioaddr;
5055 auio.uio_iovs.iov32p = &aiov;
5056 auio.uio_iovcnt = 1;
5057
5058 FSDBG(323, auio.uio_offset, uio_uio_resid(&auio), ioaddr, xsize);
5059 OSAddAtomic(1, (SInt32*)&nfsstats.pageouts);
5060
5061 vnode_startwrite(vp);
5062
5063 /* NMODIFIED would be set here if doing unstable writes */
5064 iomode = NFSV3WRITE_FILESYNC;
5065 error = nfs_writerpc(vp, &auio, cred, p, &iomode, &must_commit);
5066 if (must_commit)
5067 nfs_clearcommit(vnode_mount(vp));
5068 vnode_writedone(vp);
5069 if (error)
5070 goto cleanup;
5071 /* Note: no need to check uio_resid, because */
5072 /* it'll only be set if there was an error. */
5073 ioaddr += iosize;
5074 xsize -= iosize;
5075 } while (xsize > 0);
5076
5077 cleanup:
5078 ubc_upl_unmap(pl);
5079 /*
5080 * We've had several different solutions on what to do when the pageout
5081 * gets an error. If we don't handle it, and return an error to the
5082 * caller, vm, it will retry . This can end in endless looping
5083 * between vm and here doing retries of the same page. Doing a dump
5084 * back to vm, will get it out of vm's knowledge and we lose whatever
5085 * data existed. This is risky, but in some cases necessary. For
5086 * example, the initial fix here was to do that for ESTALE. In that case
5087 * the server is telling us that the file is no longer the same. We
5088 * would not want to keep paging out to that. We also saw some 151
5089 * errors from Auspex server and NFSv3 can return errors higher than
5090 * ELAST. Those along with NFS known server errors we will "dump" from
5091 * vm. Errors we don't expect to occur, we dump and log for further
5092 * analysis. Errors that could be transient, networking ones,
5093 * we let vm "retry". Lastly, errors that we retry, but may have potential
5094 * to storm the network, we "retrywithsleep". "sever" will be used in
5095 * in the future to dump all pages of object for cases like ESTALE.
5096 * All this is the basis for the states returned and first guesses on
5097 * error handling. Tweaking expected as more statistics are gathered.
5098 * Note, in the long run we may need another more robust solution to
5099 * have some kind of persistant store when the vm cannot dump nor keep
5100 * retrying as a solution, but this would be a file architectural change
5101 */
5102
5103 if (!nofreeupl) { /* otherwise stacked file system has to handle this */
5104 if (error) {
5105 int abortflags = 0;
5106 short action = nfs_pageouterrorhandler(error);
5107
5108 switch (action) {
5109 case DUMP:
5110 abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
5111 break;
5112 case DUMPANDLOG:
5113 abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
5114 if (error <= ELAST &&
5115 (errorcount[error] % 100 == 0))
5116 printf("nfs_pageout: unexpected error %d. dumping vm page\n", error);
5117 errorcount[error]++;
5118 break;
5119 case RETRY:
5120 abortflags = UPL_ABORT_FREE_ON_EMPTY;
5121 break;
5122 case RETRYWITHSLEEP:
5123 abortflags = UPL_ABORT_FREE_ON_EMPTY;
5124 /* pri unused. PSOCK for placeholder. */
5125 tsleep(&lbolt, PSOCK, "nfspageout", 0);
5126 break;
5127 case SEVER: /* not implemented */
5128 default:
5129 printf("nfs_pageout: action %d not expected\n", action);
5130 break;
5131 }
5132
5133 ubc_upl_abort_range(pl, pl_offset, size, abortflags);
5134 /* return error in all cases above */
5135
5136 } else
5137 ubc_upl_commit_range(pl, pl_offset, pgsize,
5138 UPL_COMMIT_CLEAR_DIRTY |
5139 UPL_COMMIT_FREE_ON_EMPTY);
5140 }
5141 return (error);
5142 }
5143
5144 /* Blktooff derives file offset given a logical block number */
5145 static int
5146 nfs_blktooff(ap)
5147 struct vnop_blktooff_args /* {
5148 struct vnodeop_desc *a_desc;
5149 vnode_t a_vp;
5150 daddr64_t a_lblkno;
5151 off_t *a_offset;
5152 } */ *ap;
5153 {
5154 int biosize;
5155 vnode_t vp = ap->a_vp;
5156 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5157
5158 if (!nmp)
5159 return (ENXIO);
5160 biosize = nmp->nm_biosize;
5161
5162 *ap->a_offset = (off_t)(ap->a_lblkno * biosize);
5163
5164 return (0);
5165 }
5166
5167 static int
5168 nfs_offtoblk(ap)
5169 struct vnop_offtoblk_args /* {
5170 struct vnodeop_desc *a_desc;
5171 vnode_t a_vp;
5172 off_t a_offset;
5173 daddr64_t *a_lblkno;
5174 } */ *ap;
5175 {
5176 int biosize;
5177 vnode_t vp = ap->a_vp;
5178 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5179
5180 if (!nmp)
5181 return (ENXIO);
5182 biosize = nmp->nm_biosize;
5183
5184 *ap->a_lblkno = (daddr64_t)(ap->a_offset / biosize);
5185
5186 return (0);
5187 }
5188