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