]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_vnops.c
5ed8d569b18ea06c88fc74fac9bc2c1f5b1d01a7
[apple/xnu.git] / bsd / nfs / nfs_vnops.c
1 /*
2 * Copyright (c) 2006 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 vnode_attr *vap = ap->a_vap;
1043 int error = 0;
1044 u_quad_t tsize;
1045 kauth_cred_t cred;
1046 proc_t p;
1047
1048 #ifndef nolint
1049 tsize = (u_quad_t)0;
1050 #endif
1051
1052 /* Setting of flags is not supported. */
1053 if (VATTR_IS_ACTIVE(vap, va_flags))
1054 return (ENOTSUP);
1055
1056 cred = vfs_context_ucred(ap->a_context);
1057 p = vfs_context_proc(ap->a_context);
1058
1059 VATTR_SET_SUPPORTED(vap, va_mode);
1060 VATTR_SET_SUPPORTED(vap, va_uid);
1061 VATTR_SET_SUPPORTED(vap, va_gid);
1062 VATTR_SET_SUPPORTED(vap, va_data_size);
1063 VATTR_SET_SUPPORTED(vap, va_access_time);
1064 VATTR_SET_SUPPORTED(vap, va_modify_time);
1065
1066 /* Disallow write attempts if the filesystem is mounted read-only. */
1067 if ((VATTR_IS_ACTIVE(vap, va_flags) || VATTR_IS_ACTIVE(vap, va_mode) ||
1068 VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid) ||
1069 VATTR_IS_ACTIVE(vap, va_access_time) ||
1070 VATTR_IS_ACTIVE(vap, va_modify_time)) &&
1071 vnode_vfsisrdonly(vp))
1072 return (EROFS);
1073
1074 if (VATTR_IS_ACTIVE(vap, va_data_size)) {
1075 switch (vnode_vtype(vp)) {
1076 case VDIR:
1077 return (EISDIR);
1078 case VCHR:
1079 case VBLK:
1080 case VSOCK:
1081 case VFIFO:
1082 if (!VATTR_IS_ACTIVE(vap, va_modify_time) &&
1083 !VATTR_IS_ACTIVE(vap, va_access_time) &&
1084 !VATTR_IS_ACTIVE(vap, va_mode) &&
1085 !VATTR_IS_ACTIVE(vap, va_uid) &&
1086 !VATTR_IS_ACTIVE(vap, va_gid))
1087 return (0);
1088 VATTR_CLEAR_ACTIVE(vap, va_data_size);
1089 break;
1090 default:
1091 /*
1092 * Disallow write attempts if the filesystem is
1093 * mounted read-only.
1094 */
1095 if (vnode_vfsisrdonly(vp))
1096 return (EROFS);
1097 FSDBG_TOP(512, np->n_size, vap->va_data_size,
1098 np->n_vattr.nva_size, np->n_flag);
1099 if (np->n_flag & NMODIFIED) {
1100 if (vap->va_data_size == 0)
1101 error = nfs_vinvalbuf(vp, 0, cred, p, 1);
1102 else
1103 error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1104 if (error) {
1105 printf("nfs_setattr: nfs_vinvalbuf %d\n", error);
1106 FSDBG_BOT(512, np->n_size, vap->va_data_size,
1107 np->n_vattr.nva_size, -1);
1108 return (error);
1109 }
1110 } else if (np->n_size > vap->va_data_size) { /* shrinking? */
1111 daddr64_t obn, bn;
1112 int biosize, neweofoff, mustwrite;
1113 struct nfsbuf *bp;
1114
1115 biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
1116 obn = (np->n_size - 1) / biosize;
1117 bn = vap->va_data_size / biosize;
1118 for ( ; obn >= bn; obn--) {
1119 if (!nfs_buf_is_incore(vp, obn))
1120 continue;
1121 error = nfs_buf_get(vp, obn, biosize, 0, NBLK_READ, &bp);
1122 if (error)
1123 continue;
1124 if (obn != bn) {
1125 FSDBG(512, bp, bp->nb_flags, 0, obn);
1126 SET(bp->nb_flags, NB_INVAL);
1127 nfs_buf_release(bp, 1);
1128 continue;
1129 }
1130 mustwrite = 0;
1131 neweofoff = vap->va_data_size - NBOFF(bp);
1132 /* check for any dirty data before the new EOF */
1133 if (bp->nb_dirtyend && bp->nb_dirtyoff < neweofoff) {
1134 /* clip dirty range to EOF */
1135 if (bp->nb_dirtyend > neweofoff)
1136 bp->nb_dirtyend = neweofoff;
1137 mustwrite++;
1138 }
1139 bp->nb_dirty &= (1 << round_page_32(neweofoff)/PAGE_SIZE) - 1;
1140 if (bp->nb_dirty)
1141 mustwrite++;
1142 if (!mustwrite) {
1143 FSDBG(512, bp, bp->nb_flags, 0, obn);
1144 SET(bp->nb_flags, NB_INVAL);
1145 nfs_buf_release(bp, 1);
1146 continue;
1147 }
1148 /* gotta write out dirty data before invalidating */
1149 /* (NB_STABLE indicates that data writes should be FILESYNC) */
1150 /* (NB_NOCACHE indicates buffer should be discarded) */
1151 CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL | NB_ASYNC | NB_READ));
1152 SET(bp->nb_flags, NB_STABLE | NB_NOCACHE);
1153 if (bp->nb_wcred == NOCRED) {
1154 kauth_cred_ref(cred);
1155 bp->nb_wcred = cred;
1156 }
1157 error = nfs_buf_write(bp);
1158 // Note: bp has been released
1159 if (error) {
1160 FSDBG(512, bp, 0xd00dee, 0xbad, error);
1161 np->n_error = error;
1162 np->n_flag |= NWRITEERR;
1163 /*
1164 * There was a write error and we need to
1165 * invalidate attrs and flush buffers in
1166 * order to sync up with the server.
1167 * (if this write was extending the file,
1168 * we may no longer know the correct size)
1169 */
1170 NATTRINVALIDATE(np);
1171 nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
1172 error = 0;
1173 }
1174 }
1175 }
1176 tsize = np->n_size;
1177 np->n_size = np->n_vattr.nva_size = vap->va_data_size;
1178 ubc_setsize(vp, (off_t)vap->va_data_size); /* XXX error? */
1179 }
1180 } else if ((VATTR_IS_ACTIVE(vap, va_modify_time) ||
1181 VATTR_IS_ACTIVE(vap, va_access_time)) &&
1182 (np->n_flag & NMODIFIED) && (vnode_vtype(vp) == VREG)) {
1183 error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1184 if (error == EINTR)
1185 return (error);
1186 }
1187 if (VATTR_IS_ACTIVE(vap, va_mode)) {
1188 NMODEINVALIDATE(np);
1189 }
1190 error = nfs_setattrrpc(vp, vap, cred, p);
1191 FSDBG_BOT(512, np->n_size, vap->va_data_size, np->n_vattr.nva_size, error);
1192 if (error && VATTR_IS_ACTIVE(vap, va_data_size)) {
1193 /* make every effort to resync file size w/ server... */
1194 int err; /* preserve "error" for return */
1195
1196 np->n_size = np->n_vattr.nva_size = tsize;
1197 ubc_setsize(vp, (off_t)np->n_size); /* XXX check error */
1198 vap->va_data_size = tsize;
1199 err = nfs_setattrrpc(vp, vap, cred, p);
1200 printf("nfs_setattr: nfs_setattrrpc %d %d\n", error, err);
1201 }
1202 return (error);
1203 }
1204
1205 /*
1206 * Do an nfs setattr rpc.
1207 */
1208 static int
1209 nfs_setattrrpc(vp, vap, cred, procp)
1210 vnode_t vp;
1211 struct vnode_attr *vap;
1212 kauth_cred_t cred;
1213 proc_t procp;
1214 {
1215 register struct nfsv2_sattr *sp;
1216 register caddr_t cp;
1217 register long t1, t2;
1218 caddr_t bpos, dpos, cp2;
1219 u_long *tl;
1220 int error = 0, wccpostattr = 0;
1221 mbuf_t mreq, mrep, md, mb, mb2;
1222 int v3;
1223 u_int64_t xid;
1224 struct timeval now;
1225
1226 if (!VFSTONFS(vnode_mount(vp)))
1227 return (ENXIO);
1228 v3 = NFS_ISV3(vp);
1229
1230 nfsm_reqhead(NFSX_FH(v3) + NFSX_SATTR(v3));
1231 if (error)
1232 return (error);
1233 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SETATTR]);
1234 nfsm_fhtom(vp, v3);
1235 if (v3) {
1236 if (VATTR_IS_ACTIVE(vap, va_mode)) {
1237 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1238 *tl++ = nfs_true;
1239 *tl = txdr_unsigned(vap->va_mode);
1240 } else {
1241 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1242 *tl = nfs_false;
1243 }
1244 if (VATTR_IS_ACTIVE(vap, va_uid)) {
1245 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1246 *tl++ = nfs_true;
1247 *tl = txdr_unsigned(vap->va_uid);
1248 } else {
1249 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1250 *tl = nfs_false;
1251 }
1252 if (VATTR_IS_ACTIVE(vap, va_gid)) {
1253 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1254 *tl++ = nfs_true;
1255 *tl = txdr_unsigned(vap->va_gid);
1256 } else {
1257 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1258 *tl = nfs_false;
1259 }
1260 if (VATTR_IS_ACTIVE(vap, va_data_size)) {
1261 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
1262 *tl++ = nfs_true;
1263 txdr_hyper(&vap->va_data_size, tl);
1264 } else {
1265 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1266 *tl = nfs_false;
1267 }
1268 microtime(&now);
1269 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1270 if (vap->va_access_time.tv_sec != now.tv_sec) {
1271 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
1272 *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
1273 txdr_nfsv3time(&vap->va_access_time, tl);
1274 } else {
1275 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1276 *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
1277 }
1278 } else {
1279 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1280 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
1281 }
1282 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1283 if (vap->va_modify_time.tv_sec != now.tv_sec) {
1284 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
1285 *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
1286 txdr_nfsv3time(&vap->va_modify_time, tl);
1287 } else {
1288 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1289 *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
1290 }
1291 } else {
1292 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1293 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
1294 }
1295 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1296 *tl = nfs_false;
1297 } else {
1298 struct timespec neg1time = { -1, -1 };
1299 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
1300 if (VATTR_IS_ACTIVE(vap, va_mode))
1301 sp->sa_mode = vtonfsv2_mode(vnode_vtype(vp), vap->va_mode);
1302 else
1303 sp->sa_mode = nfs_xdrneg1;
1304 if (VATTR_IS_ACTIVE(vap, va_uid))
1305 sp->sa_uid = txdr_unsigned(vap->va_uid);
1306 else
1307 sp->sa_uid = nfs_xdrneg1;
1308 if (VATTR_IS_ACTIVE(vap, va_gid))
1309 sp->sa_gid = txdr_unsigned(vap->va_gid);
1310 else
1311 sp->sa_gid = nfs_xdrneg1;
1312 if (VATTR_IS_ACTIVE(vap, va_data_size))
1313 sp->sa_size = txdr_unsigned(vap->va_data_size);
1314 else
1315 sp->sa_size = nfs_xdrneg1;
1316 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1317 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
1318 } else {
1319 txdr_nfsv2time(&neg1time, &sp->sa_atime);
1320 }
1321 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1322 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
1323 } else {
1324 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
1325 }
1326 }
1327 nfsm_request(vp, NFSPROC_SETATTR, procp, cred, &xid);
1328 if (v3) {
1329 struct timespec premtime = { 0, 0 };
1330 if (mrep) {
1331 nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
1332 }
1333 /* if file hadn't changed, update cached mtime */
1334 if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==)) {
1335 VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
1336 }
1337 /* if directory hadn't changed, update namecache mtime */
1338 if ((vnode_vtype(vp) == VDIR) &&
1339 nfstimespeccmp(&VTONFS(vp)->n_ncmtime, &premtime, ==)) {
1340 VTONFS(vp)->n_ncmtime = VTONFS(vp)->n_vattr.nva_mtime;
1341 }
1342 if (!wccpostattr)
1343 NATTRINVALIDATE(VTONFS(vp));
1344 } else {
1345 if (mrep) {
1346 nfsm_loadattr(vp, v3, NULL, &xid);
1347 }
1348 }
1349 nfsm_reqdone;
1350 return (error);
1351 }
1352
1353 /*
1354 * nfs lookup call, one step at a time...
1355 * First look in cache
1356 * If not found, unlock the directory nfsnode and do the rpc
1357 */
1358 static int
1359 nfs_lookup(ap)
1360 struct vnop_lookup_args /* {
1361 struct vnodeop_desc *a_desc;
1362 vnode_t a_dvp;
1363 vnode_t *a_vpp;
1364 struct componentname *a_cnp;
1365 vfs_context_t a_context;
1366 } */ *ap;
1367 {
1368 struct componentname *cnp = ap->a_cnp;
1369 vnode_t dvp = ap->a_dvp;
1370 vnode_t *vpp = ap->a_vpp;
1371 int flags = cnp->cn_flags;
1372 vnode_t newvp;
1373 u_long *tl;
1374 caddr_t cp;
1375 long t1, t2;
1376 caddr_t bpos, dpos, cp2;
1377 mbuf_t mreq, mrep, md, mb, mb2;
1378 long len;
1379 u_char *fhp;
1380 struct nfsnode *dnp, *np;
1381 int wantparent, error, attrflag, dattrflag, fhsize, fhisdvp;
1382 int v3 = NFS_ISV3(dvp);
1383 u_int64_t xid, dxid;
1384 struct nfs_vattr nvattr;
1385 kauth_cred_t cred;
1386 proc_t p;
1387 int ngflags;
1388
1389 *vpp = NULLVP;
1390
1391 cred = vfs_context_ucred(ap->a_context);
1392 p = vfs_context_proc(ap->a_context);
1393
1394 wantparent = flags & (LOCKPARENT|WANTPARENT);
1395 dnp = VTONFS(dvp);
1396
1397 error = nfs_getattr(dvp, &nvattr, cred, p);
1398 if (error)
1399 goto error_return;
1400 if (nfstimespeccmp(&dnp->n_ncmtime, &nvattr.nva_mtime, !=)) {
1401 /*
1402 * This directory has changed on us.
1403 * Purge any name cache entries.
1404 */
1405 cache_purge(dvp);
1406 dnp->n_ncmtime = nvattr.nva_mtime;
1407 }
1408
1409 error = cache_lookup(dvp, vpp, cnp);
1410 switch (error) {
1411 case ENOENT:
1412 /* negative cache entry same as cache miss */
1413 error = 0;
1414 /* FALLTHROUGH */
1415 case 0:
1416 /* cache miss */
1417 break;
1418 case -1:
1419 /* cache hit, not really an error */
1420 {
1421 struct vnop_access_args naa;
1422
1423 OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_hits);
1424
1425 /* check for directory access */
1426 naa.a_vp = dvp;
1427 naa.a_action = KAUTH_VNODE_SEARCH;
1428 naa.a_context = ap->a_context;
1429
1430 /* compute actual success/failure based on accessibility */
1431 error = nfs_access(&naa);
1432 }
1433 /* FALLTHROUGH */
1434 default:
1435 /* unexpected error from cache_lookup */
1436 goto error_return;
1437 }
1438
1439 /* check for lookup of "." */
1440 if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
1441 /* skip lookup, we know who we are */
1442 fhisdvp = 1;
1443 fhp = NULL;
1444 fhsize = 0;
1445 mrep = NULL;
1446 goto found;
1447 }
1448
1449 /* do we know this name is too long? */
1450 if (v3) {
1451 /* For NFSv3: need uniform pathconf info to test pc_namemax */
1452 struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1453 if (!nmp) {
1454 error = ENXIO;
1455 goto error_return;
1456 }
1457 if (((nmp->nm_state & (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) ==
1458 (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) &&
1459 (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS) &&
1460 (cnp->cn_namelen > (long)nmp->nm_fsinfo.namemax)) {
1461 error = ENAMETOOLONG;
1462 goto error_return;
1463 }
1464 } else if (cnp->cn_namelen > NFS_MAXNAMLEN) {
1465 error = ENAMETOOLONG;
1466 goto error_return;
1467 }
1468
1469 error = 0;
1470 newvp = NULLVP;
1471
1472 OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_misses);
1473 len = cnp->cn_namelen;
1474 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
1475 if (error)
1476 goto error_return;
1477 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
1478 nfsm_fhtom(dvp, v3);
1479 nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
1480 /* nfsm_request for NFSv2 causes you to goto to nfsmout upon errors */
1481 nfsm_request(dvp, NFSPROC_LOOKUP, p, cred, &xid);
1482
1483 if (error) {
1484 if (mrep) {
1485 nfsm_postop_attr_update(dvp, v3, dattrflag, &xid);
1486 mbuf_freem(mrep);
1487 }
1488 goto nfsmout;
1489 }
1490
1491 /* get the filehandle */
1492 nfsm_getfh(fhp, fhsize, v3);
1493 /* is the file handle the same as this directory's file handle? */
1494 fhisdvp = NFS_CMPFH(dnp, fhp, fhsize);
1495
1496 /* get attributes */
1497 if (v3) {
1498 dxid = xid;
1499 nfsm_postop_attr_get(v3, attrflag, &nvattr);
1500 nfsm_postop_attr_update(dvp, v3, dattrflag, &dxid);
1501 if (!attrflag && (!fhisdvp || !dattrflag)) {
1502 /* We need valid attributes in order */
1503 /* to call nfs_nget/vnode_create(). */
1504 error = nfs_getattr_no_vnode(vnode_mount(dvp),
1505 fhp, fhsize, cred, p, &nvattr, &xid);
1506 if (error) {
1507 mbuf_freem(mrep);
1508 goto error_return;
1509 }
1510 }
1511 } else {
1512 nfsm_attr_get(v3, &nvattr);
1513 }
1514
1515 found:
1516
1517 /*
1518 * Handle RENAME case...
1519 */
1520 if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
1521 if (fhisdvp) {
1522 mbuf_freem(mrep);
1523 error = EISDIR;
1524 goto error_return;
1525 }
1526 error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
1527 &nvattr, &xid, 0, &np);
1528 if (error) {
1529 mbuf_freem(mrep);
1530 goto error_return;
1531 }
1532 *vpp = NFSTOV(np);
1533 mbuf_freem(mrep);
1534
1535 goto error_return;
1536 }
1537
1538 if ((cnp->cn_flags & MAKEENTRY) &&
1539 (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))
1540 ngflags = NG_MAKEENTRY;
1541 else
1542 ngflags = 0;
1543
1544 if (fhisdvp) {
1545 error = vnode_get(dvp);
1546 if (error) {
1547 mbuf_freem(mrep);
1548 goto error_return;
1549 }
1550 newvp = dvp;
1551 /* test fhp to see if we have valid attributes in nvattr */
1552 if (fhp && (dnp->n_xid <= xid)) {
1553 error = nfs_loadattrcache(dnp, &nvattr, &xid, 0);
1554 if (error) {
1555 vnode_put(dvp);
1556 mbuf_freem(mrep);
1557 goto error_return;
1558 }
1559 }
1560 } else {
1561 error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
1562 &nvattr, &xid, ngflags, &np);
1563 if (error) {
1564 mbuf_freem(mrep);
1565 goto error_return;
1566 }
1567 newvp = NFSTOV(np);
1568 }
1569 *vpp = newvp;
1570 // if (error == 0 && *vpp != NULL && *vpp != dvp)
1571 // nfs_unlock(VTONFS(*vpp));
1572
1573 nfsm_reqdone;
1574 if (error) {
1575 if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
1576 (flags & ISLASTCN) && error == ENOENT) {
1577 if (vnode_mount(dvp) && vnode_vfsisrdonly(dvp))
1578 error = EROFS;
1579 else
1580 error = EJUSTRETURN;
1581 }
1582 }
1583 error_return:
1584 if (error && *vpp) {
1585 vnode_put(*vpp);
1586 *vpp = NULLVP;
1587 }
1588 return (error);
1589 }
1590
1591 /*
1592 * nfs read call.
1593 * Just call nfs_bioread() to do the work.
1594 */
1595 static int
1596 nfs_read(ap)
1597 struct vnop_read_args /* {
1598 struct vnodeop_desc *a_desc;
1599 vnode_t a_vp;
1600 struct uio *a_uio;
1601 int a_ioflag;
1602 vfs_context_t a_context;
1603 } */ *ap;
1604 {
1605 if (vnode_vtype(ap->a_vp) != VREG)
1606 return (EPERM);
1607 return (nfs_bioread(ap->a_vp, ap->a_uio, ap->a_ioflag,
1608 vfs_context_ucred(ap->a_context),
1609 vfs_context_proc(ap->a_context)));
1610 }
1611
1612
1613 /*
1614 * nfs readlink call
1615 */
1616 static int
1617 nfs_readlink(ap)
1618 struct vnop_readlink_args /* {
1619 struct vnodeop_desc *a_desc;
1620 vnode_t a_vp;
1621 struct uio *a_uio;
1622 vfs_context_t a_context;
1623 } */ *ap;
1624 {
1625 if (vnode_vtype(ap->a_vp) != VLNK)
1626 return (EPERM);
1627 return (nfs_bioread(ap->a_vp, ap->a_uio, 0,
1628 vfs_context_ucred(ap->a_context),
1629 vfs_context_proc(ap->a_context)));
1630 }
1631
1632 /*
1633 * Do a readlink rpc.
1634 * Called by nfs_doio() from below the buffer cache.
1635 */
1636 int
1637 nfs_readlinkrpc(
1638 vnode_t vp,
1639 struct uio *uiop,
1640 kauth_cred_t cred,
1641 proc_t p)
1642 {
1643 register u_long *tl;
1644 register caddr_t cp;
1645 register long t1, t2;
1646 caddr_t bpos, dpos, cp2;
1647 int error = 0, len, attrflag;
1648 mbuf_t mreq, mrep, md, mb, mb2;
1649 int v3;
1650 u_int64_t xid;
1651
1652 if (!VFSTONFS(vnode_mount(vp)))
1653 return (ENXIO);
1654 v3 = NFS_ISV3(vp);
1655
1656 nfsm_reqhead(NFSX_FH(v3));
1657 if (error)
1658 return (error);
1659 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READLINK]);
1660 nfsm_fhtom(vp, v3);
1661 nfsm_request(vp, NFSPROC_READLINK, p, cred, &xid);
1662 if (v3 && mrep)
1663 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
1664 if (!error) {
1665 nfsm_strsiz(len, NFS_MAXPATHLEN, v3);
1666 if (len >= NFS_MAXPATHLEN) {
1667 struct nfsnode *np = VTONFS(vp);
1668 #if DIAGNOSTIC
1669 if (!np)
1670 panic("nfs_readlinkrpc: null np");
1671 #endif
1672 if (np->n_size && np->n_size < NFS_MAXPATHLEN)
1673 len = np->n_size;
1674 }
1675 nfsm_mtouio(uiop, len);
1676 }
1677 nfsm_reqdone;
1678 return (error);
1679 }
1680
1681 /*
1682 * nfs read rpc call
1683 * Ditto above
1684 */
1685 int
1686 nfs_readrpc(
1687 vnode_t vp,
1688 struct uio *uiop,
1689 kauth_cred_t cred,
1690 proc_t p)
1691 {
1692 register u_long *tl;
1693 register caddr_t cp;
1694 register long t1, t2;
1695 caddr_t bpos, dpos, cp2;
1696 mbuf_t mreq, mrep, md, mb, mb2;
1697 struct nfsmount *nmp;
1698 int error = 0, len, retlen, tsiz, eof = 0, attrflag;
1699 int v3, nmrsize;
1700 u_int64_t xid;
1701
1702 FSDBG_TOP(536, vp, uiop->uio_offset, uio_uio_resid(uiop), 0);
1703 nmp = VFSTONFS(vnode_mount(vp));
1704 if (!nmp)
1705 return (ENXIO);
1706 v3 = NFS_ISV3(vp);
1707 nmrsize = nmp->nm_rsize;
1708
1709 // LP64todo - fix this
1710 tsiz = uio_uio_resid(uiop);
1711 if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
1712 FSDBG_BOT(536, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
1713 return (EFBIG);
1714 }
1715 while (tsiz > 0) {
1716 len = (tsiz > nmrsize) ? nmrsize : tsiz;
1717 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED * 3);
1718 if (error)
1719 break;
1720 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READ]);
1721 nfsm_fhtom(vp, v3);
1722 nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
1723 if (v3) {
1724 txdr_hyper(&uiop->uio_offset, tl);
1725 *(tl + 2) = txdr_unsigned(len);
1726 } else {
1727 *tl++ = txdr_unsigned(uiop->uio_offset);
1728 *tl++ = txdr_unsigned(len);
1729 *tl = 0;
1730 }
1731 FSDBG(536, vp, uiop->uio_offset, len, 0);
1732 nfsm_request(vp, NFSPROC_READ, p, cred, &xid);
1733 if (v3) {
1734 if (mrep) {
1735 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
1736 }
1737 if (error) {
1738 mbuf_freem(mrep);
1739 goto nfsmout;
1740 }
1741 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
1742 eof = fxdr_unsigned(int, *(tl + 1));
1743 } else {
1744 if (mrep) {
1745 nfsm_loadattr(vp, v3, NULL, &xid);
1746 }
1747 }
1748 if (mrep) {
1749 nfsm_strsiz(retlen, nmrsize, 0);
1750 nfsm_mtouio(uiop, retlen);
1751 mbuf_freem(mrep);
1752 } else {
1753 retlen = 0;
1754 }
1755 tsiz -= retlen;
1756 if (v3) {
1757 if (eof || retlen == 0)
1758 tsiz = 0;
1759 } else if (retlen < len)
1760 tsiz = 0;
1761 }
1762 nfsmout:
1763 FSDBG_BOT(536, vp, eof, uio_uio_resid(uiop), error);
1764 return (error);
1765 }
1766
1767 /*
1768 * nfs write call
1769 */
1770 int
1771 nfs_writerpc(
1772 vnode_t vp,
1773 struct uio *uiop,
1774 kauth_cred_t cred,
1775 proc_t p,
1776 int *iomode,
1777 int *must_commit)
1778 {
1779 register u_long *tl;
1780 register caddr_t cp;
1781 register int t1, t2, backup;
1782 caddr_t bpos, dpos, cp2;
1783 mbuf_t mreq, mrep, md, mb, mb2;
1784 struct nfsmount *nmp;
1785 int error = 0, len, tsiz, updatemtime = 0, wccpostattr = 0, rlen, commit;
1786 int v3, committed = NFSV3WRITE_FILESYNC;
1787 u_int64_t xid;
1788 mount_t mp;
1789
1790 #if DIAGNOSTIC
1791 if (uiop->uio_iovcnt != 1)
1792 panic("nfs_writerpc: iovcnt > 1");
1793 #endif
1794 FSDBG_TOP(537, vp, uiop->uio_offset, uio_uio_resid(uiop), *iomode);
1795 nmp = VFSTONFS(vnode_mount(vp));
1796 if (!nmp)
1797 return (ENXIO);
1798 v3 = NFS_ISV3(vp);
1799 *must_commit = 0;
1800 // LP64todo - fix this
1801 tsiz = uio_uio_resid(uiop);
1802 if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
1803 FSDBG_BOT(537, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
1804 return (EFBIG);
1805 }
1806 while (tsiz > 0) {
1807 nmp = VFSTONFS(vnode_mount(vp));
1808 if (!nmp) {
1809 error = ENXIO;
1810 break;
1811 }
1812 len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
1813 nfsm_reqhead(NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
1814 if (error)
1815 break;
1816 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_WRITE]);
1817 nfsm_fhtom(vp, v3);
1818 if (v3) {
1819 nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
1820 txdr_hyper(&uiop->uio_offset, tl);
1821 tl += 2;
1822 *tl++ = txdr_unsigned(len);
1823 *tl++ = txdr_unsigned(*iomode);
1824 } else {
1825 nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
1826 *++tl = txdr_unsigned(uiop->uio_offset);
1827 tl += 2;
1828 }
1829 *tl = txdr_unsigned(len);
1830 FSDBG(537, vp, uiop->uio_offset, len, 0);
1831 nfsm_uiotom(uiop, len);
1832 nfsm_request(vp, NFSPROC_WRITE, p, cred, &xid);
1833 nmp = VFSTONFS(vnode_mount(vp));
1834 if (!nmp)
1835 error = ENXIO;
1836 if (v3) {
1837 if (mrep) {
1838 struct timespec premtime;
1839 nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
1840 if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==))
1841 updatemtime = 1;
1842 }
1843 if (!error) {
1844 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
1845 NFSX_V3WRITEVERF);
1846 rlen = fxdr_unsigned(int, *tl++);
1847 if (rlen <= 0) {
1848 error = NFSERR_IO;
1849 break;
1850 } else if (rlen < len) {
1851 backup = len - rlen;
1852 uio_iov_base_add(uiop, -backup);
1853 uio_iov_len_add(uiop, backup);
1854 uiop->uio_offset -= backup;
1855 uio_uio_resid_add(uiop, backup);
1856 len = rlen;
1857 }
1858 commit = fxdr_unsigned(int, *tl++);
1859
1860 /*
1861 * Return the lowest committment level
1862 * obtained by any of the RPCs.
1863 */
1864 if (committed == NFSV3WRITE_FILESYNC)
1865 committed = commit;
1866 else if (committed == NFSV3WRITE_DATASYNC &&
1867 commit == NFSV3WRITE_UNSTABLE)
1868 committed = commit;
1869 if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) {
1870 bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
1871 NFSX_V3WRITEVERF);
1872 nmp->nm_state |= NFSSTA_HASWRITEVERF;
1873 } else if (bcmp((caddr_t)tl,
1874 (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) {
1875 *must_commit = 1;
1876 bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
1877 NFSX_V3WRITEVERF);
1878 }
1879 }
1880 } else {
1881 if (mrep) {
1882 nfsm_loadattr(vp, v3, NULL, &xid);
1883 }
1884 }
1885
1886 if (updatemtime)
1887 VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
1888 mbuf_freem(mrep);
1889 /*
1890 * we seem to have a case where we end up looping on shutdown
1891 * and taking down nfs servers. For V3, error cases, there is
1892 * no way to terminate loop, if the len was 0, meaning,
1893 * nmp->nm_wsize was trashed. FreeBSD has this fix in it.
1894 * Let's try it.
1895 */
1896 if (error)
1897 break;
1898 tsiz -= len;
1899 }
1900 nfsmout:
1901 if ((mp = vnode_mount(vp)) && (vfs_flags(mp) & MNT_ASYNC))
1902 committed = NFSV3WRITE_FILESYNC;
1903 *iomode = committed;
1904 if (error)
1905 uio_uio_resid_set(uiop, tsiz);
1906 FSDBG_BOT(537, vp, committed, uio_uio_resid(uiop), error);
1907 return (error);
1908 }
1909
1910 /*
1911 * nfs mknod rpc
1912 * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1913 * mode set to specify the file type and the size field for rdev.
1914 */
1915 static int
1916 nfs_mknodrpc(
1917 vnode_t dvp,
1918 vnode_t *vpp,
1919 struct componentname *cnp,
1920 struct vnode_attr *vap,
1921 kauth_cred_t cred,
1922 proc_t p)
1923 {
1924 register struct nfsv2_sattr *sp;
1925 register u_long *tl;
1926 register caddr_t cp;
1927 register long t1, t2;
1928 vnode_t newvp = (vnode_t)0;
1929 struct nfsnode *np = (struct nfsnode *)0;
1930 struct nfs_vattr nvattr;
1931 char *cp2;
1932 caddr_t bpos, dpos;
1933 int error = 0, wccpostattr = 0, gotvp = 0;
1934 struct timespec premtime = { 0, 0 };
1935 mbuf_t mreq, mrep, md, mb, mb2;
1936 u_long rdev;
1937 u_int64_t xid;
1938 int v3 = NFS_ISV3(dvp);
1939 int gotuid, gotgid;
1940
1941 if (!VATTR_IS_ACTIVE(vap, va_type))
1942 return (EINVAL);
1943 if (vap->va_type == VCHR || vap->va_type == VBLK) {
1944 if (!VATTR_IS_ACTIVE(vap, va_rdev))
1945 return (EINVAL);
1946 rdev = txdr_unsigned(vap->va_rdev);
1947 } else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
1948 rdev = 0xffffffff;
1949 else {
1950 return (ENOTSUP);
1951 }
1952 nfsm_reqhead(NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
1953 nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
1954 if (error)
1955 return (error);
1956
1957 VATTR_SET_SUPPORTED(vap, va_mode);
1958 VATTR_SET_SUPPORTED(vap, va_uid);
1959 VATTR_SET_SUPPORTED(vap, va_gid);
1960 VATTR_SET_SUPPORTED(vap, va_data_size);
1961 VATTR_SET_SUPPORTED(vap, va_access_time);
1962 VATTR_SET_SUPPORTED(vap, va_modify_time);
1963 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
1964 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
1965
1966 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKNOD]);
1967 nfsm_fhtom(dvp, v3);
1968 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
1969 if (v3) {
1970 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
1971 *tl++ = vtonfsv3_type(vap->va_type);
1972 nfsm_v3sattr(vap);
1973 if (vap->va_type == VCHR || vap->va_type == VBLK) {
1974 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
1975 *tl++ = txdr_unsigned(major(vap->va_rdev));
1976 *tl = txdr_unsigned(minor(vap->va_rdev));
1977 }
1978 } else {
1979 struct timespec neg1time = { -1, -1 };
1980 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
1981 sp->sa_mode = vtonfsv2_mode(vap->va_type,
1982 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
1983 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
1984 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
1985 sp->sa_size = rdev;
1986 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1987 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
1988 } else {
1989 txdr_nfsv2time(&neg1time, &sp->sa_atime);
1990 }
1991 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1992 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
1993 } else {
1994 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
1995 }
1996 }
1997 nfsm_request(dvp, NFSPROC_MKNOD, p, cred, &xid);
1998 /* XXX no EEXIST kludge here? */
1999 if (!error) {
2000 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2001 if (!gotvp) {
2002 error = nfs_lookitup(dvp, cnp->cn_nameptr,
2003 cnp->cn_namelen, cred, p, &np);
2004 if (!error)
2005 newvp = NFSTOV(np);
2006 }
2007 }
2008 if (v3 && mrep)
2009 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2010 if (!error && (gotuid || gotgid) &&
2011 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2012 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2013 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2014 /* clear ID bits if server didn't use them (or we can't tell) */
2015 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2016 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2017 }
2018 nfsm_reqdone;
2019 if (error) {
2020 if (newvp)
2021 vnode_put(newvp);
2022 } else {
2023 *vpp = newvp;
2024 }
2025 VTONFS(dvp)->n_flag |= NMODIFIED;
2026 /* if directory hadn't changed, update namecache mtime */
2027 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2028 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2029 if (!wccpostattr)
2030 NATTRINVALIDATE(VTONFS(dvp));
2031 return (error);
2032 }
2033
2034 /*
2035 * nfs mknod vop
2036 * just call nfs_mknodrpc() to do the work.
2037 */
2038 /* ARGSUSED */
2039 static int
2040 nfs_mknod(ap)
2041 struct vnop_mknod_args /* {
2042 struct vnodeop_desc *a_desc;
2043 vnode_t a_dvp;
2044 vnode_t *a_vpp;
2045 struct componentname *a_cnp;
2046 struct vnode_attr *a_vap;
2047 vfs_context_t a_context;
2048 } */ *ap;
2049 {
2050 int error;
2051
2052 error = nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap,
2053 vfs_context_ucred(ap->a_context),
2054 vfs_context_proc(ap->a_context));
2055
2056 return (error);
2057 }
2058
2059 static u_long create_verf;
2060 /*
2061 * nfs file create call
2062 */
2063 static int
2064 nfs_create(ap)
2065 struct vnop_create_args /* {
2066 struct vnodeop_desc *a_desc;
2067 vnode_t a_dvp;
2068 vnode_t *a_vpp;
2069 struct componentname *a_cnp;
2070 struct vnode_attr *a_vap;
2071 vfs_context_t a_context;
2072 } */ *ap;
2073 {
2074 vnode_t dvp = ap->a_dvp;
2075 struct vnode_attr *vap = ap->a_vap;
2076 struct componentname *cnp = ap->a_cnp;
2077 struct nfs_vattr nvattr;
2078 struct nfsv2_sattr *sp;
2079 u_long *tl;
2080 caddr_t cp;
2081 long t1, t2;
2082 struct nfsnode *np = (struct nfsnode *)0;
2083 vnode_t newvp = (vnode_t)0;
2084 caddr_t bpos, dpos, cp2;
2085 int error = 0, wccpostattr = 0, gotvp = 0, fmode = 0;
2086 struct timespec premtime = { 0, 0 };
2087 mbuf_t mreq, mrep, md, mb, mb2;
2088 int v3 = NFS_ISV3(dvp);
2089 int gotuid, gotgid;
2090 u_int64_t xid;
2091 kauth_cred_t cred;
2092 proc_t p;
2093
2094 cred = vfs_context_ucred(ap->a_context);
2095 p = vfs_context_proc(ap->a_context);
2096
2097 if (!VATTR_IS_ACTIVE(vap, va_type))
2098 return (EINVAL);
2099
2100 /*
2101 * Oops, not for me..
2102 */
2103 if (vap->va_type == VSOCK)
2104 return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap, cred, p));
2105
2106 VATTR_SET_SUPPORTED(vap, va_mode);
2107 VATTR_SET_SUPPORTED(vap, va_uid);
2108 VATTR_SET_SUPPORTED(vap, va_gid);
2109 VATTR_SET_SUPPORTED(vap, va_data_size);
2110 VATTR_SET_SUPPORTED(vap, va_access_time);
2111 VATTR_SET_SUPPORTED(vap, va_modify_time);
2112 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
2113 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
2114
2115 if (vap->va_vaflags & VA_EXCLUSIVE)
2116 fmode |= O_EXCL;
2117 again:
2118 nfsm_reqhead(NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
2119 nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
2120 if (error)
2121 return (error);
2122 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_CREATE]);
2123 nfsm_fhtom(dvp, v3);
2124 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2125 if (v3) {
2126 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
2127 if (fmode & O_EXCL) {
2128 *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
2129 nfsm_build(tl, u_long *, NFSX_V3CREATEVERF);
2130 if (!TAILQ_EMPTY(&in_ifaddrhead))
2131 *tl++ = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr.s_addr;
2132 else
2133 *tl++ = create_verf;
2134 *tl = ++create_verf;
2135 } else {
2136 *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
2137 nfsm_v3sattr(vap);
2138 }
2139 } else {
2140 struct timespec neg1time = { -1, -1 };
2141 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
2142 sp->sa_mode = vtonfsv2_mode(vap->va_type,
2143 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
2144 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
2145 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
2146 sp->sa_size = 0;
2147 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
2148 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
2149 } else {
2150 txdr_nfsv2time(&neg1time, &sp->sa_atime);
2151 }
2152 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
2153 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
2154 } else {
2155 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2156 }
2157 }
2158 nfsm_request(dvp, NFSPROC_CREATE, p, cred, &xid);
2159 if (!error) {
2160 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2161 if (!gotvp) {
2162 error = nfs_lookitup(dvp, cnp->cn_nameptr,
2163 cnp->cn_namelen, cred, p, &np);
2164 if (!error)
2165 newvp = NFSTOV(np);
2166 }
2167 }
2168 if (v3 && mrep)
2169 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2170 nfsm_reqdone;
2171 if (error) {
2172 if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
2173 fmode &= ~O_EXCL;
2174 goto again;
2175 }
2176 if (newvp)
2177 vnode_put(newvp);
2178 } else if (v3 && (fmode & O_EXCL)) {
2179 error = nfs_setattrrpc(newvp, vap, cred, p);
2180 if (error && (gotuid || gotgid)) {
2181 /* it's possible the server didn't like our attempt to set IDs. */
2182 /* so, let's try it again without those */
2183 VATTR_CLEAR_ACTIVE(vap, va_uid);
2184 VATTR_CLEAR_ACTIVE(vap, va_gid);
2185 error = nfs_setattrrpc(newvp, vap, cred, p);
2186 }
2187 if (error)
2188 vnode_put(newvp);
2189 }
2190 if (!error) {
2191 *ap->a_vpp = newvp;
2192 }
2193 VTONFS(dvp)->n_flag |= NMODIFIED;
2194 /* if directory hadn't changed, update namecache mtime */
2195 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2196 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2197 if (!wccpostattr)
2198 NATTRINVALIDATE(VTONFS(dvp));
2199 if (!error && (gotuid || gotgid) &&
2200 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2201 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2202 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2203 /* clear ID bits if server didn't use them (or we can't tell) */
2204 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2205 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2206 }
2207 return (error);
2208 }
2209
2210 /*
2211 * nfs file remove call
2212 * To try and make nfs semantics closer to ufs semantics, a file that has
2213 * other processes using the vnode is renamed instead of removed and then
2214 * removed later on the last close.
2215 * - If vnode_isinuse()
2216 * If a rename is not already in the works
2217 * call nfs_sillyrename() to set it up
2218 * else
2219 * do the remove rpc
2220 */
2221 static int
2222 nfs_remove(ap)
2223 struct vnop_remove_args /* {
2224 struct vnodeop_desc *a_desc;
2225 vnode_t a_dvp;
2226 vnode_t a_vp;
2227 struct componentname *a_cnp;
2228 int a_flags;
2229 vfs_context_t a_context;
2230 } */ *ap;
2231 {
2232 vnode_t vp = ap->a_vp;
2233 vnode_t dvp = ap->a_dvp;
2234 struct componentname *cnp = ap->a_cnp;
2235 struct nfsnode *np = VTONFS(vp);
2236 int error = 0, gofree = 0;
2237 struct nfs_vattr nvattr;
2238 kauth_cred_t cred;
2239 proc_t p;
2240
2241 cred = vfs_context_ucred(ap->a_context);
2242 p = vfs_context_proc(ap->a_context);
2243
2244 gofree = vnode_isinuse(vp, 0) ? 0 : 1;
2245 if ((ap->a_flags & VNODE_REMOVE_NODELETEBUSY) && !gofree) {
2246 /* Caller requested Carbon delete semantics, but file is busy */
2247 return (EBUSY);
2248 }
2249 if (gofree || (np->n_sillyrename &&
2250 nfs_getattr(vp, &nvattr, cred, p) == 0 &&
2251 nvattr.nva_nlink > 1)) {
2252 /*
2253 * Purge the name cache so that the chance of a lookup for
2254 * the name succeeding while the remove is in progress is
2255 * minimized.
2256 */
2257 cache_purge(vp);
2258 /*
2259 * throw away biocache buffers, mainly to avoid
2260 * unnecessary delayed writes later.
2261 */
2262 error = nfs_vinvalbuf(vp, 0, cred, p, 1);
2263 np->n_size = 0;
2264 ubc_setsize(vp, (off_t)0); /* XXX check error */
2265 /* Do the rpc */
2266 if (error != EINTR)
2267 error = nfs_removerpc(dvp, cnp->cn_nameptr,
2268 cnp->cn_namelen, cred, p);
2269 /*
2270 * Kludge City: If the first reply to the remove rpc is lost..
2271 * the reply to the retransmitted request will be ENOENT
2272 * since the file was in fact removed
2273 * Therefore, we cheat and return success.
2274 */
2275 if (error == ENOENT)
2276 error = 0;
2277 if (!error) {
2278 /*
2279 * remove nfsnode from hash now so we can't accidentally find it
2280 * again if another object gets created with the same filehandle
2281 * before this vnode gets reclaimed
2282 */
2283 lck_mtx_lock(nfs_node_hash_mutex);
2284 LIST_REMOVE(np, n_hash);
2285 np->n_flag &= ~NHASHED;
2286 lck_mtx_unlock(nfs_node_hash_mutex);
2287 }
2288 if (!error && !np->n_sillyrename) {
2289 /* clear flags now: won't get nfs_inactive for recycled vnode */
2290 /* clear all flags other than these */
2291 np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
2292 vnode_recycle(vp);
2293 }
2294 } else if (!np->n_sillyrename) {
2295 error = nfs_sillyrename(dvp, vp, cnp, cred, p);
2296 }
2297 NATTRINVALIDATE(np);
2298
2299 return (error);
2300 }
2301
2302 /*
2303 * nfs file remove rpc called from nfs_inactive
2304 */
2305 int
2306 nfs_removeit(struct sillyrename *sp)
2307 {
2308 return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, NULL));
2309 }
2310
2311 /*
2312 * Nfs remove rpc, called from nfs_remove() and nfs_removeit().
2313 */
2314 static int
2315 nfs_removerpc(dvp, name, namelen, cred, proc)
2316 vnode_t dvp;
2317 char *name;
2318 int namelen;
2319 kauth_cred_t cred;
2320 proc_t proc;
2321 {
2322 register u_long *tl;
2323 register caddr_t cp;
2324 register long t1, t2;
2325 caddr_t bpos, dpos, cp2;
2326 int error = 0, wccpostattr = 0;
2327 struct timespec premtime = { 0, 0 };
2328 mbuf_t mreq, mrep, md, mb, mb2;
2329 int v3;
2330 u_int64_t xid;
2331
2332 if (!VFSTONFS(vnode_mount(dvp)))
2333 return (ENXIO);
2334 v3 = NFS_ISV3(dvp);
2335
2336 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
2337 if (error)
2338 return (error);
2339 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_REMOVE]);
2340 nfsm_fhtom(dvp, v3);
2341 nfsm_strtom(name, namelen, NFS_MAXNAMLEN, v3);
2342 nfsm_request(dvp, NFSPROC_REMOVE, proc, cred, &xid);
2343 if (v3 && mrep)
2344 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2345 nfsm_reqdone;
2346 VTONFS(dvp)->n_flag |= NMODIFIED;
2347 /* if directory hadn't changed, update namecache mtime */
2348 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2349 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2350 if (!wccpostattr)
2351 NATTRINVALIDATE(VTONFS(dvp));
2352 return (error);
2353 }
2354
2355 /*
2356 * nfs file rename call
2357 */
2358 static int
2359 nfs_rename(ap)
2360 struct vnop_rename_args /* {
2361 struct vnodeop_desc *a_desc;
2362 vnode_t a_fdvp;
2363 vnode_t a_fvp;
2364 struct componentname *a_fcnp;
2365 vnode_t a_tdvp;
2366 vnode_t a_tvp;
2367 struct componentname *a_tcnp;
2368 vfs_context_t a_context;
2369 } */ *ap;
2370 {
2371 vnode_t fvp = ap->a_fvp;
2372 vnode_t tvp = ap->a_tvp;
2373 vnode_t fdvp = ap->a_fdvp;
2374 vnode_t tdvp = ap->a_tdvp;
2375 struct componentname *tcnp = ap->a_tcnp;
2376 struct componentname *fcnp = ap->a_fcnp;
2377 int error, inuse=0;
2378 mount_t fmp, tdmp, tmp;
2379 struct nfsnode *tnp;
2380 kauth_cred_t cred;
2381 proc_t p;
2382
2383 cred = vfs_context_ucred(ap->a_context);
2384 p = vfs_context_proc(ap->a_context);
2385
2386 tnp = tvp ? VTONFS(tvp) : NULL;
2387
2388 /* Check for cross-device rename */
2389 fmp = vnode_mount(fvp);
2390 tmp = tvp ? vnode_mount(tvp) : NULL;
2391 tdmp = vnode_mount(tdvp);
2392 if ((fmp != tdmp) || (tvp && (fmp != tmp))) {
2393 error = EXDEV;
2394 goto out;
2395 }
2396
2397 /*
2398 * If the tvp exists and is in use, sillyrename it before doing the
2399 * rename of the new file over it.
2400 * XXX Can't sillyrename a directory.
2401 * Don't sillyrename if source and target are same vnode (hard
2402 * links or case-variants)
2403 */
2404 if (tvp && tvp != fvp) {
2405 inuse = vnode_isinuse(tvp, 0);
2406 }
2407 if (inuse && !tnp->n_sillyrename && vnode_vtype(tvp) != VDIR) {
2408 if ((error = nfs_sillyrename(tdvp, tvp, tcnp, cred, p))) {
2409 /* sillyrename failed. Instead of pressing on, return error */
2410 goto out; /* should not be ENOENT. */
2411 } else {
2412 /* sillyrename succeeded.*/
2413 tvp = NULL;
2414 }
2415 }
2416
2417 error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
2418 tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, cred, p);
2419
2420 /*
2421 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
2422 */
2423 if (error == ENOENT)
2424 error = 0;
2425
2426 if (!error && tvp && tvp != fvp && !tnp->n_sillyrename) {
2427 /*
2428 * remove nfsnode from hash now so we can't accidentally find it
2429 * again if another object gets created with the same filehandle
2430 * before this vnode gets reclaimed
2431 */
2432 lck_mtx_lock(nfs_node_hash_mutex);
2433 LIST_REMOVE(tnp, n_hash);
2434 tnp->n_flag &= ~NHASHED;
2435 lck_mtx_unlock(nfs_node_hash_mutex);
2436 }
2437
2438 /* purge the old name cache entries and enter the new one */
2439 cache_purge(fvp);
2440 if (tvp) {
2441 cache_purge(tvp);
2442 if (!error && !tnp->n_sillyrename) {
2443 /* clear flags now: won't get nfs_inactive for recycled vnode */
2444 /* clear all flags other than these */
2445 tnp->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
2446 vnode_recycle(tvp);
2447 }
2448 }
2449 if (!error)
2450 cache_enter(tdvp, fvp, tcnp);
2451
2452 out:
2453 /*
2454 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
2455 */
2456 if (error == ENOENT)
2457 error = 0;
2458 return (error);
2459 }
2460
2461 /*
2462 * Do an nfs rename rpc. Called from nfs_rename() and nfs_sillyrename().
2463 */
2464 static int
2465 nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
2466 vnode_t fdvp;
2467 char *fnameptr;
2468 int fnamelen;
2469 vnode_t tdvp;
2470 char *tnameptr;
2471 int tnamelen;
2472 kauth_cred_t cred;
2473 proc_t proc;
2474 {
2475 register u_long *tl;
2476 register caddr_t cp;
2477 register long t1, t2;
2478 caddr_t bpos, dpos, cp2;
2479 int error = 0, fwccpostattr = 0, twccpostattr = 0;
2480 struct timespec fpremtime = { 0, 0 }, tpremtime = { 0, 0 };
2481 mbuf_t mreq, mrep, md, mb, mb2;
2482 int v3;
2483 u_int64_t xid;
2484
2485 if (!VFSTONFS(vnode_mount(fdvp)))
2486 return (ENXIO);
2487 v3 = NFS_ISV3(fdvp);
2488
2489 nfsm_reqhead((NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
2490 nfsm_rndup(tnamelen));
2491 if (error)
2492 return (error);
2493 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RENAME]);
2494 nfsm_fhtom(fdvp, v3);
2495 nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN, v3);
2496 nfsm_fhtom(tdvp, v3);
2497 nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN, v3);
2498 nfsm_request(fdvp, NFSPROC_RENAME, proc, cred, &xid);
2499 if (v3 && mrep) {
2500 u_int64_t txid = xid;
2501
2502 nfsm_wcc_data(fdvp, &fpremtime, fwccpostattr, &xid);
2503 nfsm_wcc_data(tdvp, &tpremtime, twccpostattr, &txid);
2504 }
2505 nfsm_reqdone;
2506 VTONFS(fdvp)->n_flag |= NMODIFIED;
2507 /* if directory hadn't changed, update namecache mtime */
2508 if (nfstimespeccmp(&VTONFS(fdvp)->n_ncmtime, &fpremtime, ==))
2509 VTONFS(fdvp)->n_ncmtime = VTONFS(fdvp)->n_vattr.nva_mtime;
2510 if (!fwccpostattr)
2511 NATTRINVALIDATE(VTONFS(fdvp));
2512 VTONFS(tdvp)->n_flag |= NMODIFIED;
2513 /* if directory hadn't changed, update namecache mtime */
2514 if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &tpremtime, ==))
2515 VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
2516 if (!twccpostattr)
2517 NATTRINVALIDATE(VTONFS(tdvp));
2518 return (error);
2519 }
2520
2521 /*
2522 * nfs hard link create call
2523 */
2524 static int
2525 nfs_link(ap)
2526 struct vnop_link_args /* {
2527 struct vnodeop_desc *a_desc;
2528 vnode_t a_vp;
2529 vnode_t a_tdvp;
2530 struct componentname *a_cnp;
2531 vfs_context_t a_context;
2532 } */ *ap;
2533 {
2534 vnode_t vp = ap->a_vp;
2535 vnode_t tdvp = ap->a_tdvp;
2536 struct componentname *cnp = ap->a_cnp;
2537 u_long *tl;
2538 caddr_t cp;
2539 long t1, t2;
2540 caddr_t bpos, dpos, cp2;
2541 int error = 0, wccpostattr = 0, attrflag = 0;
2542 struct timespec premtime = { 0, 0 };
2543 mbuf_t mreq, mrep, md, mb, mb2;
2544 int v3;
2545 u_int64_t xid;
2546 kauth_cred_t cred;
2547 proc_t p;
2548
2549 if (vnode_mount(vp) != vnode_mount(tdvp)) {
2550 return (EXDEV);
2551 }
2552
2553 cred = vfs_context_ucred(ap->a_context);
2554 p = vfs_context_proc(ap->a_context);
2555
2556 v3 = NFS_ISV3(vp);
2557
2558 /*
2559 * Push all writes to the server, so that the attribute cache
2560 * doesn't get "out of sync" with the server.
2561 * XXX There should be a better way!
2562 */
2563 nfs_flush(vp, MNT_WAIT, cred, p, 0);
2564
2565 nfsm_reqhead(NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
2566 if (error)
2567 return (error);
2568 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LINK]);
2569 nfsm_fhtom(vp, v3);
2570 nfsm_fhtom(tdvp, v3);
2571 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2572 nfsm_request(vp, NFSPROC_LINK, p, cred, &xid);
2573 if (v3 && mrep) {
2574 u_int64_t txid = xid;
2575
2576 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
2577 nfsm_wcc_data(tdvp, &premtime, wccpostattr, &txid);
2578 }
2579 nfsm_reqdone;
2580
2581 VTONFS(tdvp)->n_flag |= NMODIFIED;
2582 if (!attrflag)
2583 NATTRINVALIDATE(VTONFS(vp));
2584 /* if directory hadn't changed, update namecache mtime */
2585 if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &premtime, ==))
2586 VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
2587 if (!wccpostattr)
2588 NATTRINVALIDATE(VTONFS(tdvp));
2589 /*
2590 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2591 */
2592 if (error == EEXIST)
2593 error = 0;
2594 return (error);
2595 }
2596
2597 /*
2598 * nfs symbolic link create call
2599 */
2600 static int
2601 nfs_symlink(ap)
2602 struct vnop_symlink_args /* {
2603 struct vnodeop_desc *a_desc;
2604 vnode_t a_dvp;
2605 vnode_t *a_vpp;
2606 struct componentname *a_cnp;
2607 struct vnode_attr *a_vap;
2608 char *a_target;
2609 vfs_context_t a_context;
2610 } */ *ap;
2611 {
2612 vnode_t dvp = ap->a_dvp;
2613 struct vnode_attr *vap = ap->a_vap;
2614 struct componentname *cnp = ap->a_cnp;
2615 struct nfs_vattr nvattr;
2616 struct nfsv2_sattr *sp;
2617 u_long *tl;
2618 caddr_t cp;
2619 long t1, t2;
2620 caddr_t bpos, dpos, cp2;
2621 int slen, error = 0, wccpostattr = 0, gotvp = 0;
2622 struct timespec premtime = { 0, 0 };
2623 mbuf_t mreq, mrep, md, mb, mb2;
2624 vnode_t newvp = (vnode_t)0;
2625 int v3 = NFS_ISV3(dvp);
2626 int gotuid, gotgid;
2627 u_int64_t xid;
2628 kauth_cred_t cred;
2629 proc_t p;
2630 struct nfsnode *np = NULL;
2631
2632 cred = vfs_context_ucred(ap->a_context);
2633 p = vfs_context_proc(ap->a_context);
2634
2635 slen = strlen(ap->a_target);
2636 nfsm_reqhead(NFSX_FH(v3) + 2*NFSX_UNSIGNED +
2637 nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
2638 if (error)
2639 return (error);
2640
2641 VATTR_SET_SUPPORTED(vap, va_mode);
2642 VATTR_SET_SUPPORTED(vap, va_uid);
2643 VATTR_SET_SUPPORTED(vap, va_gid);
2644 VATTR_SET_SUPPORTED(vap, va_data_size);
2645 VATTR_SET_SUPPORTED(vap, va_access_time);
2646 VATTR_SET_SUPPORTED(vap, va_modify_time);
2647 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
2648 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
2649
2650 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SYMLINK]);
2651 nfsm_fhtom(dvp, v3);
2652 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2653 if (v3) {
2654 nfsm_v3sattr(vap);
2655 }
2656 nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN, v3);
2657 if (!v3) {
2658 struct timespec neg1time = { -1, -1 };
2659 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
2660 sp->sa_mode = vtonfsv2_mode(VLNK,
2661 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
2662 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
2663 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
2664 sp->sa_size = nfs_xdrneg1;
2665 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
2666 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
2667 } else {
2668 txdr_nfsv2time(&neg1time, &sp->sa_atime);
2669 }
2670 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
2671 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
2672 } else {
2673 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2674 }
2675 }
2676 nfsm_request(dvp, NFSPROC_SYMLINK, p, cred, &xid);
2677 if (v3 && mrep) {
2678 u_int64_t dxid = xid;
2679
2680 if (!error)
2681 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2682 nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
2683 }
2684 nfsm_reqdone;
2685
2686 VTONFS(dvp)->n_flag |= NMODIFIED;
2687 /* if directory hadn't changed, update namecache mtime */
2688 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2689 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2690 if (!wccpostattr)
2691 NATTRINVALIDATE(VTONFS(dvp));
2692
2693 /*
2694 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
2695 * if we can succeed in looking up the symlink.
2696 */
2697 if ((error == EEXIST) || (!error && !gotvp)) {
2698 if (newvp) {
2699 vnode_put(newvp);
2700 newvp = NULL;
2701 }
2702 error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cred, p, &np);
2703 if (!error) {
2704 newvp = NFSTOV(np);
2705 if (vnode_vtype(newvp) != VLNK)
2706 error = EEXIST;
2707 }
2708 }
2709 if (!error && (gotuid || gotgid) &&
2710 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2711 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2712 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2713 /* clear ID bits if server didn't use them (or we can't tell) */
2714 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2715 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2716 }
2717 if (error) {
2718 if (newvp)
2719 vnode_put(newvp);
2720 } else {
2721 *ap->a_vpp = newvp;
2722 }
2723 return (error);
2724 }
2725
2726 /*
2727 * nfs make dir call
2728 */
2729 static int
2730 nfs_mkdir(ap)
2731 struct vnop_mkdir_args /* {
2732 struct vnodeop_desc *a_desc;
2733 vnode_t a_dvp;
2734 vnode_t *a_vpp;
2735 struct componentname *a_cnp;
2736 struct vnode_attr *a_vap;
2737 vfs_context_t a_context;
2738 } */ *ap;
2739 {
2740 vnode_t dvp = ap->a_dvp;
2741 struct vnode_attr *vap = ap->a_vap;
2742 struct componentname *cnp = ap->a_cnp;
2743 struct nfs_vattr nvattr;
2744 struct nfsv2_sattr *sp;
2745 u_long *tl;
2746 caddr_t cp;
2747 long t1, t2;
2748 int len;
2749 struct nfsnode *np = (struct nfsnode *)0;
2750 vnode_t newvp = (vnode_t)0;
2751 caddr_t bpos, dpos, cp2;
2752 int error = 0, wccpostattr = 0;
2753 struct timespec premtime = { 0, 0 };
2754 int gotvp = 0;
2755 mbuf_t mreq, mrep, md, mb, mb2;
2756 int v3 = NFS_ISV3(dvp);
2757 int gotuid, gotgid;
2758 u_int64_t xid, dxid;
2759 kauth_cred_t cred;
2760 proc_t p;
2761
2762 cred = vfs_context_ucred(ap->a_context);
2763 p = vfs_context_proc(ap->a_context);
2764
2765 len = cnp->cn_namelen;
2766 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
2767 if (error)
2768 return (error);
2769
2770 VATTR_SET_SUPPORTED(vap, va_mode);
2771 VATTR_SET_SUPPORTED(vap, va_uid);
2772 VATTR_SET_SUPPORTED(vap, va_gid);
2773 VATTR_SET_SUPPORTED(vap, va_data_size);
2774 VATTR_SET_SUPPORTED(vap, va_access_time);
2775 VATTR_SET_SUPPORTED(vap, va_modify_time);
2776 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
2777 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
2778
2779 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKDIR]);
2780 nfsm_fhtom(dvp, v3);
2781 nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
2782 if (v3) {
2783 nfsm_v3sattr(vap);
2784 } else {
2785 struct timespec neg1time = { -1, -1 };
2786 nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
2787 sp->sa_mode = vtonfsv2_mode(VDIR,
2788 (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
2789 sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
2790 sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
2791 sp->sa_size = nfs_xdrneg1;
2792 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
2793 txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
2794 } else {
2795 txdr_nfsv2time(&neg1time, &sp->sa_atime);
2796 }
2797 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
2798 txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
2799 } else {
2800 txdr_nfsv2time(&neg1time, &sp->sa_mtime);
2801 }
2802 }
2803 nfsm_request(dvp, NFSPROC_MKDIR, p, cred, &xid);
2804 dxid = xid;
2805 if (!error)
2806 nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
2807 if (v3 && mrep)
2808 nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
2809 nfsm_reqdone;
2810 VTONFS(dvp)->n_flag |= NMODIFIED;
2811 /* if directory hadn't changed, update namecache mtime */
2812 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2813 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2814 if (!wccpostattr)
2815 NATTRINVALIDATE(VTONFS(dvp));
2816 /*
2817 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
2818 * if we can succeed in looking up the directory.
2819 */
2820 if (error == EEXIST || (!error && !gotvp)) {
2821 if (newvp) {
2822 vnode_put(newvp);
2823 newvp = NULL;
2824 }
2825 error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cred, p, &np);
2826 if (!error) {
2827 newvp = NFSTOV(np);
2828 if (vnode_vtype(newvp) != VDIR)
2829 error = EEXIST;
2830 }
2831 }
2832 if (!error && (gotuid || gotgid) &&
2833 (!newvp || nfs_getattrcache(newvp, &nvattr) ||
2834 (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
2835 (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
2836 /* clear ID bits if server didn't use them (or we can't tell) */
2837 VATTR_CLEAR_SUPPORTED(vap, va_uid);
2838 VATTR_CLEAR_SUPPORTED(vap, va_gid);
2839 }
2840 if (error) {
2841 if (newvp)
2842 vnode_put(newvp);
2843 } else {
2844 *ap->a_vpp = newvp;
2845 }
2846 return (error);
2847 }
2848
2849 /*
2850 * nfs remove directory call
2851 */
2852 static int
2853 nfs_rmdir(ap)
2854 struct vnop_rmdir_args /* {
2855 struct vnodeop_desc *a_desc;
2856 vnode_t a_dvp;
2857 vnode_t a_vp;
2858 struct componentname *a_cnp;
2859 vfs_context_t a_context;
2860 } */ *ap;
2861 {
2862 vnode_t vp = ap->a_vp;
2863 vnode_t dvp = ap->a_dvp;
2864 struct componentname *cnp = ap->a_cnp;
2865 u_long *tl;
2866 caddr_t cp;
2867 long t1, t2;
2868 caddr_t bpos, dpos, cp2;
2869 int error = 0, wccpostattr = 0;
2870 struct timespec premtime = { 0, 0 };
2871 mbuf_t mreq, mrep, md, mb, mb2;
2872 int v3 = NFS_ISV3(dvp);
2873 u_int64_t xid;
2874 kauth_cred_t cred;
2875 proc_t p;
2876
2877 cred = vfs_context_ucred(ap->a_context);
2878 p = vfs_context_proc(ap->a_context);
2879
2880 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
2881 if (error)
2882 return (error);
2883 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RMDIR]);
2884 nfsm_fhtom(dvp, v3);
2885 nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
2886 nfsm_request(dvp, NFSPROC_RMDIR, p, cred, &xid);
2887 if (v3 && mrep)
2888 nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
2889 nfsm_reqdone;
2890 VTONFS(dvp)->n_flag |= NMODIFIED;
2891 /* if directory hadn't changed, update namecache mtime */
2892 if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
2893 VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
2894 if (!wccpostattr)
2895 NATTRINVALIDATE(VTONFS(dvp));
2896 cache_purge(vp);
2897 /*
2898 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2899 */
2900 if (error == ENOENT)
2901 error = 0;
2902 if (!error) {
2903 /*
2904 * remove nfsnode from hash now so we can't accidentally find it
2905 * again if another object gets created with the same filehandle
2906 * before this vnode gets reclaimed
2907 */
2908 lck_mtx_lock(nfs_node_hash_mutex);
2909 LIST_REMOVE(VTONFS(vp), n_hash);
2910 VTONFS(vp)->n_flag &= ~NHASHED;
2911 lck_mtx_unlock(nfs_node_hash_mutex);
2912 }
2913 return (error);
2914 }
2915
2916 /*
2917 * nfs readdir call
2918 */
2919 static int
2920 nfs_readdir(ap)
2921 struct vnop_readdir_args /* {
2922 struct vnodeop_desc *a_desc;
2923 vnode_t a_vp;
2924 struct uio *a_uio;
2925 int *a_eofflag;
2926 int *a_ncookies;
2927 u_long **a_cookies;
2928 vfs_context_t a_context;
2929 } */ *ap;
2930 {
2931 vnode_t vp = ap->a_vp;
2932 struct nfsnode *np = VTONFS(vp);
2933 struct uio *uio = ap->a_uio;
2934 int tresid, error;
2935 struct nfs_vattr nvattr;
2936 kauth_cred_t cred;
2937 proc_t p;
2938
2939 if (vnode_vtype(vp) != VDIR)
2940 return (EPERM);
2941
2942 cred = vfs_context_ucred(ap->a_context);
2943 p = vfs_context_proc(ap->a_context);
2944
2945 /*
2946 * First, check for hit on the EOF offset cache
2947 */
2948 if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
2949 (np->n_flag & NMODIFIED) == 0) {
2950 if (!nfs_getattr(vp, &nvattr, cred, p)) {
2951 if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, ==)) {
2952 OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_hits);
2953 return (0);
2954 }
2955 if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=)) {
2956 /* directory changed, purge any name cache entries */
2957 cache_purge(vp);
2958 }
2959 }
2960 }
2961
2962 /*
2963 * Call nfs_bioread() to do the real work.
2964 */
2965 // LP64todo - fix this
2966 tresid = uio_uio_resid(uio);
2967 error = nfs_bioread(vp, uio, 0, cred, p);
2968
2969 if (!error && uio_uio_resid(uio) == tresid)
2970 OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_misses);
2971 return (error);
2972 }
2973
2974 /*
2975 * Readdir rpc call.
2976 * Called from below the buffer cache by nfs_doio().
2977 */
2978 int
2979 nfs_readdirrpc(
2980 vnode_t vp,
2981 struct uio *uiop,
2982 kauth_cred_t cred,
2983 proc_t p)
2984 {
2985 register int len, skiplen, left;
2986 register struct dirent *dp;
2987 register u_long *tl;
2988 register caddr_t cp;
2989 register long t1, t2;
2990 register nfsuint64 *cookiep;
2991 caddr_t bpos, dpos, cp2;
2992 mbuf_t mreq, mrep, md, mb, mb2;
2993 nfsuint64 cookie;
2994 struct nfsmount *nmp;
2995 struct nfsnode *dnp = VTONFS(vp);
2996 u_quad_t fileno;
2997 int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2998 int attrflag;
2999 int v3, nmreaddirsize;
3000 u_int64_t xid;
3001
3002 #ifndef nolint
3003 dp = (struct dirent *)0;
3004 #endif
3005 #if DIAGNOSTIC
3006 if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) ||
3007 (uio_uio_resid(uiop) & (NFS_DIRBLKSIZ - 1)))
3008 panic("nfs_readdirrpc: bad uio");
3009 #endif
3010 nmp = VFSTONFS(vnode_mount(vp));
3011 if (!nmp)
3012 return (ENXIO);
3013 v3 = NFS_ISV3(vp);
3014 nmreaddirsize = nmp->nm_readdirsize;
3015
3016 /*
3017 * If there is no cookie, assume directory was stale.
3018 */
3019 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
3020 if (cookiep)
3021 cookie = *cookiep;
3022 else
3023 return (NFSERR_BAD_COOKIE);
3024 /*
3025 * Loop around doing readdir rpc's of size nm_readdirsize
3026 * truncated to a multiple of DIRBLKSIZ.
3027 * The stopping criteria is EOF or buffer full.
3028 */
3029 while (more_dirs && bigenough) {
3030 nfsm_reqhead(NFSX_FH(v3) + NFSX_READDIR(v3));
3031 if (error)
3032 goto nfsmout;
3033 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIR]);
3034 nfsm_fhtom(vp, v3);
3035 if (v3) {
3036 nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
3037 *tl++ = cookie.nfsuquad[0];
3038 *tl++ = cookie.nfsuquad[1];
3039 *tl++ = dnp->n_cookieverf.nfsuquad[0];
3040 *tl++ = dnp->n_cookieverf.nfsuquad[1];
3041 } else {
3042 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
3043 *tl++ = cookie.nfsuquad[0];
3044 }
3045 *tl = txdr_unsigned(nmreaddirsize);
3046 nfsm_request(vp, NFSPROC_READDIR, p, cred, &xid);
3047 if (v3) {
3048 if (mrep) {
3049 nfsm_postop_attr_update(vp, v3, attrflag, &xid);
3050 }
3051 if (!error) {
3052 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3053 dnp->n_cookieverf.nfsuquad[0] = *tl++;
3054 dnp->n_cookieverf.nfsuquad[1] = *tl;
3055 } else {
3056 mbuf_freem(mrep);
3057 goto nfsmout;
3058 }
3059 } else if (!mrep) {
3060 // XXX assert error?
3061 goto nfsmout;
3062 }
3063 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3064 more_dirs = fxdr_unsigned(int, *tl);
3065
3066 /* loop thru the dir entries, doctoring them to 4bsd form */
3067 while (more_dirs && bigenough) {
3068 if (v3) {
3069 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3070 fxdr_hyper(tl, &fileno);
3071 len = fxdr_unsigned(int, *(tl + 2));
3072 } else {
3073 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3074 fileno = fxdr_unsigned(u_quad_t, *tl++);
3075 len = fxdr_unsigned(int, *tl);
3076 }
3077 /* Note: v3 supports longer names, but struct dirent doesn't */
3078 /* so we just truncate the names to fit */
3079 if (len <= 0) {
3080 error = EBADRPC;
3081 mbuf_freem(mrep);
3082 goto nfsmout;
3083 }
3084 if (len > MAXNAMLEN) {
3085 skiplen = len - MAXNAMLEN;
3086 len = MAXNAMLEN;
3087 } else {
3088 skiplen = 0;
3089 }
3090 tlen = nfsm_rndup(len);
3091 if (tlen == len)
3092 tlen += 4; /* To ensure null termination */
3093 left = DIRBLKSIZ - blksiz;
3094 if ((tlen + (int)DIRHDSIZ) > left) {
3095 dp->d_reclen += left;
3096 uio_iov_base_add(uiop, left);
3097 uio_iov_len_add(uiop, -left);
3098 uiop->uio_offset += left;
3099 uio_uio_resid_add(uiop, -left);
3100 blksiz = 0;
3101 }
3102 if ((tlen + (int)DIRHDSIZ) > uio_uio_resid(uiop))
3103 bigenough = 0;
3104 if (bigenough) {
3105 // LP64todo - fix this!
3106 dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3107 dp->d_fileno = (int)fileno;
3108 dp->d_namlen = len;
3109 dp->d_reclen = tlen + DIRHDSIZ;
3110 dp->d_type = DT_UNKNOWN;
3111 blksiz += dp->d_reclen;
3112 if (blksiz == DIRBLKSIZ)
3113 blksiz = 0;
3114 uiop->uio_offset += DIRHDSIZ;
3115 #if LP64KERN
3116 uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
3117 uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
3118 #else
3119 uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
3120 uio_iov_len_add(uiop, -((int)DIRHDSIZ));
3121 #endif
3122 uio_iov_base_add(uiop, DIRHDSIZ);
3123 nfsm_mtouio(uiop, len);
3124 // LP64todo - fix this!
3125 cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3126 tlen -= len;
3127 *cp = '\0'; /* null terminate */
3128 uio_iov_base_add(uiop, tlen);
3129 uio_iov_len_add(uiop, -tlen);
3130 uiop->uio_offset += tlen;
3131 uio_uio_resid_add(uiop, -tlen);
3132 } else {
3133 nfsm_adv(nfsm_rndup(len));
3134 }
3135 if (skiplen)
3136 nfsm_adv(nfsm_rndup(skiplen));
3137 if (v3) {
3138 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3139 } else {
3140 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3141 }
3142 if (bigenough) {
3143 cookie.nfsuquad[0] = *tl++;
3144 if (v3)
3145 cookie.nfsuquad[1] = *tl++;
3146 } else if (v3)
3147 tl += 2;
3148 else
3149 tl++;
3150 more_dirs = fxdr_unsigned(int, *tl);
3151 }
3152 /*
3153 * If at end of rpc data, get the eof boolean
3154 */
3155 if (!more_dirs) {
3156 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3157 more_dirs = (fxdr_unsigned(int, *tl) == 0);
3158 }
3159 mbuf_freem(mrep);
3160 }
3161 /*
3162 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3163 * by increasing d_reclen for the last record.
3164 */
3165 if (blksiz > 0) {
3166 left = DIRBLKSIZ - blksiz;
3167 dp->d_reclen += left;
3168 uio_iov_base_add(uiop, left);
3169 uio_iov_len_add(uiop, -left);
3170 uiop->uio_offset += left;
3171 uio_uio_resid_add(uiop, -left);
3172 }
3173
3174 /*
3175 * We are now either at the end of the directory or have filled the
3176 * block.
3177 */
3178 if (bigenough)
3179 dnp->n_direofoffset = uiop->uio_offset;
3180 else {
3181 if (uio_uio_resid(uiop) > 0)
3182 printf("EEK! readdirrpc resid > 0\n");
3183 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
3184 if (cookiep)
3185 *cookiep = cookie;
3186 }
3187 nfsmout:
3188 return (error);
3189 }
3190
3191 /*
3192 * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc().
3193 */
3194 int
3195 nfs_readdirplusrpc(
3196 vnode_t vp,
3197 struct uio *uiop,
3198 kauth_cred_t cred,
3199 proc_t p)
3200 {
3201 int len, skiplen, left;
3202 struct dirent *dp;
3203 u_long *tl;
3204 caddr_t cp;
3205 long t1, t2;
3206 vnode_t newvp;
3207 nfsuint64 *cookiep;
3208 caddr_t bpos, dpos, cp2;
3209 mbuf_t mreq, mrep, md, mb, mb2;
3210 struct componentname cn, *cnp = &cn;
3211 nfsuint64 cookie;
3212 struct nfsmount *nmp;
3213 struct nfsnode *dnp = VTONFS(vp), *np;
3214 u_char *fhp;
3215 u_quad_t fileno;
3216 int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
3217 int attrflag, fhsize, nmreaddirsize, nmrsize;
3218 u_int64_t xid, savexid;
3219 struct nfs_vattr nvattr;
3220
3221 #ifndef nolint
3222 dp = (struct dirent *)0;
3223 #endif
3224 #if DIAGNOSTIC
3225 if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
3226 (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
3227 panic("nfs_readdirplusrpc: bad uio");
3228 #endif
3229 nmp = VFSTONFS(vnode_mount(vp));
3230 if (!nmp)
3231 return (ENXIO);
3232 nmreaddirsize = nmp->nm_readdirsize;
3233 nmrsize = nmp->nm_rsize;
3234
3235 bzero(cnp, sizeof(*cnp));
3236 newvp = NULLVP;
3237
3238 /*
3239 * If there is no cookie, assume directory was stale.
3240 */
3241 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
3242 if (cookiep)
3243 cookie = *cookiep;
3244 else
3245 return (NFSERR_BAD_COOKIE);
3246 /*
3247 * Loop around doing readdir rpc's of size nm_readdirsize
3248 * truncated to a multiple of DIRBLKSIZ.
3249 * The stopping criteria is EOF or buffer full.
3250 */
3251 while (more_dirs && bigenough) {
3252 nfsm_reqhead(NFSX_FH(1) + 6 * NFSX_UNSIGNED);
3253 if (error)
3254 goto nfsmout;
3255 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIRPLUS]);
3256 nfsm_fhtom(vp, 1);
3257 nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED);
3258 *tl++ = cookie.nfsuquad[0];
3259 *tl++ = cookie.nfsuquad[1];
3260 *tl++ = dnp->n_cookieverf.nfsuquad[0];
3261 *tl++ = dnp->n_cookieverf.nfsuquad[1];
3262 *tl++ = txdr_unsigned(nmreaddirsize);
3263 *tl = txdr_unsigned(nmrsize);
3264 nfsm_request(vp, NFSPROC_READDIRPLUS, p, cred, &xid);
3265 savexid = xid;
3266 if (mrep) {
3267 nfsm_postop_attr_update(vp, 1, attrflag, &xid);
3268 }
3269 if (error) {
3270 mbuf_freem(mrep);
3271 goto nfsmout;
3272 }
3273 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3274 dnp->n_cookieverf.nfsuquad[0] = *tl++;
3275 dnp->n_cookieverf.nfsuquad[1] = *tl++;
3276 more_dirs = fxdr_unsigned(int, *tl);
3277
3278 /* loop thru the dir entries, doctoring them to 4bsd form */
3279 while (more_dirs && bigenough) {
3280 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3281 fxdr_hyper(tl, &fileno);
3282 len = fxdr_unsigned(int, *(tl + 2));
3283 /* Note: v3 supports longer names, but struct dirent doesn't */
3284 /* so we just truncate the names to fit */
3285 if (len <= 0) {
3286 error = EBADRPC;
3287 mbuf_freem(mrep);
3288 goto nfsmout;
3289 }
3290 if (len > MAXNAMLEN) {
3291 skiplen = len - MAXNAMLEN;
3292 len = MAXNAMLEN;
3293 } else {
3294 skiplen = 0;
3295 }
3296 tlen = nfsm_rndup(len);
3297 if (tlen == len)
3298 tlen += 4; /* To ensure null termination*/
3299 left = DIRBLKSIZ - blksiz;
3300 if ((tlen + (int)DIRHDSIZ) > left) {
3301 dp->d_reclen += left;
3302 uio_iov_base_add(uiop, left);
3303 uio_iov_len_add(uiop, -left);
3304 uiop->uio_offset += left;
3305 uio_uio_resid_add(uiop, -left);
3306 blksiz = 0;
3307 }
3308 if ((tlen + (int)DIRHDSIZ) > uio_uio_resid(uiop))
3309 bigenough = 0;
3310 if (bigenough) {
3311 // LP64todo - fix this!
3312 dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3313 dp->d_fileno = (int)fileno;
3314 dp->d_namlen = len;
3315 dp->d_reclen = tlen + DIRHDSIZ;
3316 dp->d_type = DT_UNKNOWN;
3317 blksiz += dp->d_reclen;
3318 if (blksiz == DIRBLKSIZ)
3319 blksiz = 0;
3320 uiop->uio_offset += DIRHDSIZ;
3321 #if LP64KERN
3322 uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
3323 uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
3324 #else
3325 uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
3326 uio_iov_len_add(uiop, -((int)DIRHDSIZ));
3327 #endif
3328 uio_iov_base_add(uiop, DIRHDSIZ);
3329 // LP64todo - fix this!
3330 cnp->cn_nameptr = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3331 cnp->cn_namelen = len;
3332 nfsm_mtouio(uiop, len);
3333 cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3334 tlen -= len;
3335 *cp = '\0';
3336 uio_iov_base_add(uiop, tlen);
3337 uio_iov_len_add(uiop, -tlen);
3338 uiop->uio_offset += tlen;
3339 uio_uio_resid_add(uiop, -tlen);
3340 } else {
3341 nfsm_adv(nfsm_rndup(len));
3342 }
3343 if (skiplen)
3344 nfsm_adv(nfsm_rndup(skiplen));
3345 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
3346 if (bigenough) {
3347 cookie.nfsuquad[0] = *tl++;
3348 cookie.nfsuquad[1] = *tl++;
3349 } else
3350 tl += 2;
3351
3352 /*
3353 * Since the attributes are before the file handle
3354 * (sigh), we must skip over the attributes and then
3355 * come back and get them.
3356 */
3357 attrflag = fxdr_unsigned(int, *tl);
3358 if (attrflag) {
3359 /* grab attributes */
3360 nfsm_attr_get(1, &nvattr);
3361 dp->d_type = IFTODT(VTTOIF(nvattr.nva_type));
3362 /* check for file handle */
3363 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3364 doit = fxdr_unsigned(int, *tl);
3365 if (doit) {
3366 nfsm_getfh(fhp, fhsize, 1);
3367 if (NFS_CMPFH(dnp, fhp, fhsize)) {
3368 error = vnode_ref(vp);
3369 if (error) {
3370 doit = 0;
3371 } else {
3372 newvp = vp;
3373 np = dnp;
3374 }
3375 } else if (!bigenough ||
3376 (cnp->cn_namelen == 2 &&
3377 cnp->cn_nameptr[1] == '.' &&
3378 cnp->cn_nameptr[0] == '.')) {
3379 /*
3380 * XXXmacko I don't think this ".." thing is a problem anymore.
3381 * don't doit if we can't guarantee
3382 * that this entry is NOT ".." because
3383 * we would have to drop the lock on
3384 * the directory before getting the
3385 * lock on the ".." vnode... and we
3386 * don't want to drop the dvp lock in
3387 * the middle of a readdirplus.
3388 */
3389 doit = 0;
3390 } else {
3391 cnp->cn_hash = 0;
3392
3393 error = nfs_nget(vnode_mount(vp), vp, cnp,
3394 fhp, fhsize, &nvattr, &xid,
3395 NG_MAKEENTRY, &np);
3396 if (error)
3397 doit = 0;
3398 else
3399 newvp = NFSTOV(np);
3400 }
3401 }
3402 /* update attributes if not already updated */
3403 if (doit && bigenough && (np->n_xid <= savexid)) {
3404 xid = savexid;
3405 nfs_loadattrcache(np, &nvattr, &xid, 0);
3406 /* any error can be ignored */
3407 }
3408 } else {
3409 /* Just skip over the file handle */
3410 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3411 i = fxdr_unsigned(int, *tl);
3412 nfsm_adv(nfsm_rndup(i));
3413 }
3414 if (newvp != NULLVP) {
3415 if (newvp == vp)
3416 vnode_rele(newvp);
3417 else
3418 vnode_put(newvp);
3419 newvp = NULLVP;
3420 }
3421 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3422 more_dirs = fxdr_unsigned(int, *tl);
3423 }
3424 /*
3425 * If at end of rpc data, get the eof boolean
3426 */
3427 if (!more_dirs) {
3428 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
3429 more_dirs = (fxdr_unsigned(int, *tl) == 0);
3430 }
3431 mbuf_freem(mrep);
3432 }
3433 /*
3434 * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
3435 * by increasing d_reclen for the last record.
3436 */
3437 if (blksiz > 0) {
3438 left = DIRBLKSIZ - blksiz;
3439 dp->d_reclen += left;
3440 uio_iov_base_add(uiop, left);
3441 uio_iov_len_add(uiop, -left);
3442 uiop->uio_offset += left;
3443 uio_uio_resid_add(uiop, -left);
3444 }
3445
3446 /*
3447 * We are now either at the end of the directory or have filled the
3448 * block.
3449 */
3450 if (bigenough)
3451 dnp->n_direofoffset = uiop->uio_offset;
3452 else {
3453 if (uio_uio_resid(uiop) > 0)
3454 printf("EEK! readdirplusrpc resid > 0\n");
3455 cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
3456 if (cookiep)
3457 *cookiep = cookie;
3458 }
3459 nfsmout:
3460 return (error);
3461 }
3462
3463 /*
3464 * Silly rename. To make the NFS filesystem that is stateless look a little
3465 * more like the "ufs" a remove of an active vnode is translated to a rename
3466 * to a funny looking filename that is removed by nfs_inactive on the
3467 * nfsnode. There is the potential for another process on a different client
3468 * to create the same funny name between the nfs_lookitup() fails and the
3469 * nfs_rename() completes, but...
3470 */
3471
3472 /* format of "random" names and next name to try */
3473 /* (note: shouldn't exceed size of sillyrename.s_name) */
3474 static char sillyrename_name[] = ".nfsAAA%04x4.4";
3475
3476 static int
3477 nfs_sillyrename(
3478 vnode_t dvp,
3479 vnode_t vp,
3480 struct componentname *cnp,
3481 kauth_cred_t cred,
3482 proc_t p)
3483 {
3484 register struct sillyrename *sp;
3485 struct nfsnode *np;
3486 int error;
3487 short pid;
3488 kauth_cred_t tmpcred;
3489 int i, j, k;
3490
3491 cache_purge(vp);
3492 np = VTONFS(vp);
3493 #if DIAGNOSTIC
3494 if (vnode_vtype(vp) == VDIR)
3495 panic("nfs_sillyrename: dir");
3496 #endif
3497 MALLOC_ZONE(sp, struct sillyrename *,
3498 sizeof (struct sillyrename), M_NFSREQ, M_WAITOK);
3499 if (!sp)
3500 return (ENOMEM);
3501 kauth_cred_ref(cred);
3502 sp->s_cred = cred;
3503 sp->s_dvp = dvp;
3504 error = vnode_ref(dvp);
3505 if (error)
3506 goto bad_norele;
3507
3508 /* Fudge together a funny name */
3509 pid = proc_pid(p);
3510 sp->s_namlen = sprintf(sp->s_name, sillyrename_name, pid);
3511
3512 /* Try lookitups until we get one that isn't there */
3513 i = j = k = 0;
3514 while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, NULL) == 0) {
3515 if (sp->s_name[4]++ >= 'z')
3516 sp->s_name[4] = 'A';
3517 if (++i > ('z' - 'A' + 1)) {
3518 i = 0;
3519 if (sp->s_name[5]++ >= 'z')
3520 sp->s_name[5] = 'A';
3521 if (++j > ('z' - 'A' + 1)) {
3522 j = 0;
3523 if (sp->s_name[6]++ >= 'z')
3524 sp->s_name[6] = 'A';
3525 if (++k > ('z' - 'A' + 1)) {
3526 error = EINVAL;
3527 goto bad;
3528 }
3529 }
3530 }
3531 }
3532 /* make note of next "random" name to try */
3533 if ((sillyrename_name[4] = (sp->s_name[4] + 1)) > 'z') {
3534 sillyrename_name[4] = 'A';
3535 if ((sillyrename_name[5] = (sp->s_name[5] + 1)) > 'z') {
3536 sillyrename_name[5] = 'A';
3537 if ((sillyrename_name[6] = (sp->s_name[6] + 1)) > 'z')
3538 sillyrename_name[6] = 'A';
3539 }
3540 }
3541 /* now, do the rename */
3542 error = nfs_renamerpc(dvp, cnp->cn_nameptr, cnp->cn_namelen,
3543 dvp, sp->s_name, sp->s_namlen, sp->s_cred, p);
3544 if (error)
3545 goto bad;
3546 error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, &np);
3547 #if DIAGNOSTIC
3548 kprintf("sillyrename: %s, vp=%x, np=%x, dvp=%x\n",
3549 &sp->s_name[0], (unsigned)vp, (unsigned)np, (unsigned)dvp);
3550 #endif
3551 np->n_sillyrename = sp;
3552 return (0);
3553 bad:
3554 vnode_rele(sp->s_dvp);
3555 bad_norele:
3556 tmpcred = sp->s_cred;
3557 sp->s_cred = NOCRED;
3558 kauth_cred_rele(tmpcred);
3559 FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
3560 return (error);
3561 }
3562
3563 /*
3564 * Look up a file name and optionally either update the file handle or
3565 * allocate an nfsnode, depending on the value of npp.
3566 * npp == NULL --> just do the lookup
3567 * *npp == NULL --> allocate a new nfsnode and make sure attributes are
3568 * handled too
3569 * *npp != NULL --> update the file handle in the vnode
3570 */
3571 static int
3572 nfs_lookitup(dvp, name, len, cred, procp, npp)
3573 vnode_t dvp;
3574 char *name;
3575 int len;
3576 kauth_cred_t cred;
3577 proc_t procp;
3578 struct nfsnode **npp;
3579 {
3580 u_long *tl;
3581 caddr_t cp;
3582 long t1, t2;
3583 vnode_t newvp = (vnode_t)0;
3584 struct nfsnode *np, *dnp = VTONFS(dvp);
3585 caddr_t bpos, dpos, cp2;
3586 int error = 0, fhlen, attrflag;
3587 mbuf_t mreq, mrep, md, mb, mb2;
3588 u_char *nfhp;
3589 int v3;
3590 u_int64_t xid, dxid, savedxid;
3591 struct nfs_vattr nvattr;
3592
3593 if (!VFSTONFS(vnode_mount(dvp)))
3594 return (ENXIO);
3595 v3 = NFS_ISV3(dvp);
3596
3597 nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
3598 if (error)
3599 return (error);
3600 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
3601 nfsm_fhtom(dvp, v3);
3602 nfsm_strtom(name, len, NFS_MAXNAMLEN, v3);
3603 nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred, &xid);
3604 if (npp && !error) {
3605 savedxid = xid;
3606 nfsm_getfh(nfhp, fhlen, v3);
3607 /* get attributes */
3608 if (v3) {
3609 nfsm_postop_attr_get(v3, attrflag, &nvattr);
3610 if (!attrflag) {
3611 /* We need valid attributes in order */
3612 /* to call nfs_nget/vnode_create(). */
3613 error = nfs_getattr_no_vnode(vnode_mount(dvp),
3614 nfhp, fhlen, cred, procp, &nvattr, &xid);
3615 if (error) {
3616 mbuf_freem(mrep);
3617 goto nfsmout;
3618 }
3619 }
3620 dxid = savedxid;
3621 nfsm_postop_attr_update(dvp, v3, attrflag, &dxid);
3622 } else {
3623 nfsm_attr_get(v3, &nvattr);
3624 }
3625 if (*npp) {
3626 np = *npp;
3627 if (fhlen != np->n_fhsize) {
3628 u_char *oldbuf = (np->n_fhsize > NFS_SMALLFH) ? np->n_fhp : NULL;
3629 if (fhlen > NFS_SMALLFH) {
3630 MALLOC_ZONE(np->n_fhp, u_char *, fhlen, M_NFSBIGFH, M_WAITOK);
3631 if (!np->n_fhp) {
3632 np->n_fhp = oldbuf;
3633 error = ENOMEM;
3634 mbuf_freem(mrep);
3635 goto nfsmout;
3636 }
3637 } else {
3638 np->n_fhp = &np->n_fh[0];
3639 }
3640 if (oldbuf) {
3641 FREE_ZONE(oldbuf, np->n_fhsize, M_NFSBIGFH);
3642 }
3643 }
3644 bcopy(nfhp, np->n_fhp, fhlen);
3645 np->n_fhsize = fhlen;
3646 newvp = NFSTOV(np);
3647 error = nfs_loadattrcache(np, &nvattr, &xid, 0);
3648 if (error) {
3649 mbuf_freem(mrep);
3650 goto nfsmout;
3651 }
3652 } else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
3653 newvp = dvp;
3654 if (dnp->n_xid <= savedxid) {
3655 dxid = savedxid;
3656 error = nfs_loadattrcache(dnp, &nvattr, &dxid, 0);
3657 if (error) {
3658 mbuf_freem(mrep);
3659 goto nfsmout;
3660 }
3661 }
3662 } else {
3663 struct componentname cn, *cnp = &cn;
3664 bzero(cnp, sizeof(*cnp));
3665 cnp->cn_nameptr = name;
3666 cnp->cn_namelen = len;
3667
3668 error = nfs_nget(vnode_mount(dvp), dvp, cnp, nfhp, fhlen,
3669 &nvattr, &xid, NG_MAKEENTRY, &np);
3670 if (error) {
3671 mbuf_freem(mrep);
3672 return (error);
3673 }
3674 newvp = NFSTOV(np);
3675 }
3676 }
3677 nfsm_reqdone;
3678 if (npp && *npp == NULL) {
3679 if (error) {
3680 if (newvp) {
3681 if (newvp == dvp)
3682 vnode_rele(newvp);
3683 else
3684 vnode_put(newvp);
3685 }
3686 } else
3687 *npp = np;
3688 }
3689 return (error);
3690 }
3691
3692 /*
3693 * Nfs Version 3 commit rpc
3694 */
3695 int
3696 nfs_commit(vp, offset, count, cred, procp)
3697 vnode_t vp;
3698 u_quad_t offset;
3699 u_int32_t count;
3700 kauth_cred_t cred;
3701 proc_t procp;
3702 {
3703 caddr_t cp;
3704 u_long *tl;
3705 int t1, t2;
3706 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3707 caddr_t bpos, dpos, cp2;
3708 int error = 0, wccpostattr = 0;
3709 struct timespec premtime = { 0, 0 };
3710 mbuf_t mreq, mrep, md, mb, mb2;
3711 u_int64_t xid;
3712
3713 FSDBG(521, vp, offset, count, nmp->nm_state);
3714 if (!nmp)
3715 return (ENXIO);
3716 if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0)
3717 return (0);
3718 nfsm_reqhead(NFSX_FH(1));
3719 if (error)
3720 return (error);
3721 OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_COMMIT]);
3722 nfsm_fhtom(vp, 1);
3723 nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
3724 txdr_hyper(&offset, tl);
3725 tl += 2;
3726 *tl = txdr_unsigned(count);
3727 nfsm_request(vp, NFSPROC_COMMIT, procp, cred, &xid);
3728 if (mrep) {
3729 nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
3730 /* XXX can we do anything useful with the wcc info? */
3731 }
3732 if (!error) {
3733 nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF);
3734 if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl,
3735 NFSX_V3WRITEVERF)) {
3736 bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
3737 NFSX_V3WRITEVERF);
3738 error = NFSERR_STALEWRITEVERF;
3739 }
3740 }
3741 nfsm_reqdone;
3742 return (error);
3743 }
3744
3745 static int
3746 nfs_blockmap(
3747 __unused struct vnop_blockmap_args /* {
3748 struct vnodeop_desc *a_desc;
3749 vnode_t a_vp;
3750 off_t a_foffset;
3751 size_t a_size;
3752 daddr64_t *a_bpn;
3753 size_t *a_run;
3754 void *a_poff;
3755 int a_flags;
3756 } */ *ap)
3757 {
3758 return (ENOTSUP);
3759 }
3760
3761 /*
3762 * Mmap a file
3763 *
3764 * NB Currently unsupported.
3765 */
3766 /*ARGSUSED*/
3767 static int
3768 nfs_mmap(
3769 __unused struct vnop_mmap_args /* {
3770 struct vnodeop_desc *a_desc;
3771 vnode_t a_vp;
3772 int a_fflags;
3773 kauth_cred_t a_cred;
3774 proc_t a_p;
3775 } */ *ap)
3776 {
3777
3778 return (EINVAL);
3779 }
3780
3781 /*
3782 * fsync vnode op. Just call nfs_flush() with commit == 1.
3783 */
3784 /* ARGSUSED */
3785 static int
3786 nfs_fsync(ap)
3787 struct vnop_fsync_args /* {
3788 struct vnodeop_desc *a_desc;
3789 vnode_t a_vp;
3790 int a_waitfor;
3791 vfs_context_t a_context;
3792 } */ *ap;
3793 {
3794 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
3795 proc_t p = vfs_context_proc(ap->a_context);
3796 struct nfsnode *np = VTONFS(ap->a_vp);
3797 int error;
3798
3799 np->n_flag |= NWRBUSY;
3800 error = nfs_flush(ap->a_vp, ap->a_waitfor, cred, p, 0);
3801 np->n_flag &= ~NWRBUSY;
3802 return (error);
3803 }
3804
3805 int
3806 nfs_flushcommits(vnode_t vp, proc_t p, int nowait)
3807 {
3808 struct nfsnode *np = VTONFS(vp);
3809 struct nfsbuf *bp;
3810 struct nfsbuflists blist, commitlist;
3811 int error = 0, retv, wcred_set, flags;
3812 u_quad_t off, endoff, toff;
3813 u_int32_t count;
3814 kauth_cred_t wcred = NULL;
3815
3816 FSDBG_TOP(557, vp, np, 0, 0);
3817
3818 /*
3819 * A nb_flags == (NB_DELWRI | NB_NEEDCOMMIT) block has been written to the
3820 * server, but nas not been committed to stable storage on the server
3821 * yet. The byte range is worked out for as many nfsbufs as we can handle
3822 * and the commit rpc is done.
3823 */
3824 if (!LIST_EMPTY(&np->n_dirtyblkhd))
3825 np->n_flag |= NMODIFIED;
3826
3827 off = (u_quad_t)-1;
3828 endoff = 0;
3829 wcred_set = 0;
3830 LIST_INIT(&commitlist);
3831
3832 if (!VFSTONFS(vnode_mount(vp))) {
3833 error = ENXIO;
3834 goto done;
3835 }
3836 if (!NFS_ISV3(vp)) {
3837 error = EINVAL;
3838 goto done;
3839 }
3840
3841 flags = NBI_DIRTY;
3842 if (nowait)
3843 flags |= NBI_NOWAIT;
3844 lck_mtx_lock(nfs_buf_mutex);
3845 if (!nfs_buf_iterprepare(np, &blist, flags)) {
3846 while ((bp = LIST_FIRST(&blist))) {
3847 LIST_REMOVE(bp, nb_vnbufs);
3848 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
3849 error = nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0);
3850 if (error)
3851 continue;
3852 if (((bp->nb_flags & (NB_DELWRI | NB_NEEDCOMMIT))
3853 != (NB_DELWRI | NB_NEEDCOMMIT))) {
3854 nfs_buf_drop(bp);
3855 continue;
3856 }
3857 nfs_buf_remfree(bp);
3858 lck_mtx_unlock(nfs_buf_mutex);
3859 /*
3860 * we need a upl to see if the page has been
3861 * dirtied (think mmap) since the unstable write, and
3862 * also to prevent vm from paging it during our commit rpc
3863 */
3864 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
3865 retv = nfs_buf_upl_setup(bp);
3866 if (retv) {
3867 /* unable to create upl */
3868 /* vm object must no longer exist */
3869 /* this could be fatal if we need */
3870 /* to write the data again, we'll see... */
3871 printf("nfs_flushcommits: upl create failed %d\n", retv);
3872 bp->nb_valid = bp->nb_dirty = 0;
3873 }
3874 }
3875 nfs_buf_upl_check(bp);
3876 lck_mtx_lock(nfs_buf_mutex);
3877
3878 FSDBG(557, bp, bp->nb_flags, bp->nb_valid, bp->nb_dirty);
3879 FSDBG(557, bp->nb_validoff, bp->nb_validend,
3880 bp->nb_dirtyoff, bp->nb_dirtyend);
3881
3882 /*
3883 * We used to check for dirty pages here; if there were any
3884 * we'd abort the commit and force the entire buffer to be
3885 * written again.
3886 *
3887 * Instead of doing that, we now go ahead and commit the dirty
3888 * range, and then leave the buffer around with dirty pages
3889 * that will be written out later.
3890 */
3891
3892 /*
3893 * Work out if all buffers are using the same cred
3894 * so we can deal with them all with one commit.
3895 *
3896 * XXX creds in bp's must be obtained by kauth_cred_ref on
3897 * the same original cred in order for them to be equal.
3898 */
3899 if (wcred_set == 0) {
3900 wcred = bp->nb_wcred;
3901 if (wcred == NOCRED)
3902 panic("nfs: needcommit w/out wcred");
3903 wcred_set = 1;
3904 } else if ((wcred_set == 1) && wcred != bp->nb_wcred) {
3905 wcred_set = -1;
3906 }
3907 SET(bp->nb_flags, NB_WRITEINPROG);
3908
3909 /*
3910 * A list of these buffers is kept so that the
3911 * second loop knows which buffers have actually
3912 * been committed. This is necessary, since there
3913 * may be a race between the commit rpc and new
3914 * uncommitted writes on the file.
3915 */
3916 LIST_REMOVE(bp, nb_vnbufs);
3917 LIST_INSERT_HEAD(&commitlist, bp, nb_vnbufs);
3918 toff = NBOFF(bp) + bp->nb_dirtyoff;
3919 if (toff < off)
3920 off = toff;
3921 toff += (u_quad_t)(bp->nb_dirtyend - bp->nb_dirtyoff);
3922 if (toff > endoff)
3923 endoff = toff;
3924 }
3925 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
3926 }
3927 lck_mtx_unlock(nfs_buf_mutex);
3928
3929 if (LIST_EMPTY(&commitlist)) {
3930 error = ENOBUFS;
3931 goto done;
3932 }
3933
3934 /*
3935 * Commit data on the server, as required.
3936 * If all bufs are using the same wcred, then use that with
3937 * one call for all of them, otherwise commit each one
3938 * separately.
3939 */
3940 if (wcred_set == 1) {
3941 /*
3942 * Note, it's possible the commit range could be >2^32-1.
3943 * If it is, we'll send one commit that covers the whole file.
3944 */
3945 if ((endoff - off) > 0xffffffff)
3946 count = 0;
3947 else
3948 count = (endoff - off);
3949 retv = nfs_commit(vp, off, count, wcred, p);
3950 } else {
3951 retv = 0;
3952 LIST_FOREACH(bp, &commitlist, nb_vnbufs) {
3953 toff = NBOFF(bp) + bp->nb_dirtyoff;
3954 count = bp->nb_dirtyend - bp->nb_dirtyoff;
3955 retv = nfs_commit(vp, toff, count, bp->nb_wcred, p);
3956 if (retv)
3957 break;
3958 }
3959 }
3960 if (retv == NFSERR_STALEWRITEVERF)
3961 nfs_clearcommit(vnode_mount(vp));
3962
3963 /*
3964 * Now, either mark the blocks I/O done or mark the
3965 * blocks dirty, depending on whether the commit
3966 * succeeded.
3967 */
3968 while ((bp = LIST_FIRST(&commitlist))) {
3969 LIST_REMOVE(bp, nb_vnbufs);
3970 FSDBG(557, bp, retv, bp->nb_flags, bp->nb_dirty);
3971 CLR(bp->nb_flags, (NB_NEEDCOMMIT | NB_WRITEINPROG));
3972 np->n_needcommitcnt--;
3973 CHECK_NEEDCOMMITCNT(np);
3974
3975 if (retv) {
3976 /* move back to dirty list */
3977 lck_mtx_lock(nfs_buf_mutex);
3978 LIST_INSERT_HEAD(&VTONFS(vp)->n_dirtyblkhd, bp, nb_vnbufs);
3979 lck_mtx_unlock(nfs_buf_mutex);
3980 nfs_buf_release(bp, 1);
3981 continue;
3982 }
3983
3984 vnode_startwrite(vp);
3985 if (ISSET(bp->nb_flags, NB_DELWRI)) {
3986 OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
3987 NFSBUFCNTCHK(0);
3988 wakeup(&nfs_nbdwrite);
3989 }
3990 CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
3991 /* if block still has dirty pages, we don't want it to */
3992 /* be released in nfs_buf_iodone(). So, don't set NB_ASYNC. */
3993 if (!bp->nb_dirty)
3994 SET(bp->nb_flags, NB_ASYNC);
3995
3996 /* move to clean list */
3997 lck_mtx_lock(nfs_buf_mutex);
3998 LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
3999 lck_mtx_unlock(nfs_buf_mutex);
4000
4001 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
4002
4003 nfs_buf_iodone(bp);
4004 if (bp->nb_dirty) {
4005 /* throw it back in as a delayed write buffer */
4006 CLR(bp->nb_flags, NB_DONE);
4007 nfs_buf_write_delayed(bp, p);
4008 }
4009 }
4010
4011 done:
4012 FSDBG_BOT(557, vp, np, 0, error);
4013 return (error);
4014 }
4015
4016 /*
4017 * Flush all the blocks associated with a vnode.
4018 * Walk through the buffer pool and push any dirty pages
4019 * associated with the vnode.
4020 */
4021 int
4022 nfs_flush(
4023 vnode_t vp,
4024 int waitfor,
4025 __unused kauth_cred_t cred,
4026 proc_t p,
4027 int ignore_writeerr)
4028 {
4029 struct nfsnode *np = VTONFS(vp);
4030 struct nfsbuf *bp;
4031 struct nfsbuflists blist;
4032 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4033 int error = 0, error2, slptimeo = 0, slpflag = 0;
4034 int flags, passone = 1;
4035
4036 FSDBG_TOP(517, vp, np, waitfor, 0);
4037
4038 if (!nmp) {
4039 error = ENXIO;
4040 goto done;
4041 }
4042 if (nmp->nm_flag & NFSMNT_INT)
4043 slpflag = PCATCH;
4044
4045 /*
4046 * On the first pass, start async/unstable writes on all
4047 * delayed write buffers. Then wait for all writes to complete
4048 * and call nfs_flushcommits() to commit any uncommitted buffers.
4049 * On all subsequent passes, start STABLE writes on any remaining
4050 * dirty buffers. Then wait for all writes to complete.
4051 */
4052 again:
4053 lck_mtx_lock(nfs_buf_mutex);
4054 FSDBG(518, LIST_FIRST(&np->n_dirtyblkhd), np->n_flag, 0, 0);
4055 if (!LIST_EMPTY(&np->n_dirtyblkhd))
4056 np->n_flag |= NMODIFIED;
4057 if (!VFSTONFS(vnode_mount(vp))) {
4058 lck_mtx_unlock(nfs_buf_mutex);
4059 error = ENXIO;
4060 goto done;
4061 }
4062
4063 /* Start/do any write(s) that are required. */
4064 if (!nfs_buf_iterprepare(np, &blist, NBI_DIRTY)) {
4065 while ((bp = LIST_FIRST(&blist))) {
4066 LIST_REMOVE(bp, nb_vnbufs);
4067 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
4068 flags = (passone || (waitfor != MNT_WAIT)) ? NBAC_NOWAIT : 0;
4069 if (flags != NBAC_NOWAIT)
4070 nfs_buf_refget(bp);
4071 while ((error = nfs_buf_acquire(bp, flags, slpflag, slptimeo))) {
4072 FSDBG(524, bp, flags, bp->nb_lflags, bp->nb_flags);
4073 if (error == EBUSY)
4074 break;
4075 if (error) {
4076 error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
4077 if (error2) {
4078 if (flags != NBAC_NOWAIT)
4079 nfs_buf_refrele(bp);
4080 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
4081 lck_mtx_unlock(nfs_buf_mutex);
4082 error = error2;
4083 goto done;
4084 }
4085 if (slpflag == PCATCH) {
4086 slpflag = 0;
4087 slptimeo = 2 * hz;
4088 }
4089 }
4090 }
4091 if (flags != NBAC_NOWAIT)
4092 nfs_buf_refrele(bp);
4093 if (error == EBUSY)
4094 continue;
4095 if (!bp->nb_vp) {
4096 /* buffer is no longer valid */
4097 nfs_buf_drop(bp);
4098 continue;
4099 }
4100 if (!ISSET(bp->nb_flags, NB_DELWRI))
4101 panic("nfs_flush: not dirty");
4102 FSDBG(525, bp, passone, bp->nb_lflags, bp->nb_flags);
4103 if ((passone || (waitfor != MNT_WAIT)) &&
4104 ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
4105 nfs_buf_drop(bp);
4106 continue;
4107 }
4108 nfs_buf_remfree(bp);
4109 lck_mtx_unlock(nfs_buf_mutex);
4110 if (ISSET(bp->nb_flags, NB_ERROR)) {
4111 np->n_error = bp->nb_error ? bp->nb_error : EIO;
4112 np->n_flag |= NWRITEERR;
4113 nfs_buf_release(bp, 1);
4114 lck_mtx_lock(nfs_buf_mutex);
4115 continue;
4116 }
4117 SET(bp->nb_flags, NB_ASYNC);
4118 if (!passone) {
4119 /* NB_STABLE forces this to be written FILESYNC */
4120 SET(bp->nb_flags, NB_STABLE);
4121 }
4122 nfs_buf_write(bp);
4123 lck_mtx_lock(nfs_buf_mutex);
4124 }
4125 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
4126 }
4127 lck_mtx_unlock(nfs_buf_mutex);
4128
4129 if (waitfor == MNT_WAIT) {
4130 while ((error = vnode_waitforwrites(vp, 0, slpflag, slptimeo, "nfsflush"))) {
4131 error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
4132 if (error2) {
4133 error = error2;
4134 goto done;
4135 }
4136 if (slpflag == PCATCH) {
4137 slpflag = 0;
4138 slptimeo = 2 * hz;
4139 }
4140 }
4141 }
4142
4143 if (NFS_ISV3(vp)) {
4144 /* loop while it looks like there are still buffers to be */
4145 /* commited and nfs_flushcommits() seems to be handling them. */
4146 while (np->n_needcommitcnt)
4147 if (nfs_flushcommits(vp, p, 0))
4148 break;
4149 }
4150
4151 if (passone) {
4152 passone = 0;
4153 goto again;
4154 }
4155
4156 if (waitfor == MNT_WAIT) {
4157 if (!LIST_EMPTY(&np->n_dirtyblkhd))
4158 goto again;
4159 /* if we have no dirty blocks, we can clear the modified flag */
4160 np->n_flag &= ~NMODIFIED;
4161 }
4162
4163 FSDBG(526, np->n_flag, np->n_error, 0, 0);
4164 if (!ignore_writeerr && (np->n_flag & NWRITEERR)) {
4165 error = np->n_error;
4166 np->n_flag &= ~NWRITEERR;
4167 }
4168 done:
4169 FSDBG_BOT(517, vp, np, error, 0);
4170 return (error);
4171 }
4172
4173 /*
4174 * Do an nfs pathconf rpc.
4175 */
4176 int
4177 nfs_pathconfrpc(
4178 vnode_t vp,
4179 struct nfsv3_pathconf *pc,
4180 kauth_cred_t cred,
4181 proc_t procp)
4182 {
4183 mbuf_t mreq, mrep, md, mb, mb2;
4184 caddr_t bpos, dpos, cp, cp2;
4185 int32_t t1, t2;
4186 u_long *tl;
4187 u_int64_t xid;
4188 int attrflag, error = 0;
4189 struct nfsv3_pathconf *mpc;
4190
4191 /* fetch pathconf info from server */
4192 nfsm_reqhead(NFSX_FH(1));
4193 if (error)
4194 return (error);
4195 nfsm_fhtom(vp, 1);
4196 nfsm_request(vp, NFSPROC_PATHCONF, procp, cred, &xid);
4197 nfsm_postop_attr_update(vp, 1, attrflag, &xid);
4198 if (!error) {
4199 nfsm_dissect(mpc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
4200 pc->pc_linkmax = fxdr_unsigned(long, mpc->pc_linkmax);
4201 pc->pc_namemax = fxdr_unsigned(long, mpc->pc_namemax);
4202 pc->pc_chownrestricted = fxdr_unsigned(long, mpc->pc_chownrestricted);
4203 pc->pc_notrunc = fxdr_unsigned(long, mpc->pc_notrunc);
4204 pc->pc_caseinsensitive = fxdr_unsigned(long, mpc->pc_caseinsensitive);
4205 pc->pc_casepreserving = fxdr_unsigned(long, mpc->pc_casepreserving);
4206 }
4207 nfsm_reqdone;
4208
4209 return (error);
4210 }
4211
4212 void
4213 nfs_pathconf_cache(struct nfsmount *nmp, struct nfsv3_pathconf *pc)
4214 {
4215 nmp->nm_state |= NFSSTA_GOTPATHCONF;
4216 nmp->nm_fsinfo.linkmax = pc->pc_linkmax;
4217 nmp->nm_fsinfo.namemax = pc->pc_namemax;
4218 nmp->nm_fsinfo.pcflags = 0;
4219 if (pc->pc_notrunc)
4220 nmp->nm_fsinfo.pcflags |= NFSPCINFO_NOTRUNC;
4221 if (pc->pc_chownrestricted)
4222 nmp->nm_fsinfo.pcflags |= NFSPCINFO_CHOWN_RESTRICTED;
4223 if (pc->pc_caseinsensitive)
4224 nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_INSENSITIVE;
4225 if (pc->pc_casepreserving)
4226 nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_PRESERVING;
4227 }
4228
4229 /*
4230 * Return POSIX pathconf information applicable to nfs.
4231 *
4232 * The NFS V2 protocol doesn't support this, so just return EINVAL
4233 * for V2.
4234 */
4235 /* ARGSUSED */
4236 static int
4237 nfs_pathconf(ap)
4238 struct vnop_pathconf_args /* {
4239 struct vnodeop_desc *a_desc;
4240 vnode_t a_vp;
4241 int a_name;
4242 register_t *a_retval;
4243 vfs_context_t a_context;
4244 } */ *ap;
4245 {
4246 vnode_t vp = ap->a_vp;
4247 struct nfsmount *nmp;
4248 struct nfsv3_pathconf pc;
4249 int error = 0, cached;
4250
4251 nmp = VFSTONFS(vnode_mount(vp));
4252 if (!nmp)
4253 return (ENXIO);
4254 if (!NFS_ISV3(vp))
4255 return (EINVAL);
4256
4257 switch (ap->a_name) {
4258 case _PC_LINK_MAX:
4259 case _PC_NAME_MAX:
4260 case _PC_CHOWN_RESTRICTED:
4261 case _PC_NO_TRUNC:
4262 case _PC_CASE_SENSITIVE:
4263 case _PC_CASE_PRESERVING:
4264 break;
4265 default:
4266 /* don't bother contacting the server if we know the answer */
4267 return (EINVAL);
4268 }
4269
4270 if (!(nmp->nm_state & NFSSTA_GOTPATHCONF)) {
4271 /* no pathconf info cached */
4272 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
4273 proc_t p = vfs_context_proc(ap->a_context);
4274 error = nfs_pathconfrpc(vp, &pc, cred, p);
4275 if (error)
4276 return (error);
4277 nmp = VFSTONFS(vnode_mount(vp));
4278 if (!nmp)
4279 return (ENXIO);
4280 if (!(nmp->nm_state & NFSSTA_GOTFSINFO)) {
4281 nfs_fsinfo(nmp, vp, cred, p);
4282 nmp = VFSTONFS(vnode_mount(vp));
4283 if (!nmp)
4284 return (ENXIO);
4285 }
4286 if ((nmp->nm_state & NFSSTA_GOTFSINFO) &&
4287 (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS)) {
4288 /* all files have the same pathconf info, */
4289 /* so cache a copy of the results */
4290 nfs_pathconf_cache(nmp, &pc);
4291 }
4292 }
4293
4294 cached = (nmp->nm_state & NFSSTA_GOTPATHCONF);
4295
4296 switch (ap->a_name) {
4297 case _PC_LINK_MAX:
4298 *ap->a_retval = cached ? nmp->nm_fsinfo.linkmax : pc.pc_linkmax;
4299 break;
4300 case _PC_NAME_MAX:
4301 *ap->a_retval = cached ? nmp->nm_fsinfo.namemax : pc.pc_namemax;
4302 break;
4303 case _PC_CHOWN_RESTRICTED:
4304 if (cached)
4305 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CHOWN_RESTRICTED) ? 1 : 0;
4306 else
4307 *ap->a_retval = pc.pc_chownrestricted;
4308 break;
4309 case _PC_NO_TRUNC:
4310 if (cached)
4311 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_NOTRUNC) ? 1 : 0;
4312 else
4313 *ap->a_retval = pc.pc_notrunc;
4314 break;
4315 case _PC_CASE_SENSITIVE:
4316 if (cached)
4317 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_INSENSITIVE) ? 0 : 1;
4318 else
4319 *ap->a_retval = !pc.pc_caseinsensitive;
4320 break;
4321 case _PC_CASE_PRESERVING:
4322 if (cached)
4323 *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_PRESERVING) ? 1 : 0;
4324 else
4325 *ap->a_retval = pc.pc_casepreserving;
4326 break;
4327 default:
4328 error = EINVAL;
4329 }
4330
4331 return (error);
4332 }
4333
4334 /*
4335 * NFS advisory byte-level locks (client)
4336 */
4337 static int
4338 nfs_advlock(ap)
4339 struct vnop_advlock_args /* {
4340 struct vnodeop_desc *a_desc;
4341 vnode_t a_vp;
4342 caddr_t a_id;
4343 int a_op;
4344 struct flock *a_fl;
4345 int a_flags;
4346 vfs_context_t a_context;
4347 } */ *ap;
4348 {
4349 return (nfs_dolock(ap));
4350 }
4351
4352 /*
4353 * write (or commit) the given NFS buffer
4354 */
4355 int
4356 nfs_buf_write(struct nfsbuf *bp)
4357 {
4358 int oldflags = bp->nb_flags, rv = 0;
4359 vnode_t vp = bp->nb_vp;
4360 struct nfsnode *np = VTONFS(vp);
4361 kauth_cred_t cr;
4362 proc_t p = current_proc(); // XXX
4363
4364 FSDBG_TOP(553, bp, NBOFF(bp), bp->nb_flags, 0);
4365
4366 if (!ISSET(bp->nb_lflags, NBL_BUSY))
4367 panic("nfs_buf_write: buffer is not busy???");
4368
4369 CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
4370 if (ISSET(oldflags, NB_DELWRI)) {
4371 OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
4372 NFSBUFCNTCHK(0);
4373 wakeup(&nfs_nbdwrite);
4374 }
4375
4376 /* move to clean list */
4377 if (ISSET(oldflags, (NB_ASYNC|NB_DELWRI))) {
4378 lck_mtx_lock(nfs_buf_mutex);
4379 if (bp->nb_vnbufs.le_next != NFSNOLIST)
4380 LIST_REMOVE(bp, nb_vnbufs);
4381 LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
4382 lck_mtx_unlock(nfs_buf_mutex);
4383 }
4384 vnode_startwrite(vp);
4385
4386 if (p && p->p_stats)
4387 p->p_stats->p_ru.ru_oublock++;
4388
4389 /*
4390 * For async requests when nfsiod(s) are running, queue the request by
4391 * calling nfs_asyncio(), otherwise just all nfs_doio() to do the request.
4392 */
4393 if (ISSET(bp->nb_flags, NB_ASYNC))
4394 p = NULL;
4395 if (ISSET(bp->nb_flags, NB_READ))
4396 cr = bp->nb_rcred;
4397 else
4398 cr = bp->nb_wcred;
4399 if (!ISSET(bp->nb_flags, NB_ASYNC) || nfs_asyncio(bp, NOCRED))
4400 rv = nfs_doio(bp, cr, p);
4401
4402 if ((oldflags & NB_ASYNC) == 0) {
4403 rv = nfs_buf_iowait(bp);
4404 /* move to clean list */
4405 if (oldflags & NB_DELWRI) {
4406 lck_mtx_lock(nfs_buf_mutex);
4407 if (bp->nb_vnbufs.le_next != NFSNOLIST)
4408 LIST_REMOVE(bp, nb_vnbufs);
4409 LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
4410 lck_mtx_unlock(nfs_buf_mutex);
4411 }
4412 oldflags = bp->nb_flags;
4413 FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
4414 if (cr) {
4415 kauth_cred_ref(cr);
4416 }
4417 nfs_buf_release(bp, 1);
4418 if (ISSET(oldflags, NB_ERROR) && !(np->n_flag & NFLUSHINPROG)) {
4419 /*
4420 * There was a write error and we need to
4421 * invalidate attrs and flush buffers in
4422 * order to sync up with the server.
4423 * (if this write was extending the file,
4424 * we may no longer know the correct size)
4425 *
4426 * But we couldn't call vinvalbuf while holding
4427 * the buffer busy. So we call vinvalbuf() after
4428 * releasing the buffer.
4429 */
4430 nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cr, p, 1);
4431 }
4432 if (cr)
4433 kauth_cred_rele(cr);
4434 return (rv);
4435 }
4436
4437 FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
4438 return (rv);
4439 }
4440
4441 /*
4442 * Read wrapper for special devices.
4443 */
4444 static int
4445 nfsspec_read(ap)
4446 struct vnop_read_args /* {
4447 struct vnodeop_desc *a_desc;
4448 vnode_t a_vp;
4449 struct uio *a_uio;
4450 int a_ioflag;
4451 vfs_context_t a_context;
4452 } */ *ap;
4453 {
4454 register struct nfsnode *np = VTONFS(ap->a_vp);
4455 struct timeval now;
4456
4457 /*
4458 * Set access flag.
4459 */
4460 np->n_flag |= NACC;
4461 microtime(&now);
4462 np->n_atim.tv_sec = now.tv_sec;
4463 np->n_atim.tv_nsec = now.tv_usec * 1000;
4464 return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap));
4465 }
4466
4467 /*
4468 * Write wrapper for special devices.
4469 */
4470 static int
4471 nfsspec_write(ap)
4472 struct vnop_write_args /* {
4473 struct vnodeop_desc *a_desc;
4474 vnode_t a_vp;
4475 struct uio *a_uio;
4476 int a_ioflag;
4477 vfs_context_t a_context;
4478 } */ *ap;
4479 {
4480 register struct nfsnode *np = VTONFS(ap->a_vp);
4481 struct timeval now;
4482
4483 /*
4484 * Set update flag.
4485 */
4486 np->n_flag |= NUPD;
4487 microtime(&now);
4488 np->n_mtim.tv_sec = now.tv_sec;
4489 np->n_mtim.tv_nsec = now.tv_usec * 1000;
4490 return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap));
4491 }
4492
4493 /*
4494 * Close wrapper for special devices.
4495 *
4496 * Update the times on the nfsnode then do device close.
4497 */
4498 static int
4499 nfsspec_close(ap)
4500 struct vnop_close_args /* {
4501 struct vnodeop_desc *a_desc;
4502 vnode_t a_vp;
4503 int a_fflag;
4504 vfs_context_t a_context;
4505 } */ *ap;
4506 {
4507 vnode_t vp = ap->a_vp;
4508 struct nfsnode *np = VTONFS(vp);
4509 struct vnode_attr vattr;
4510 mount_t mp;
4511
4512 if (np->n_flag & (NACC | NUPD)) {
4513 np->n_flag |= NCHG;
4514 if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
4515 VATTR_INIT(&vattr);
4516 if (np->n_flag & NACC) {
4517 vattr.va_access_time = np->n_atim;
4518 VATTR_SET_ACTIVE(&vattr, va_access_time);
4519 }
4520 if (np->n_flag & NUPD) {
4521 vattr.va_modify_time = np->n_mtim;
4522 VATTR_SET_ACTIVE(&vattr, va_modify_time);
4523 }
4524 vnode_setattr(vp, &vattr, ap->a_context);
4525 }
4526 }
4527 return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap));
4528 }
4529
4530 extern vnop_t **fifo_vnodeop_p;
4531
4532 /*
4533 * Read wrapper for fifos.
4534 */
4535 static int
4536 nfsfifo_read(ap)
4537 struct vnop_read_args /* {
4538 struct vnodeop_desc *a_desc;
4539 vnode_t a_vp;
4540 struct uio *a_uio;
4541 int a_ioflag;
4542 vfs_context_t a_context;
4543 } */ *ap;
4544 {
4545 register struct nfsnode *np = VTONFS(ap->a_vp);
4546 struct timeval now;
4547
4548 /*
4549 * Set access flag.
4550 */
4551 np->n_flag |= NACC;
4552 microtime(&now);
4553 np->n_atim.tv_sec = now.tv_sec;
4554 np->n_atim.tv_nsec = now.tv_usec * 1000;
4555 return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_read), ap));
4556 }
4557
4558 /*
4559 * Write wrapper for fifos.
4560 */
4561 static int
4562 nfsfifo_write(ap)
4563 struct vnop_write_args /* {
4564 struct vnodeop_desc *a_desc;
4565 vnode_t a_vp;
4566 struct uio *a_uio;
4567 int a_ioflag;
4568 vfs_context_t a_context;
4569 } */ *ap;
4570 {
4571 register struct nfsnode *np = VTONFS(ap->a_vp);
4572 struct timeval now;
4573
4574 /*
4575 * Set update flag.
4576 */
4577 np->n_flag |= NUPD;
4578 microtime(&now);
4579 np->n_mtim.tv_sec = now.tv_sec;
4580 np->n_mtim.tv_nsec = now.tv_usec * 1000;
4581 return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_write), ap));
4582 }
4583
4584 /*
4585 * Close wrapper for fifos.
4586 *
4587 * Update the times on the nfsnode then do fifo close.
4588 */
4589 static int
4590 nfsfifo_close(ap)
4591 struct vnop_close_args /* {
4592 struct vnodeop_desc *a_desc;
4593 vnode_t a_vp;
4594 int a_fflag;
4595 vfs_context_t a_context;
4596 } */ *ap;
4597 {
4598 vnode_t vp = ap->a_vp;
4599 struct nfsnode *np = VTONFS(vp);
4600 struct vnode_attr vattr;
4601 struct timeval now;
4602 mount_t mp;
4603
4604 if (np->n_flag & (NACC | NUPD)) {
4605 microtime(&now);
4606 if (np->n_flag & NACC) {
4607 np->n_atim.tv_sec = now.tv_sec;
4608 np->n_atim.tv_nsec = now.tv_usec * 1000;
4609 }
4610 if (np->n_flag & NUPD) {
4611 np->n_mtim.tv_sec = now.tv_sec;
4612 np->n_mtim.tv_nsec = now.tv_usec * 1000;
4613 }
4614 np->n_flag |= NCHG;
4615 if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
4616 VATTR_INIT(&vattr);
4617 if (np->n_flag & NACC) {
4618 vattr.va_access_time = np->n_atim;
4619 VATTR_SET_ACTIVE(&vattr, va_access_time);
4620 }
4621 if (np->n_flag & NUPD) {
4622 vattr.va_modify_time = np->n_mtim;
4623 VATTR_SET_ACTIVE(&vattr, va_modify_time);
4624 }
4625 vnode_setattr(vp, &vattr, ap->a_context);
4626 }
4627 }
4628 return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_close), ap));
4629 }
4630
4631 /*ARGSUSED*/
4632 static int
4633 nfs_ioctl(
4634 __unused struct vnop_ioctl_args /* {
4635 struct vnodeop_desc *a_desc;
4636 vnode_t a_vp;
4637 u_long a_command;
4638 caddr_t a_data;
4639 int a_fflag;
4640 kauth_cred_t a_cred;
4641 proc_t a_p;
4642 } */ *ap)
4643 {
4644
4645 /*
4646 * XXX we were once bogusly enoictl() which returned this (ENOTTY).
4647 * Probably we should return ENODEV.
4648 */
4649 return (ENOTTY);
4650 }
4651
4652 /*ARGSUSED*/
4653 static int
4654 nfs_select(
4655 __unused struct vnop_select_args /* {
4656 struct vnodeop_desc *a_desc;
4657 vnode_t a_vp;
4658 int a_which;
4659 int a_fflags;
4660 kauth_cred_t a_cred;
4661 void *a_wql;
4662 proc_t a_p;
4663 } */ *ap)
4664 {
4665
4666 /*
4667 * We were once bogusly seltrue() which returns 1. Is this right?
4668 */
4669 return (1);
4670 }
4671
4672 /*
4673 * Vnode op for pagein using getblk_pages
4674 * derived from nfs_bioread()
4675 * No read aheads are started from pagein operation
4676 */
4677 static int
4678 nfs_pagein(ap)
4679 struct vnop_pagein_args /* {
4680 struct vnodeop_desc *a_desc;
4681 vnode_t a_vp;
4682 upl_t a_pl;
4683 vm_offset_t a_pl_offset;
4684 off_t a_f_offset;
4685 size_t a_size;
4686 int a_flags;
4687 vfs_context_t a_context;
4688 } */ *ap;
4689 {
4690 vnode_t vp = ap->a_vp;
4691 upl_t pl = ap->a_pl;
4692 size_t size= ap->a_size;
4693 off_t f_offset = ap->a_f_offset;
4694 vm_offset_t pl_offset = ap->a_pl_offset;
4695 int flags = ap->a_flags;
4696 kauth_cred_t cred;
4697 proc_t p;
4698 struct nfsnode *np = VTONFS(vp);
4699 int biosize, xsize, iosize;
4700 struct nfsmount *nmp;
4701 int error = 0;
4702 vm_offset_t ioaddr;
4703 struct uio auio;
4704 struct iovec_32 aiov;
4705 struct uio * uio = &auio;
4706 int nofreeupl = flags & UPL_NOCOMMIT;
4707 upl_page_info_t *plinfo;
4708
4709 FSDBG(322, vp, f_offset, size, flags);
4710 if (pl == (upl_t)NULL)
4711 panic("nfs_pagein: no upl");
4712
4713 if (UBCINVALID(vp)) {
4714 printf("nfs_pagein: invalid vnode 0x%x", (int)vp);
4715 if (!nofreeupl)
4716 (void) ubc_upl_abort(pl, 0);
4717 return (EPERM);
4718 }
4719 UBCINFOCHECK("nfs_pagein", vp);
4720
4721 if (size <= 0) {
4722 printf("nfs_pagein: invalid size %d", size);
4723 if (!nofreeupl)
4724 (void) ubc_upl_abort(pl, 0);
4725 return (EINVAL);
4726 }
4727 if (f_offset < 0 || f_offset >= (off_t)np->n_size || (f_offset & PAGE_MASK_64)) {
4728 if (!nofreeupl)
4729 ubc_upl_abort_range(pl, pl_offset, size,
4730 UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
4731 return (EINVAL);
4732 }
4733
4734 cred = ubc_getcred(vp);
4735 if (cred == NOCRED)
4736 cred = vfs_context_ucred(ap->a_context);
4737 p = vfs_context_proc(ap->a_context);
4738
4739 auio.uio_offset = f_offset;
4740 #if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
4741 auio.uio_segflg = UIO_SYSSPACE;
4742 #else
4743 auio.uio_segflg = UIO_SYSSPACE32;
4744 #endif
4745 auio.uio_rw = UIO_READ;
4746 auio.uio_procp = p;
4747
4748 nmp = VFSTONFS(vnode_mount(vp));
4749 if (!nmp) {
4750 if (!nofreeupl)
4751 ubc_upl_abort_range(pl, pl_offset, size,
4752 UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
4753 return (ENXIO);
4754 }
4755 if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
4756 (void)nfs_fsinfo(nmp, vp, cred, p);
4757 biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
4758
4759 plinfo = ubc_upl_pageinfo(pl);
4760 ubc_upl_map(pl, &ioaddr);
4761 ioaddr += pl_offset;
4762 xsize = size;
4763
4764 do {
4765 /*
4766 * It would be nice to be able to issue all these requests
4767 * in parallel instead of waiting for each one to complete
4768 * before sending the next one.
4769 * XXX Should we align these requests to block boundaries?
4770 */
4771 iosize = min(biosize, xsize);
4772 aiov.iov_len = iosize;
4773 aiov.iov_base = (uintptr_t)ioaddr;
4774 auio.uio_iovs.iov32p = &aiov;
4775 auio.uio_iovcnt = 1;
4776 uio_uio_resid_set(&auio, iosize);
4777
4778 FSDBG(322, uio->uio_offset, uio_uio_resid(uio), ioaddr, xsize);
4779 /*
4780 * With UBC we get here only when the file data is not in the VM
4781 * page cache, so go ahead and read in.
4782 */
4783 #ifdef UPL_DEBUG
4784 upl_ubc_alias_set(pl, current_thread(), 2);
4785 #endif /* UPL_DEBUG */
4786 OSAddAtomic(1, (SInt32*)&nfsstats.pageins);
4787
4788 error = nfs_readrpc(vp, uio, cred, p);
4789
4790 if (!error) {
4791 if (uio_uio_resid(uio)) {
4792 /*
4793 * If uio_resid > 0, there is a hole in the file
4794 * and no writes after the hole have been pushed
4795 * to the server yet... or we're at the EOF
4796 * Just zero fill the rest of the valid area.
4797 */
4798 // LP64todo - fix this
4799 int zcnt = uio_uio_resid(uio);
4800 int zoff = iosize - zcnt;
4801 bzero((char *)ioaddr + zoff, zcnt);
4802
4803 FSDBG(324, uio->uio_offset, zoff, zcnt, ioaddr);
4804 uio->uio_offset += zcnt;
4805 }
4806 ioaddr += iosize;
4807 xsize -= iosize;
4808 } else {
4809 FSDBG(322, uio->uio_offset, uio_uio_resid(uio), error, -1);
4810 }
4811
4812 nmp = VFSTONFS(vnode_mount(vp));
4813 } while (error == 0 && xsize > 0);
4814
4815 ubc_upl_unmap(pl);
4816
4817 if (!nofreeupl) {
4818 if (error)
4819 ubc_upl_abort_range(pl, pl_offset, size,
4820 UPL_ABORT_ERROR |
4821 UPL_ABORT_FREE_ON_EMPTY);
4822 else
4823 ubc_upl_commit_range(pl, pl_offset, size,
4824 UPL_COMMIT_CLEAR_DIRTY |
4825 UPL_COMMIT_FREE_ON_EMPTY);
4826 }
4827 return (error);
4828 }
4829
4830
4831 /*
4832 * Vnode op for pageout using UPL
4833 * Derived from nfs_write()
4834 * File size changes are not permitted in pageout.
4835 */
4836 static int
4837 nfs_pageout(ap)
4838 struct vnop_pageout_args /* {
4839 struct vnodeop_desc *a_desc;
4840 vnode_t a_vp;
4841 upl_t a_pl;
4842 vm_offset_t a_pl_offset;
4843 off_t a_f_offset;
4844 size_t a_size;
4845 int a_flags;
4846 vfs_context_t a_context;
4847 } */ *ap;
4848 {
4849 vnode_t vp = ap->a_vp;
4850 upl_t pl = ap->a_pl;
4851 size_t size= ap->a_size;
4852 off_t f_offset = ap->a_f_offset;
4853 vm_offset_t pl_offset = ap->a_pl_offset;
4854 int flags = ap->a_flags;
4855 struct nfsnode *np = VTONFS(vp);
4856 kauth_cred_t cred;
4857 proc_t p;
4858 struct nfsbuf *bp;
4859 struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4860 daddr64_t lbn;
4861 int error = 0, iomode, must_commit;
4862 off_t off;
4863 vm_offset_t ioaddr;
4864 struct uio auio;
4865 struct iovec_32 aiov;
4866 int nofreeupl = flags & UPL_NOCOMMIT;
4867 size_t biosize, iosize, pgsize, xsize;
4868
4869 FSDBG(323, f_offset, size, pl, pl_offset);
4870
4871 if (pl == (upl_t)NULL)
4872 panic("nfs_pageout: no upl");
4873
4874 if (UBCINVALID(vp)) {
4875 printf("nfs_pageout: invalid vnode 0x%x", (int)vp);
4876 if (!nofreeupl)
4877 ubc_upl_abort(pl, 0);
4878 return (EIO);
4879 }
4880 UBCINFOCHECK("nfs_pageout", vp);
4881
4882 if (size <= 0) {
4883 printf("nfs_pageout: invalid size %d", size);
4884 if (!nofreeupl)
4885 ubc_upl_abort(pl, 0);
4886 return (EINVAL);
4887 }
4888
4889 if (!nmp) {
4890 if (!nofreeupl)
4891 ubc_upl_abort(pl, UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY);
4892 return (ENXIO);
4893 }
4894 biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
4895
4896 /*
4897 * Check to see whether the buffer is incore.
4898 * If incore and not busy, invalidate it from the cache.
4899 */
4900 for (iosize = 0; iosize < size; iosize += xsize) {
4901 off = f_offset + iosize;
4902 /* need make sure we do things on block boundaries */
4903 xsize = biosize - (off % biosize);
4904 if (off + xsize > f_offset + size)
4905 xsize = f_offset + size - off;
4906 lbn = ubc_offtoblk(vp, off);
4907 lck_mtx_lock(nfs_buf_mutex);
4908 if ((bp = nfs_buf_incore(vp, lbn))) {
4909 FSDBG(323, off, bp, bp->nb_lflags, bp->nb_flags);
4910 if (nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0)) {
4911 lck_mtx_unlock(nfs_buf_mutex);
4912 /* no panic. just tell vm we are busy */
4913 if (!nofreeupl)
4914 ubc_upl_abort(pl, 0);
4915 return (EBUSY);
4916 }
4917 if (bp->nb_dirtyend > 0) {
4918 /*
4919 * if there's a dirty range in the buffer, check
4920 * to see if it extends beyond the pageout region
4921 *
4922 * if the dirty region lies completely within the
4923 * pageout region, we just invalidate the buffer
4924 * because it's all being written out now anyway.
4925 *
4926 * if any of the dirty region lies outside the
4927 * pageout region, we'll try to clip the dirty
4928 * region to eliminate the portion that's being
4929 * paged out. If that's not possible, because
4930 * the dirty region extends before and after the
4931 * pageout region, then we'll just return EBUSY.
4932 */
4933 off_t boff, start, end;
4934 boff = NBOFF(bp);
4935 start = off;
4936 end = off + xsize;
4937 /* clip end to EOF */
4938 if (end > (off_t)np->n_size)
4939 end = np->n_size;
4940 start -= boff;
4941 end -= boff;
4942 if ((bp->nb_dirtyoff < start) &&
4943 (bp->nb_dirtyend > end)) {
4944 /* not gonna be able to clip the dirty region */
4945 FSDBG(323, vp, bp, 0xd00deebc, EBUSY);
4946 nfs_buf_drop(bp);
4947 lck_mtx_unlock(nfs_buf_mutex);
4948 if (!nofreeupl)
4949 ubc_upl_abort(pl, 0);
4950 return (EBUSY);
4951 }
4952 if ((bp->nb_dirtyoff < start) ||
4953 (bp->nb_dirtyend > end)) {
4954 /* clip dirty region, if necessary */
4955 if (bp->nb_dirtyoff < start)
4956 bp->nb_dirtyend = min(bp->nb_dirtyend, start);
4957 if (bp->nb_dirtyend > end)
4958 bp->nb_dirtyoff = max(bp->nb_dirtyoff, end);
4959 FSDBG(323, bp, bp->nb_dirtyoff, bp->nb_dirtyend, 0xd00dee00);
4960 /* we're leaving this block dirty */
4961 nfs_buf_drop(bp);
4962 lck_mtx_unlock(nfs_buf_mutex);
4963 continue;
4964 }
4965 }
4966 nfs_buf_remfree(bp);
4967 lck_mtx_unlock(nfs_buf_mutex);
4968 SET(bp->nb_flags, NB_INVAL);
4969 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
4970 CLR(bp->nb_flags, NB_NEEDCOMMIT);
4971 np->n_needcommitcnt--;
4972 CHECK_NEEDCOMMITCNT(np);
4973 }
4974 nfs_buf_release(bp, 1);
4975 } else {
4976 lck_mtx_unlock(nfs_buf_mutex);
4977 }
4978 }
4979
4980 cred = ubc_getcred(vp);
4981 if (cred == NOCRED)
4982 cred = vfs_context_ucred(ap->a_context);
4983 p = vfs_context_proc(ap->a_context);
4984
4985 if (np->n_flag & NWRITEERR) {
4986 np->n_flag &= ~NWRITEERR;
4987 if (!nofreeupl)
4988 ubc_upl_abort_range(pl, pl_offset, size,
4989 UPL_ABORT_FREE_ON_EMPTY);
4990 return (np->n_error);
4991 }
4992 if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
4993 nfs_fsinfo(nmp, vp, cred, p);
4994
4995 if (f_offset < 0 || f_offset >= (off_t)np->n_size ||
4996 f_offset & PAGE_MASK_64 || size & PAGE_MASK_64) {
4997 if (!nofreeupl)
4998 ubc_upl_abort_range(pl, pl_offset, size,
4999 UPL_ABORT_FREE_ON_EMPTY);
5000 return (EINVAL);
5001 }
5002
5003 ubc_upl_map(pl, &ioaddr);
5004 ioaddr += pl_offset;
5005
5006 if ((u_quad_t)f_offset + size > np->n_size)
5007 xsize = np->n_size - f_offset;
5008 else
5009 xsize = size;
5010
5011 pgsize = round_page_64(xsize);
5012 if (size > pgsize) {
5013 if (!nofreeupl)
5014 ubc_upl_abort_range(pl, pl_offset + pgsize,
5015 size - pgsize,
5016 UPL_ABORT_FREE_ON_EMPTY);
5017 }
5018
5019 /*
5020 * check for partial page and clear the
5021 * contents past end of the file before
5022 * releasing it in the VM page cache
5023 */
5024 if ((u_quad_t)f_offset < np->n_size && (u_quad_t)f_offset + size > np->n_size) {
5025 size_t io = np->n_size - f_offset;
5026 bzero((caddr_t)(ioaddr + io), size - io);
5027 FSDBG(321, np->n_size, f_offset, f_offset + io, size - io);
5028 }
5029
5030 auio.uio_offset = f_offset;
5031 #if 1 /* LP64todo - can't use new segment flags until the drivers are ready */
5032 auio.uio_segflg = UIO_SYSSPACE;
5033 #else
5034 auio.uio_segflg = UIO_SYSSPACE32;
5035 #endif
5036 auio.uio_rw = UIO_READ;
5037 auio.uio_procp = p;
5038
5039 do {
5040 /*
5041 * It would be nice to be able to issue all these requests
5042 * in parallel instead of waiting for each one to complete
5043 * before sending the next one.
5044 * XXX Should we align these requests to block boundaries?
5045 */
5046 iosize = min(biosize, xsize);
5047 uio_uio_resid_set(&auio, iosize);
5048 aiov.iov_len = iosize;
5049 aiov.iov_base = (uintptr_t)ioaddr;
5050 auio.uio_iovs.iov32p = &aiov;
5051 auio.uio_iovcnt = 1;
5052
5053 FSDBG(323, auio.uio_offset, uio_uio_resid(&auio), ioaddr, xsize);
5054 OSAddAtomic(1, (SInt32*)&nfsstats.pageouts);
5055
5056 vnode_startwrite(vp);
5057
5058 /* NMODIFIED would be set here if doing unstable writes */
5059 iomode = NFSV3WRITE_FILESYNC;
5060 error = nfs_writerpc(vp, &auio, cred, p, &iomode, &must_commit);
5061 if (must_commit)
5062 nfs_clearcommit(vnode_mount(vp));
5063 vnode_writedone(vp);
5064 if (error)
5065 goto cleanup;
5066 /* Note: no need to check uio_resid, because */
5067 /* it'll only be set if there was an error. */
5068 ioaddr += iosize;
5069 xsize -= iosize;
5070 } while (xsize > 0);
5071
5072 cleanup:
5073 ubc_upl_unmap(pl);
5074 /*
5075 * We've had several different solutions on what to do when the pageout
5076 * gets an error. If we don't handle it, and return an error to the
5077 * caller, vm, it will retry . This can end in endless looping
5078 * between vm and here doing retries of the same page. Doing a dump
5079 * back to vm, will get it out of vm's knowledge and we lose whatever
5080 * data existed. This is risky, but in some cases necessary. For
5081 * example, the initial fix here was to do that for ESTALE. In that case
5082 * the server is telling us that the file is no longer the same. We
5083 * would not want to keep paging out to that. We also saw some 151
5084 * errors from Auspex server and NFSv3 can return errors higher than
5085 * ELAST. Those along with NFS known server errors we will "dump" from
5086 * vm. Errors we don't expect to occur, we dump and log for further
5087 * analysis. Errors that could be transient, networking ones,
5088 * we let vm "retry". Lastly, errors that we retry, but may have potential
5089 * to storm the network, we "retrywithsleep". "sever" will be used in
5090 * in the future to dump all pages of object for cases like ESTALE.
5091 * All this is the basis for the states returned and first guesses on
5092 * error handling. Tweaking expected as more statistics are gathered.
5093 * Note, in the long run we may need another more robust solution to
5094 * have some kind of persistant store when the vm cannot dump nor keep
5095 * retrying as a solution, but this would be a file architectural change
5096 */
5097
5098 if (!nofreeupl) { /* otherwise stacked file system has to handle this */
5099 if (error) {
5100 int abortflags = 0;
5101 short action = nfs_pageouterrorhandler(error);
5102
5103 switch (action) {
5104 case DUMP:
5105 abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
5106 break;
5107 case DUMPANDLOG:
5108 abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
5109 if (error <= ELAST &&
5110 (errorcount[error] % 100 == 0))
5111 printf("nfs_pageout: unexpected error %d. dumping vm page\n", error);
5112 errorcount[error]++;
5113 break;
5114 case RETRY:
5115 abortflags = UPL_ABORT_FREE_ON_EMPTY;
5116 break;
5117 case RETRYWITHSLEEP:
5118 abortflags = UPL_ABORT_FREE_ON_EMPTY;
5119 /* pri unused. PSOCK for placeholder. */
5120 tsleep(&lbolt, PSOCK, "nfspageout", 0);
5121 break;
5122 case SEVER: /* not implemented */
5123 default:
5124 printf("nfs_pageout: action %d not expected\n", action);
5125 break;
5126 }
5127
5128 ubc_upl_abort_range(pl, pl_offset, size, abortflags);
5129 /* return error in all cases above */
5130
5131 } else
5132 ubc_upl_commit_range(pl, pl_offset, pgsize,
5133 UPL_COMMIT_CLEAR_DIRTY |
5134 UPL_COMMIT_FREE_ON_EMPTY);
5135 }
5136 return (error);
5137 }
5138
5139 /* Blktooff derives file offset given a logical block number */
5140 static int
5141 nfs_blktooff(ap)
5142 struct vnop_blktooff_args /* {
5143 struct vnodeop_desc *a_desc;
5144 vnode_t a_vp;
5145 daddr64_t a_lblkno;
5146 off_t *a_offset;
5147 } */ *ap;
5148 {
5149 int biosize;
5150 vnode_t vp = ap->a_vp;
5151 mount_t mp = vnode_mount(vp);
5152
5153 if (!mp)
5154 return (ENXIO);
5155
5156 biosize = vfs_statfs(mp)->f_iosize;
5157
5158 *ap->a_offset = (off_t)(ap->a_lblkno * biosize);
5159
5160 return (0);
5161 }
5162
5163 static int
5164 nfs_offtoblk(ap)
5165 struct vnop_offtoblk_args /* {
5166 struct vnodeop_desc *a_desc;
5167 vnode_t a_vp;
5168 off_t a_offset;
5169 daddr64_t *a_lblkno;
5170 } */ *ap;
5171 {
5172 int biosize;
5173 vnode_t vp = ap->a_vp;
5174 mount_t mp = vnode_mount(vp);
5175
5176 if (!mp)
5177 return (ENXIO);
5178
5179 biosize = vfs_statfs(mp)->f_iosize;
5180
5181 *ap->a_lblkno = (daddr64_t)(ap->a_offset / biosize);
5182
5183 return (0);
5184 }
5185