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