2 * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * @(#)nfs_vnops.c 8.16 (Berkeley) 5/27/95
65 * FreeBSD-Id: nfs_vnops.c,v 1.72 1997/11/07 09:20:48 phk Exp $
68 #include <nfs/nfs_conf.h>
72 * vnode op calls for Sun NFS version 2 and 3
74 #include <sys/param.h>
75 #include <sys/kernel.h>
76 #include <sys/systm.h>
77 #include <sys/resourcevar.h>
78 #include <sys/proc_internal.h>
79 #include <sys/kauth.h>
80 #include <sys/mount_internal.h>
81 #include <sys/malloc.h>
82 #include <sys/kpi_mbuf.h>
84 #include <sys/vnode_internal.h>
85 #include <sys/dirent.h>
86 #include <sys/fcntl.h>
87 #include <sys/lockf.h>
88 #include <sys/ubc_internal.h>
90 #include <sys/signalvar.h>
91 #include <sys/uio_internal.h>
92 #include <sys/xattr.h>
94 #include <vfs/vfs_support.h>
99 #include <kern/clock.h>
100 #include <libkern/OSAtomic.h>
102 #include <miscfs/fifofs/fifo.h>
103 #include <miscfs/specfs/specdev.h>
105 #include <nfs/rpcv2.h>
106 #include <nfs/nfsproto.h>
108 #include <nfs/nfsnode.h>
109 #include <nfs/nfs_gss.h>
110 #include <nfs/nfsmount.h>
111 #include <nfs/nfs_lock.h>
112 #include <nfs/xdr_subs.h>
113 #include <nfs/nfsm_subs.h>
116 #include <netinet/in.h>
117 #include <netinet/in_var.h>
119 #include <vm/vm_kern.h>
120 #include <vm/vm_pageout.h>
122 #include <kern/task.h>
123 #include <kern/sched_prim.h>
125 #define NFS_VNOP_DBG(...) NFS_DBG(NFS_FAC_VNOP, 7, ## __VA_ARGS__)
126 #define DEFAULT_READLINK_NOCACHE 0
131 int nfs_vnop_lookup(struct vnop_lookup_args
*);
132 int nfsspec_vnop_read(struct vnop_read_args
*);
133 int nfsspec_vnop_write(struct vnop_write_args
*);
134 int nfsspec_vnop_close(struct vnop_close_args
*);
136 int nfsfifo_vnop_read(struct vnop_read_args
*);
137 int nfsfifo_vnop_write(struct vnop_write_args
*);
138 int nfsfifo_vnop_close(struct vnop_close_args
*);
140 int nfs_vnop_ioctl(struct vnop_ioctl_args
*);
141 int nfs_vnop_select(struct vnop_select_args
*);
142 int nfs_vnop_setattr(struct vnop_setattr_args
*);
143 int nfs_vnop_fsync(struct vnop_fsync_args
*);
144 int nfs_vnop_rename(struct vnop_rename_args
*);
145 int nfs_vnop_readdir(struct vnop_readdir_args
*);
146 int nfs_vnop_readlink(struct vnop_readlink_args
*);
147 int nfs_vnop_pathconf(struct vnop_pathconf_args
*);
148 int nfs_vnop_pagein(struct vnop_pagein_args
*);
149 int nfs_vnop_pageout(struct vnop_pageout_args
*);
150 int nfs_vnop_blktooff(struct vnop_blktooff_args
*);
151 int nfs_vnop_offtoblk(struct vnop_offtoblk_args
*);
152 int nfs_vnop_blockmap(struct vnop_blockmap_args
*);
153 int nfs_vnop_monitor(struct vnop_monitor_args
*);
155 int nfs3_vnop_create(struct vnop_create_args
*);
156 int nfs3_vnop_mknod(struct vnop_mknod_args
*);
157 int nfs3_vnop_getattr(struct vnop_getattr_args
*);
158 int nfs3_vnop_link(struct vnop_link_args
*);
159 int nfs3_vnop_mkdir(struct vnop_mkdir_args
*);
160 int nfs3_vnop_rmdir(struct vnop_rmdir_args
*);
161 int nfs3_vnop_symlink(struct vnop_symlink_args
*);
164 vnop_t
**nfsv2_vnodeop_p
;
165 static const struct vnodeopv_entry_desc nfsv2_vnodeop_entries
[] = {
166 { .opve_op
= &vnop_default_desc
, .opve_impl
= (vnop_t
*)vn_default_error
},
167 { .opve_op
= &vnop_lookup_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_lookup
}, /* lookup */
168 { .opve_op
= &vnop_create_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_create
}, /* create */
169 { .opve_op
= &vnop_mknod_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_mknod
}, /* mknod */
170 { .opve_op
= &vnop_open_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_open
}, /* open */
171 { .opve_op
= &vnop_close_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_close
}, /* close */
172 { .opve_op
= &vnop_access_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_access
}, /* access */
173 { .opve_op
= &vnop_getattr_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_getattr
}, /* getattr */
174 { .opve_op
= &vnop_setattr_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_setattr
}, /* setattr */
175 { .opve_op
= &vnop_read_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_read
}, /* read */
176 { .opve_op
= &vnop_write_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_write
}, /* write */
177 { .opve_op
= &vnop_ioctl_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_ioctl
}, /* ioctl */
178 { .opve_op
= &vnop_select_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_select
}, /* select */
179 { .opve_op
= &vnop_revoke_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_revoke
}, /* revoke */
180 { .opve_op
= &vnop_mmap_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_mmap
}, /* mmap */
181 { .opve_op
= &vnop_mnomap_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_mnomap
}, /* mnomap */
182 { .opve_op
= &vnop_fsync_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_fsync
}, /* fsync */
183 { .opve_op
= &vnop_remove_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_remove
}, /* remove */
184 { .opve_op
= &vnop_link_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_link
}, /* link */
185 { .opve_op
= &vnop_rename_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_rename
}, /* rename */
186 { .opve_op
= &vnop_mkdir_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_mkdir
}, /* mkdir */
187 { .opve_op
= &vnop_rmdir_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_rmdir
}, /* rmdir */
188 { .opve_op
= &vnop_symlink_desc
, .opve_impl
= (vnop_t
*)nfs3_vnop_symlink
}, /* symlink */
189 { .opve_op
= &vnop_readdir_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_readdir
}, /* readdir */
190 { .opve_op
= &vnop_readlink_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_readlink
}, /* readlink */
191 { .opve_op
= &vnop_inactive_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_inactive
}, /* inactive */
192 { .opve_op
= &vnop_reclaim_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_reclaim
}, /* reclaim */
193 { .opve_op
= &vnop_strategy_desc
, .opve_impl
= (vnop_t
*)err_strategy
}, /* strategy */
194 { .opve_op
= &vnop_pathconf_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_pathconf
}, /* pathconf */
195 { .opve_op
= &vnop_advlock_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_advlock
}, /* advlock */
196 { .opve_op
= &vnop_bwrite_desc
, .opve_impl
= (vnop_t
*)err_bwrite
}, /* bwrite */
197 { .opve_op
= &vnop_pagein_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_pagein
}, /* Pagein */
198 { .opve_op
= &vnop_pageout_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_pageout
}, /* Pageout */
199 { .opve_op
= &vnop_copyfile_desc
, .opve_impl
= (vnop_t
*)err_copyfile
}, /* Copyfile */
200 { .opve_op
= &vnop_blktooff_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_blktooff
}, /* blktooff */
201 { .opve_op
= &vnop_offtoblk_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_offtoblk
}, /* offtoblk */
202 { .opve_op
= &vnop_blockmap_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_blockmap
}, /* blockmap */
203 { .opve_op
= &vnop_monitor_desc
, .opve_impl
= (vnop_t
*)nfs_vnop_monitor
}, /* monitor */
204 { .opve_op
= NULL
, .opve_impl
= NULL
}
206 const struct vnodeopv_desc nfsv2_vnodeop_opv_desc
=
207 { &nfsv2_vnodeop_p
, nfsv2_vnodeop_entries
};
211 vnop_t
**nfsv4_vnodeop_p
;
212 static const struct vnodeopv_entry_desc nfsv4_vnodeop_entries
[] = {
213 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
214 { &vnop_lookup_desc
, (vnop_t
*)nfs_vnop_lookup
}, /* lookup */
215 { &vnop_create_desc
, (vnop_t
*)nfs4_vnop_create
}, /* create */
216 { &vnop_mknod_desc
, (vnop_t
*)nfs4_vnop_mknod
}, /* mknod */
217 { &vnop_open_desc
, (vnop_t
*)nfs_vnop_open
}, /* open */
218 { &vnop_close_desc
, (vnop_t
*)nfs_vnop_close
}, /* close */
219 { &vnop_access_desc
, (vnop_t
*)nfs_vnop_access
}, /* access */
220 { &vnop_getattr_desc
, (vnop_t
*)nfs4_vnop_getattr
}, /* getattr */
221 { &vnop_setattr_desc
, (vnop_t
*)nfs_vnop_setattr
}, /* setattr */
222 { &vnop_read_desc
, (vnop_t
*)nfs_vnop_read
}, /* read */
223 { &vnop_write_desc
, (vnop_t
*)nfs_vnop_write
}, /* write */
224 { &vnop_ioctl_desc
, (vnop_t
*)nfs_vnop_ioctl
}, /* ioctl */
225 { &vnop_select_desc
, (vnop_t
*)nfs_vnop_select
}, /* select */
226 { &vnop_revoke_desc
, (vnop_t
*)nfs_vnop_revoke
}, /* revoke */
227 { &vnop_mmap_desc
, (vnop_t
*)nfs_vnop_mmap
}, /* mmap */
228 { &vnop_mnomap_desc
, (vnop_t
*)nfs_vnop_mnomap
}, /* mnomap */
229 { &vnop_fsync_desc
, (vnop_t
*)nfs_vnop_fsync
}, /* fsync */
230 { &vnop_remove_desc
, (vnop_t
*)nfs_vnop_remove
}, /* remove */
231 { &vnop_link_desc
, (vnop_t
*)nfs4_vnop_link
}, /* link */
232 { &vnop_rename_desc
, (vnop_t
*)nfs_vnop_rename
}, /* rename */
233 { &vnop_mkdir_desc
, (vnop_t
*)nfs4_vnop_mkdir
}, /* mkdir */
234 { &vnop_rmdir_desc
, (vnop_t
*)nfs4_vnop_rmdir
}, /* rmdir */
235 { &vnop_symlink_desc
, (vnop_t
*)nfs4_vnop_symlink
}, /* symlink */
236 { &vnop_readdir_desc
, (vnop_t
*)nfs_vnop_readdir
}, /* readdir */
237 { &vnop_readlink_desc
, (vnop_t
*)nfs_vnop_readlink
}, /* readlink */
238 { &vnop_inactive_desc
, (vnop_t
*)nfs_vnop_inactive
}, /* inactive */
239 { &vnop_reclaim_desc
, (vnop_t
*)nfs_vnop_reclaim
}, /* reclaim */
240 { &vnop_strategy_desc
, (vnop_t
*)err_strategy
}, /* strategy */
241 { &vnop_pathconf_desc
, (vnop_t
*)nfs_vnop_pathconf
}, /* pathconf */
242 { &vnop_advlock_desc
, (vnop_t
*)nfs_vnop_advlock
}, /* advlock */
243 { &vnop_bwrite_desc
, (vnop_t
*)err_bwrite
}, /* bwrite */
244 { &vnop_pagein_desc
, (vnop_t
*)nfs_vnop_pagein
}, /* Pagein */
245 { &vnop_pageout_desc
, (vnop_t
*)nfs_vnop_pageout
}, /* Pageout */
246 { &vnop_copyfile_desc
, (vnop_t
*)err_copyfile
}, /* Copyfile */
247 { &vnop_blktooff_desc
, (vnop_t
*)nfs_vnop_blktooff
}, /* blktooff */
248 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_vnop_offtoblk
}, /* offtoblk */
249 { &vnop_blockmap_desc
, (vnop_t
*)nfs_vnop_blockmap
}, /* blockmap */
250 { &vnop_getxattr_desc
, (vnop_t
*)nfs4_vnop_getxattr
}, /* getxattr */
251 { &vnop_setxattr_desc
, (vnop_t
*)nfs4_vnop_setxattr
}, /* setxattr */
252 { &vnop_removexattr_desc
, (vnop_t
*)nfs4_vnop_removexattr
},/* removexattr */
253 { &vnop_listxattr_desc
, (vnop_t
*)nfs4_vnop_listxattr
},/* listxattr */
255 { &vnop_getnamedstream_desc
, (vnop_t
*)nfs4_vnop_getnamedstream
}, /* getnamedstream */
256 { &vnop_makenamedstream_desc
, (vnop_t
*)nfs4_vnop_makenamedstream
}, /* makenamedstream */
257 { &vnop_removenamedstream_desc
, (vnop_t
*)nfs4_vnop_removenamedstream
},/* removenamedstream */
259 { &vnop_monitor_desc
, (vnop_t
*)nfs_vnop_monitor
}, /* monitor */
262 const struct vnodeopv_desc nfsv4_vnodeop_opv_desc
=
263 { &nfsv4_vnodeop_p
, nfsv4_vnodeop_entries
};
267 * Special device vnode ops
269 vnop_t
**spec_nfsv2nodeop_p
;
270 static const struct vnodeopv_entry_desc spec_nfsv2nodeop_entries
[] = {
271 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
272 { &vnop_lookup_desc
, (vnop_t
*)spec_lookup
}, /* lookup */
273 { &vnop_create_desc
, (vnop_t
*)spec_create
}, /* create */
274 { &vnop_mknod_desc
, (vnop_t
*)spec_mknod
}, /* mknod */
275 { &vnop_open_desc
, (vnop_t
*)spec_open
}, /* open */
276 { &vnop_close_desc
, (vnop_t
*)nfsspec_vnop_close
}, /* close */
277 { &vnop_getattr_desc
, (vnop_t
*)nfs3_vnop_getattr
}, /* getattr */
278 { &vnop_setattr_desc
, (vnop_t
*)nfs_vnop_setattr
}, /* setattr */
279 { &vnop_read_desc
, (vnop_t
*)nfsspec_vnop_read
}, /* read */
280 { &vnop_write_desc
, (vnop_t
*)nfsspec_vnop_write
}, /* write */
281 { &vnop_ioctl_desc
, (vnop_t
*)spec_ioctl
}, /* ioctl */
282 { &vnop_select_desc
, (vnop_t
*)spec_select
}, /* select */
283 { &vnop_revoke_desc
, (vnop_t
*)spec_revoke
}, /* revoke */
284 { &vnop_mmap_desc
, (vnop_t
*)spec_mmap
}, /* mmap */
285 { &vnop_fsync_desc
, (vnop_t
*)nfs_vnop_fsync
}, /* fsync */
286 { &vnop_remove_desc
, (vnop_t
*)spec_remove
}, /* remove */
287 { &vnop_link_desc
, (vnop_t
*)spec_link
}, /* link */
288 { &vnop_rename_desc
, (vnop_t
*)spec_rename
}, /* rename */
289 { &vnop_mkdir_desc
, (vnop_t
*)spec_mkdir
}, /* mkdir */
290 { &vnop_rmdir_desc
, (vnop_t
*)spec_rmdir
}, /* rmdir */
291 { &vnop_symlink_desc
, (vnop_t
*)spec_symlink
}, /* symlink */
292 { &vnop_readdir_desc
, (vnop_t
*)spec_readdir
}, /* readdir */
293 { &vnop_readlink_desc
, (vnop_t
*)spec_readlink
}, /* readlink */
294 { &vnop_inactive_desc
, (vnop_t
*)nfs_vnop_inactive
}, /* inactive */
295 { &vnop_reclaim_desc
, (vnop_t
*)nfs_vnop_reclaim
}, /* reclaim */
296 { &vnop_strategy_desc
, (vnop_t
*)spec_strategy
}, /* strategy */
297 { &vnop_pathconf_desc
, (vnop_t
*)spec_pathconf
}, /* pathconf */
298 { &vnop_advlock_desc
, (vnop_t
*)spec_advlock
}, /* advlock */
299 { &vnop_bwrite_desc
, (vnop_t
*)vn_bwrite
}, /* bwrite */
300 { &vnop_pagein_desc
, (vnop_t
*)nfs_vnop_pagein
}, /* Pagein */
301 { &vnop_pageout_desc
, (vnop_t
*)nfs_vnop_pageout
}, /* Pageout */
302 { &vnop_blktooff_desc
, (vnop_t
*)nfs_vnop_blktooff
}, /* blktooff */
303 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_vnop_offtoblk
}, /* offtoblk */
304 { &vnop_blockmap_desc
, (vnop_t
*)nfs_vnop_blockmap
}, /* blockmap */
305 { &vnop_monitor_desc
, (vnop_t
*)nfs_vnop_monitor
}, /* monitor */
308 const struct vnodeopv_desc spec_nfsv2nodeop_opv_desc
=
309 { &spec_nfsv2nodeop_p
, spec_nfsv2nodeop_entries
};
311 vnop_t
**spec_nfsv4nodeop_p
;
312 static const struct vnodeopv_entry_desc spec_nfsv4nodeop_entries
[] = {
313 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
314 { &vnop_lookup_desc
, (vnop_t
*)spec_lookup
}, /* lookup */
315 { &vnop_create_desc
, (vnop_t
*)spec_create
}, /* create */
316 { &vnop_mknod_desc
, (vnop_t
*)spec_mknod
}, /* mknod */
317 { &vnop_open_desc
, (vnop_t
*)spec_open
}, /* open */
318 { &vnop_close_desc
, (vnop_t
*)nfsspec_vnop_close
}, /* close */
319 { &vnop_getattr_desc
, (vnop_t
*)nfs4_vnop_getattr
}, /* getattr */
320 { &vnop_setattr_desc
, (vnop_t
*)nfs_vnop_setattr
}, /* setattr */
321 { &vnop_read_desc
, (vnop_t
*)nfsspec_vnop_read
}, /* read */
322 { &vnop_write_desc
, (vnop_t
*)nfsspec_vnop_write
}, /* write */
323 { &vnop_ioctl_desc
, (vnop_t
*)spec_ioctl
}, /* ioctl */
324 { &vnop_select_desc
, (vnop_t
*)spec_select
}, /* select */
325 { &vnop_revoke_desc
, (vnop_t
*)spec_revoke
}, /* revoke */
326 { &vnop_mmap_desc
, (vnop_t
*)spec_mmap
}, /* mmap */
327 { &vnop_fsync_desc
, (vnop_t
*)nfs_vnop_fsync
}, /* fsync */
328 { &vnop_remove_desc
, (vnop_t
*)spec_remove
}, /* remove */
329 { &vnop_link_desc
, (vnop_t
*)spec_link
}, /* link */
330 { &vnop_rename_desc
, (vnop_t
*)spec_rename
}, /* rename */
331 { &vnop_mkdir_desc
, (vnop_t
*)spec_mkdir
}, /* mkdir */
332 { &vnop_rmdir_desc
, (vnop_t
*)spec_rmdir
}, /* rmdir */
333 { &vnop_symlink_desc
, (vnop_t
*)spec_symlink
}, /* symlink */
334 { &vnop_readdir_desc
, (vnop_t
*)spec_readdir
}, /* readdir */
335 { &vnop_readlink_desc
, (vnop_t
*)spec_readlink
}, /* readlink */
336 { &vnop_inactive_desc
, (vnop_t
*)nfs_vnop_inactive
}, /* inactive */
337 { &vnop_reclaim_desc
, (vnop_t
*)nfs_vnop_reclaim
}, /* reclaim */
338 { &vnop_strategy_desc
, (vnop_t
*)spec_strategy
}, /* strategy */
339 { &vnop_pathconf_desc
, (vnop_t
*)spec_pathconf
}, /* pathconf */
340 { &vnop_advlock_desc
, (vnop_t
*)spec_advlock
}, /* advlock */
341 { &vnop_bwrite_desc
, (vnop_t
*)vn_bwrite
}, /* bwrite */
342 { &vnop_pagein_desc
, (vnop_t
*)nfs_vnop_pagein
}, /* Pagein */
343 { &vnop_pageout_desc
, (vnop_t
*)nfs_vnop_pageout
}, /* Pageout */
344 { &vnop_blktooff_desc
, (vnop_t
*)nfs_vnop_blktooff
}, /* blktooff */
345 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_vnop_offtoblk
}, /* offtoblk */
346 { &vnop_blockmap_desc
, (vnop_t
*)nfs_vnop_blockmap
}, /* blockmap */
347 { &vnop_getxattr_desc
, (vnop_t
*)nfs4_vnop_getxattr
}, /* getxattr */
348 { &vnop_setxattr_desc
, (vnop_t
*)nfs4_vnop_setxattr
}, /* setxattr */
349 { &vnop_removexattr_desc
, (vnop_t
*)nfs4_vnop_removexattr
},/* removexattr */
350 { &vnop_listxattr_desc
, (vnop_t
*)nfs4_vnop_listxattr
},/* listxattr */
352 { &vnop_getnamedstream_desc
, (vnop_t
*)nfs4_vnop_getnamedstream
}, /* getnamedstream */
353 { &vnop_makenamedstream_desc
, (vnop_t
*)nfs4_vnop_makenamedstream
}, /* makenamedstream */
354 { &vnop_removenamedstream_desc
, (vnop_t
*)nfs4_vnop_removenamedstream
},/* removenamedstream */
356 { &vnop_monitor_desc
, (vnop_t
*)nfs_vnop_monitor
}, /* monitor */
359 const struct vnodeopv_desc spec_nfsv4nodeop_opv_desc
=
360 { &spec_nfsv4nodeop_p
, spec_nfsv4nodeop_entries
};
361 #endif /* CONFIG_NFS4 */
364 vnop_t
**fifo_nfsv2nodeop_p
;
365 static const struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries
[] = {
366 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
367 { &vnop_lookup_desc
, (vnop_t
*)fifo_lookup
}, /* lookup */
368 { &vnop_create_desc
, (vnop_t
*)fifo_create
}, /* create */
369 { &vnop_mknod_desc
, (vnop_t
*)fifo_mknod
}, /* mknod */
370 { &vnop_open_desc
, (vnop_t
*)fifo_open
}, /* open */
371 { &vnop_close_desc
, (vnop_t
*)nfsfifo_vnop_close
}, /* close */
372 { &vnop_getattr_desc
, (vnop_t
*)nfs3_vnop_getattr
}, /* getattr */
373 { &vnop_setattr_desc
, (vnop_t
*)nfs_vnop_setattr
}, /* setattr */
374 { &vnop_read_desc
, (vnop_t
*)nfsfifo_vnop_read
}, /* read */
375 { &vnop_write_desc
, (vnop_t
*)nfsfifo_vnop_write
}, /* write */
376 { &vnop_ioctl_desc
, (vnop_t
*)fifo_ioctl
}, /* ioctl */
377 { &vnop_select_desc
, (vnop_t
*)fifo_select
}, /* select */
378 { &vnop_revoke_desc
, (vnop_t
*)fifo_revoke
}, /* revoke */
379 { &vnop_mmap_desc
, (vnop_t
*)fifo_mmap
}, /* mmap */
380 { &vnop_fsync_desc
, (vnop_t
*)nfs_vnop_fsync
}, /* fsync */
381 { &vnop_remove_desc
, (vnop_t
*)fifo_remove
}, /* remove */
382 { &vnop_link_desc
, (vnop_t
*)fifo_link
}, /* link */
383 { &vnop_rename_desc
, (vnop_t
*)fifo_rename
}, /* rename */
384 { &vnop_mkdir_desc
, (vnop_t
*)fifo_mkdir
}, /* mkdir */
385 { &vnop_rmdir_desc
, (vnop_t
*)fifo_rmdir
}, /* rmdir */
386 { &vnop_symlink_desc
, (vnop_t
*)fifo_symlink
}, /* symlink */
387 { &vnop_readdir_desc
, (vnop_t
*)fifo_readdir
}, /* readdir */
388 { &vnop_readlink_desc
, (vnop_t
*)fifo_readlink
}, /* readlink */
389 { &vnop_inactive_desc
, (vnop_t
*)nfs_vnop_inactive
}, /* inactive */
390 { &vnop_reclaim_desc
, (vnop_t
*)nfs_vnop_reclaim
}, /* reclaim */
391 { &vnop_strategy_desc
, (vnop_t
*)fifo_strategy
}, /* strategy */
392 { &vnop_pathconf_desc
, (vnop_t
*)fifo_pathconf
}, /* pathconf */
393 { &vnop_advlock_desc
, (vnop_t
*)fifo_advlock
}, /* advlock */
394 { &vnop_bwrite_desc
, (vnop_t
*)vn_bwrite
}, /* bwrite */
395 { &vnop_pagein_desc
, (vnop_t
*)nfs_vnop_pagein
}, /* Pagein */
396 { &vnop_pageout_desc
, (vnop_t
*)nfs_vnop_pageout
}, /* Pageout */
397 { &vnop_blktooff_desc
, (vnop_t
*)nfs_vnop_blktooff
}, /* blktooff */
398 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_vnop_offtoblk
}, /* offtoblk */
399 { &vnop_blockmap_desc
, (vnop_t
*)nfs_vnop_blockmap
}, /* blockmap */
400 { &vnop_monitor_desc
, (vnop_t
*)nfs_vnop_monitor
}, /* monitor */
403 const struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc
=
404 { &fifo_nfsv2nodeop_p
, fifo_nfsv2nodeop_entries
};
409 vnop_t
**fifo_nfsv4nodeop_p
;
410 static const struct vnodeopv_entry_desc fifo_nfsv4nodeop_entries
[] = {
411 { &vnop_default_desc
, (vnop_t
*)vn_default_error
},
412 { &vnop_lookup_desc
, (vnop_t
*)fifo_lookup
}, /* lookup */
413 { &vnop_create_desc
, (vnop_t
*)fifo_create
}, /* create */
414 { &vnop_mknod_desc
, (vnop_t
*)fifo_mknod
}, /* mknod */
415 { &vnop_open_desc
, (vnop_t
*)fifo_open
}, /* open */
416 { &vnop_close_desc
, (vnop_t
*)nfsfifo_vnop_close
}, /* close */
417 { &vnop_getattr_desc
, (vnop_t
*)nfs4_vnop_getattr
}, /* getattr */
418 { &vnop_setattr_desc
, (vnop_t
*)nfs_vnop_setattr
}, /* setattr */
419 { &vnop_read_desc
, (vnop_t
*)nfsfifo_vnop_read
}, /* read */
420 { &vnop_write_desc
, (vnop_t
*)nfsfifo_vnop_write
}, /* write */
421 { &vnop_ioctl_desc
, (vnop_t
*)fifo_ioctl
}, /* ioctl */
422 { &vnop_select_desc
, (vnop_t
*)fifo_select
}, /* select */
423 { &vnop_revoke_desc
, (vnop_t
*)fifo_revoke
}, /* revoke */
424 { &vnop_mmap_desc
, (vnop_t
*)fifo_mmap
}, /* mmap */
425 { &vnop_fsync_desc
, (vnop_t
*)nfs_vnop_fsync
}, /* fsync */
426 { &vnop_remove_desc
, (vnop_t
*)fifo_remove
}, /* remove */
427 { &vnop_link_desc
, (vnop_t
*)fifo_link
}, /* link */
428 { &vnop_rename_desc
, (vnop_t
*)fifo_rename
}, /* rename */
429 { &vnop_mkdir_desc
, (vnop_t
*)fifo_mkdir
}, /* mkdir */
430 { &vnop_rmdir_desc
, (vnop_t
*)fifo_rmdir
}, /* rmdir */
431 { &vnop_symlink_desc
, (vnop_t
*)fifo_symlink
}, /* symlink */
432 { &vnop_readdir_desc
, (vnop_t
*)fifo_readdir
}, /* readdir */
433 { &vnop_readlink_desc
, (vnop_t
*)fifo_readlink
}, /* readlink */
434 { &vnop_inactive_desc
, (vnop_t
*)nfs_vnop_inactive
}, /* inactive */
435 { &vnop_reclaim_desc
, (vnop_t
*)nfs_vnop_reclaim
}, /* reclaim */
436 { &vnop_strategy_desc
, (vnop_t
*)fifo_strategy
}, /* strategy */
437 { &vnop_pathconf_desc
, (vnop_t
*)fifo_pathconf
}, /* pathconf */
438 { &vnop_advlock_desc
, (vnop_t
*)fifo_advlock
}, /* advlock */
439 { &vnop_bwrite_desc
, (vnop_t
*)vn_bwrite
}, /* bwrite */
440 { &vnop_pagein_desc
, (vnop_t
*)nfs_vnop_pagein
}, /* Pagein */
441 { &vnop_pageout_desc
, (vnop_t
*)nfs_vnop_pageout
}, /* Pageout */
442 { &vnop_blktooff_desc
, (vnop_t
*)nfs_vnop_blktooff
}, /* blktooff */
443 { &vnop_offtoblk_desc
, (vnop_t
*)nfs_vnop_offtoblk
}, /* offtoblk */
444 { &vnop_blockmap_desc
, (vnop_t
*)nfs_vnop_blockmap
}, /* blockmap */
445 { &vnop_getxattr_desc
, (vnop_t
*)nfs4_vnop_getxattr
}, /* getxattr */
446 { &vnop_setxattr_desc
, (vnop_t
*)nfs4_vnop_setxattr
}, /* setxattr */
447 { &vnop_removexattr_desc
, (vnop_t
*)nfs4_vnop_removexattr
},/* removexattr */
448 { &vnop_listxattr_desc
, (vnop_t
*)nfs4_vnop_listxattr
},/* listxattr */
450 { &vnop_getnamedstream_desc
, (vnop_t
*)nfs4_vnop_getnamedstream
}, /* getnamedstream */
451 { &vnop_makenamedstream_desc
, (vnop_t
*)nfs4_vnop_makenamedstream
}, /* makenamedstream */
452 { &vnop_removenamedstream_desc
, (vnop_t
*)nfs4_vnop_removenamedstream
},/* removenamedstream */
454 { &vnop_monitor_desc
, (vnop_t
*)nfs_vnop_monitor
}, /* monitor */
457 const struct vnodeopv_desc fifo_nfsv4nodeop_opv_desc
=
458 { &fifo_nfsv4nodeop_p
, fifo_nfsv4nodeop_entries
};
460 #endif /* CONFIG_NFS4 */
462 int nfs_sillyrename(nfsnode_t
, nfsnode_t
, struct componentname
*, vfs_context_t
);
463 int nfs_getattr_internal(nfsnode_t
, struct nfs_vattr
*, vfs_context_t
, int);
464 int nfs_refresh_fh(nfsnode_t
, vfs_context_t
);
468 * Update nfsnode attributes to avoid extra getattr calls for each direntry.
469 * This function should be called only if RDIRPLUS flag is enabled.
472 nfs_rdirplus_update_node_attrs(nfsnode_t dnp
, struct direntry
*dp
, fhandle_t
*fhp
, struct nfs_vattr
*nvattrp
, uint64_t *savedxidp
)
475 struct componentname cn
;
476 int isdot
= (dp
->d_namlen
== 1) && (dp
->d_name
[0] == '.');
477 int isdotdot
= (dp
->d_namlen
== 2) && (dp
->d_name
[0] == '.') && (dp
->d_name
[1] == '.');
479 if (isdot
|| isdotdot
) {
484 bzero(&cn
, sizeof(cn
));
485 cn
.cn_nameptr
= dp
->d_name
;
486 cn
.cn_namelen
= dp
->d_namlen
;
487 cn
.cn_nameiop
= LOOKUP
;
489 nfs_nget(NFSTOMP(dnp
), dnp
, &cn
, fhp
->fh_data
, fhp
->fh_len
, nvattrp
, savedxidp
, RPCAUTH_UNKNOWN
, NG_NOCREATE
, &np
);
492 vnode_put(NFSTOV(np
));
497 * Find the slot in the access cache for this UID.
498 * If adding and no existing slot is found, reuse slots in FIFO order.
499 * The index of the next slot to use is kept in the last entry of the n_access array.
502 nfs_node_access_slot(nfsnode_t np
, uid_t uid
, int add
)
506 for (slot
= 0; slot
< NFS_ACCESS_CACHE_SIZE
; slot
++) {
507 if (np
->n_accessuid
[slot
] == uid
) {
511 if (slot
== NFS_ACCESS_CACHE_SIZE
) {
515 slot
= np
->n_access
[NFS_ACCESS_CACHE_SIZE
];
516 np
->n_access
[NFS_ACCESS_CACHE_SIZE
] = (slot
+ 1) % NFS_ACCESS_CACHE_SIZE
;
522 nfs3_access_rpc(nfsnode_t np
, u_int32_t
*access
, int rpcflags
, vfs_context_t ctx
)
524 int error
= 0, lockerror
= ENOENT
, status
, slot
;
525 uint32_t access_result
= 0;
527 struct nfsm_chain nmreq
, nmrep
;
528 struct nfsmount
*nmp
;
532 nfsm_chain_null(&nmreq
);
533 nfsm_chain_null(&nmrep
);
535 nfsm_chain_build_alloc_init(error
, &nmreq
, NFSX_FH(NFS_VER3
) + NFSX_UNSIGNED
);
536 nfsm_chain_add_fh(error
, &nmreq
, NFS_VER3
, np
->n_fhp
, np
->n_fhsize
);
537 nfsm_chain_add_32(error
, &nmreq
, *access
);
538 nfsm_chain_build_done(error
, &nmreq
);
540 error
= nfs_request2(np
, NULL
, &nmreq
, NFSPROC_ACCESS
,
541 vfs_context_thread(ctx
), vfs_context_ucred(ctx
),
542 NULL
, rpcflags
, &nmrep
, &xid
, &status
);
543 if ((lockerror
= nfs_node_lock(np
))) {
546 nfsm_chain_postop_attr_update(error
, &nmrep
, np
, &xid
);
550 nfsm_chain_get_32(error
, &nmrep
, access_result
);
553 /* XXXab do we really need mount here, also why are we doing access cache management here? */
555 if (nfs_mount_gone(nmp
)) {
561 if (auth_is_kerberized(np
->n_auth
) || auth_is_kerberized(nmp
->nm_auth
)) {
562 uid
= nfs_cred_getasid2uid(vfs_context_ucred(ctx
));
564 uid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
567 uid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
568 #endif /* CONFIG_NFS_GSS */
569 slot
= nfs_node_access_slot(np
, uid
, 1);
570 np
->n_accessuid
[slot
] = uid
;
572 np
->n_accessstamp
[slot
] = now
.tv_sec
;
573 np
->n_access
[slot
] = access_result
;
576 * If we asked for DELETE but didn't get it, the server
577 * may simply not support returning that bit (possible
578 * on UNIX systems). So, we'll assume that it is OK,
579 * and just let any subsequent delete action fail if it
580 * really isn't deletable.
582 if ((*access
& NFS_ACCESS_DELETE
) &&
583 !(np
->n_access
[slot
] & NFS_ACCESS_DELETE
)) {
584 np
->n_access
[slot
] |= NFS_ACCESS_DELETE
;
586 /* ".zfs" subdirectories may erroneously give a denied answer for add/remove */
587 if (nfs_access_dotzfs
&& (np
->n_flag
& NISDOTZFSCHILD
)) {
588 np
->n_access
[slot
] |= (NFS_ACCESS_MODIFY
| NFS_ACCESS_EXTEND
| NFS_ACCESS_DELETE
);
590 /* pass back the access returned with this request */
591 *access
= np
->n_access
[slot
];
596 nfsm_chain_cleanup(&nmreq
);
597 nfsm_chain_cleanup(&nmrep
);
603 * NFS access vnode op.
604 * For NFS version 2, just return ok. File accesses may fail later.
605 * For NFS version 3+, use the access RPC to check accessibility. If file
606 * permissions are changed on the server, accesses might still fail later.
610 struct vnop_access_args
/* {
611 * struct vnodeop_desc *a_desc;
614 * vfs_context_t a_context;
617 vfs_context_t ctx
= ap
->a_context
;
618 vnode_t vp
= ap
->a_vp
;
619 int error
= 0, slot
, dorpc
, rpcflags
= 0;
620 u_int32_t access
, waccess
;
621 nfsnode_t np
= VTONFS(vp
);
622 struct nfsmount
*nmp
;
628 if (nfs_mount_gone(nmp
)) {
631 nfsvers
= nmp
->nm_vers
;
634 if (nfsvers
== NFS_VER2
|| NMFLAG(nmp
, NOOPAQUE_AUTH
)) {
635 if ((ap
->a_action
& KAUTH_VNODE_WRITE_RIGHTS
) &&
636 vfs_isrdonly(vnode_mount(vp
))) {
643 * For NFS v3, do an access rpc, otherwise you are stuck emulating
644 * ufs_access() locally using the vattr. This may not be correct,
645 * since the server may apply other access criteria such as
646 * client uid-->server uid mapping that we do not know about, but
647 * this is better than just returning anything that is lying about
652 * Convert KAUTH primitives to NFS access rights.
655 if (vnode_isdir(vp
)) {
658 (KAUTH_VNODE_LIST_DIRECTORY
|
659 KAUTH_VNODE_READ_EXTATTRIBUTES
)) {
660 access
|= NFS_ACCESS_READ
;
662 if (ap
->a_action
& KAUTH_VNODE_SEARCH
) {
663 access
|= NFS_ACCESS_LOOKUP
;
666 (KAUTH_VNODE_ADD_FILE
|
667 KAUTH_VNODE_ADD_SUBDIRECTORY
)) {
668 access
|= NFS_ACCESS_MODIFY
| NFS_ACCESS_EXTEND
;
670 if (ap
->a_action
& KAUTH_VNODE_DELETE_CHILD
) {
671 access
|= NFS_ACCESS_MODIFY
;
676 (KAUTH_VNODE_READ_DATA
|
677 KAUTH_VNODE_READ_EXTATTRIBUTES
)) {
678 access
|= NFS_ACCESS_READ
;
680 if (ap
->a_action
& KAUTH_VNODE_WRITE_DATA
) {
681 access
|= NFS_ACCESS_MODIFY
| NFS_ACCESS_EXTEND
;
683 if (ap
->a_action
& KAUTH_VNODE_APPEND_DATA
) {
684 access
|= NFS_ACCESS_EXTEND
;
686 if (ap
->a_action
& KAUTH_VNODE_EXECUTE
) {
687 access
|= NFS_ACCESS_EXECUTE
;
691 if (ap
->a_action
& KAUTH_VNODE_DELETE
) {
692 access
|= NFS_ACCESS_DELETE
;
695 (KAUTH_VNODE_WRITE_ATTRIBUTES
|
696 KAUTH_VNODE_WRITE_EXTATTRIBUTES
|
697 KAUTH_VNODE_WRITE_SECURITY
)) {
698 access
|= NFS_ACCESS_MODIFY
;
700 /* XXX this is pretty dubious */
701 if (ap
->a_action
& KAUTH_VNODE_CHANGE_OWNER
) {
702 access
|= NFS_ACCESS_MODIFY
;
705 /* if caching, always ask for every right */
706 if (nfs_access_cache_timeout
> 0) {
707 waccess
= NFS_ACCESS_READ
| NFS_ACCESS_MODIFY
|
708 NFS_ACCESS_EXTEND
| NFS_ACCESS_EXECUTE
|
709 NFS_ACCESS_DELETE
| NFS_ACCESS_LOOKUP
;
714 if ((error
= nfs_node_lock(np
))) {
719 * Does our cached result allow us to give a definite yes to
723 if (auth_is_kerberized(np
->n_auth
) || auth_is_kerberized(nmp
->nm_auth
)) {
724 uid
= nfs_cred_getasid2uid(vfs_context_ucred(ctx
));
726 uid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
729 uid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
730 #endif /* CONFIG_NFS_GSS */
731 slot
= nfs_node_access_slot(np
, uid
, 0);
734 /* not asking for any rights understood by NFS, so don't bother doing an RPC */
735 /* OSAddAtomic(1, &nfsstats.accesscache_hits); */
738 } else if (NACCESSVALID(np
, slot
)) {
740 if (((now
.tv_sec
< (np
->n_accessstamp
[slot
] + nfs_access_cache_timeout
)) &&
741 ((np
->n_access
[slot
] & access
) == access
)) || nfs_use_cache(nmp
)) {
742 /* OSAddAtomic(1, &nfsstats.accesscache_hits); */
744 waccess
= np
->n_access
[slot
];
749 /* Either a no, or a don't know. Go to the wire. */
750 /* OSAddAtomic(1, &nfsstats.accesscache_misses); */
753 * Allow an access call to timeout if we have it cached
754 * so we won't hang if the server isn't responding.
756 if (NACCESSVALID(np
, slot
)) {
760 error
= nmp
->nm_funcs
->nf_access_rpc(np
, &waccess
, rpcflags
, ctx
);
763 * If the server didn't respond return the cached access.
765 if ((error
== ETIMEDOUT
) && (rpcflags
& R_SOFT
)) {
767 waccess
= np
->n_access
[slot
];
770 if (!error
&& ((waccess
& access
) != access
)) {
781 * Perform various update/invalidation checks and then add the
782 * open to the node. Regular files will have an open file structure
783 * on the node and, for NFSv4, perform an OPEN request on the server.
787 struct vnop_open_args
/* {
788 * struct vnodeop_desc *a_desc;
791 * vfs_context_t a_context;
794 vfs_context_t ctx
= ap
->a_context
;
795 vnode_t vp
= ap
->a_vp
;
796 nfsnode_t np
= VTONFS(vp
);
797 struct nfsmount
*nmp
;
798 int error
, accessMode
, denyMode
, opened
= 0;
799 struct nfs_open_owner
*noop
= NULL
;
800 struct nfs_open_file
*nofp
= NULL
;
803 if (!(ap
->a_mode
& (FREAD
| FWRITE
))) {
808 if (nfs_mount_gone(nmp
)) {
811 if (np
->n_flag
& NREVOKE
) {
815 vtype
= vnode_vtype(vp
);
816 if ((vtype
!= VREG
) && (vtype
!= VDIR
) && (vtype
!= VLNK
)) {
820 /* First, check if we need to update/invalidate */
821 if (ISSET(np
->n_flag
, NUPDATESIZE
)) {
822 nfs_data_update_size(np
, 0);
824 if ((error
= nfs_node_lock(np
))) {
827 if (np
->n_flag
& NNEEDINVALIDATE
) {
828 np
->n_flag
&= ~NNEEDINVALIDATE
;
833 nfs_vinvalbuf(vp
, V_SAVE
| V_IGNORE_WRITEERR
, ctx
, 1);
834 if ((error
= nfs_node_lock(np
))) {
839 np
->n_lastrahead
= -1;
841 if (np
->n_flag
& NMODIFIED
) {
846 if ((error
= nfs_vinvalbuf(vp
, V_SAVE
| V_IGNORE_WRITEERR
, ctx
, 1))) {
853 /* nfs_getattr() will check changed and purge caches */
854 if ((error
= nfs_getattr(np
, NULL
, ctx
, NGA_UNCACHED
))) {
859 /* Just mark that it was opened */
860 lck_mtx_lock(&np
->n_openlock
);
862 lck_mtx_unlock(&np
->n_openlock
);
866 /* mode contains some combination of: FREAD, FWRITE, O_SHLOCK, O_EXLOCK */
868 if (ap
->a_mode
& FREAD
) {
869 accessMode
|= NFS_OPEN_SHARE_ACCESS_READ
;
871 if (ap
->a_mode
& FWRITE
) {
872 accessMode
|= NFS_OPEN_SHARE_ACCESS_WRITE
;
874 if (ap
->a_mode
& O_EXLOCK
) {
875 denyMode
= NFS_OPEN_SHARE_DENY_BOTH
;
876 } else if (ap
->a_mode
& O_SHLOCK
) {
877 denyMode
= NFS_OPEN_SHARE_DENY_WRITE
;
879 denyMode
= NFS_OPEN_SHARE_DENY_NONE
;
881 // XXX don't do deny modes just yet (and never do it for !v4)
882 denyMode
= NFS_OPEN_SHARE_DENY_NONE
;
884 noop
= nfs_open_owner_find(nmp
, vfs_context_ucred(ctx
), 1);
890 error
= nfs_mount_state_in_use_start(nmp
, vfs_context_thread(ctx
));
892 nfs_open_owner_rele(noop
);
895 if (np
->n_flag
& NREVOKE
) {
897 nfs_mount_state_in_use_end(nmp
, 0);
898 nfs_open_owner_rele(noop
);
902 error
= nfs_open_file_find(np
, noop
, &nofp
, accessMode
, denyMode
, 1);
903 if (!error
&& (nofp
->nof_flags
& NFS_OPEN_FILE_LOST
)) {
904 NP(np
, "nfs_vnop_open: LOST %d", kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
908 if (!error
&& (nofp
->nof_flags
& NFS_OPEN_FILE_REOPEN
)) {
909 nfs_mount_state_in_use_end(nmp
, 0);
910 error
= nfs4_reopen(nofp
, vfs_context_thread(ctx
));
918 error
= nfs_open_file_set_busy(nofp
, vfs_context_thread(ctx
));
925 if (nmp
->nm_vers
< NFS_VER4
) {
927 * NFS v2/v3 opens are always allowed - so just add it.
929 nfs_open_file_add_open(nofp
, accessMode
, denyMode
, 0);
934 * If we just created the file and the modes match, then we simply use
935 * the open performed in the create. Otherwise, send the request.
937 if ((nofp
->nof_flags
& NFS_OPEN_FILE_CREATE
) &&
938 (nofp
->nof_creator
== current_thread()) &&
939 (accessMode
== NFS_OPEN_SHARE_ACCESS_BOTH
) &&
940 (denyMode
== NFS_OPEN_SHARE_DENY_NONE
)) {
941 nofp
->nof_flags
&= ~NFS_OPEN_FILE_CREATE
;
942 nofp
->nof_creator
= NULL
;
946 error
= nfs4_open(np
, nofp
, accessMode
, denyMode
, ctx
);
949 if ((error
== EACCES
) && (nofp
->nof_flags
& NFS_OPEN_FILE_CREATE
) &&
950 (nofp
->nof_creator
== current_thread())) {
952 * Ugh. This can happen if we just created the file with read-only
953 * perms and we're trying to open it for real with different modes
954 * (e.g. write-only or with a deny mode) and the server decides to
955 * not allow the second open because of the read-only perms.
956 * The best we can do is to just use the create's open.
957 * We may have access we don't need or we may not have a requested
958 * deny mode. We may log complaints later, but we'll try to avoid it.
960 if (denyMode
!= NFS_OPEN_SHARE_DENY_NONE
) {
961 NP(np
, "nfs_vnop_open: deny mode foregone on create, %d", kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
963 nofp
->nof_creator
= NULL
;
971 * If we had just created the file, we already had it open.
972 * If the actual open mode is less than what we grabbed at
973 * create time, then we'll downgrade the open here.
975 if ((nofp
->nof_flags
& NFS_OPEN_FILE_CREATE
) &&
976 (nofp
->nof_creator
== current_thread())) {
977 error
= nfs_close(np
, nofp
, NFS_OPEN_SHARE_ACCESS_BOTH
, NFS_OPEN_SHARE_DENY_NONE
, ctx
);
979 NP(np
, "nfs_vnop_open: create close error %d, %d", error
, kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
981 if (!nfs_mount_state_error_should_restart(error
)) {
983 nofp
->nof_flags
&= ~NFS_OPEN_FILE_CREATE
;
990 nfs_open_file_clear_busy(nofp
);
992 if (nfs_mount_state_in_use_end(nmp
, error
)) {
997 NP(np
, "nfs_vnop_open: error %d, %d", error
, kauth_cred_getuid(noop
->noo_cred
));
1000 nfs_open_owner_rele(noop
);
1002 if (!error
&& vtype
== VREG
&& (ap
->a_mode
& FWRITE
)) {
1003 lck_mtx_lock(&nmp
->nm_lock
);
1004 nmp
->nm_state
&= ~NFSSTA_SQUISHY
;
1005 nmp
->nm_curdeadtimeout
= nmp
->nm_deadtimeout
;
1006 if (nmp
->nm_curdeadtimeout
<= 0) {
1007 nmp
->nm_deadto_start
= 0;
1010 lck_mtx_unlock(&nmp
->nm_lock
);
1017 nfs_no_of_open_file_writers(nfsnode_t np
)
1019 uint32_t writers
= 0;
1020 struct nfs_open_file
*nofp
;
1022 TAILQ_FOREACH(nofp
, &np
->n_opens
, nof_link
) {
1023 writers
+= nofp
->nof_w
+ nofp
->nof_rw
+ nofp
->nof_w_dw
+ nofp
->nof_rw_dw
+
1024 nofp
->nof_w_drw
+ nofp
->nof_rw_drw
+ nofp
->nof_d_w_dw
+
1025 nofp
->nof_d_rw_dw
+ nofp
->nof_d_w_drw
+ nofp
->nof_d_rw_drw
+
1026 nofp
->nof_d_w
+ nofp
->nof_d_rw
;
1033 * NFS close vnode op
1035 * What an NFS client should do upon close after writing is a debatable issue.
1036 * Most NFS clients push delayed writes to the server upon close, basically for
1038 * 1 - So that any write errors may be reported back to the client process
1039 * doing the close system call. By far the two most likely errors are
1040 * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
1041 * 2 - To put a worst case upper bound on cache inconsistency between
1042 * multiple clients for the file.
1043 * There is also a consistency problem for Version 2 of the protocol w.r.t.
1044 * not being able to tell if other clients are writing a file concurrently,
1045 * since there is no way of knowing if the changed modify time in the reply
1046 * is only due to the write for this client.
1047 * (NFS Version 3 provides weak cache consistency data in the reply that
1048 * should be sufficient to detect and handle this case.)
1050 * The current code does the following:
1051 * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
1052 * for NFS Version 3 - flush dirty buffers to the server but don't invalidate them.
1053 * for NFS Version 4 - basically the same as NFSv3
1057 struct vnop_close_args
/* {
1058 * struct vnodeop_desc *a_desc;
1061 * vfs_context_t a_context;
1064 vfs_context_t ctx
= ap
->a_context
;
1065 vnode_t vp
= ap
->a_vp
;
1066 nfsnode_t np
= VTONFS(vp
);
1067 struct nfsmount
*nmp
;
1068 int error
= 0, error1
, nfsvers
;
1069 int fflag
= ap
->a_fflag
;
1071 int accessMode
, denyMode
;
1072 struct nfs_open_owner
*noop
= NULL
;
1073 struct nfs_open_file
*nofp
= NULL
;
1079 nfsvers
= nmp
->nm_vers
;
1080 vtype
= vnode_vtype(vp
);
1082 /* First, check if we need to update/flush/invalidate */
1083 if (ISSET(np
->n_flag
, NUPDATESIZE
)) {
1084 nfs_data_update_size(np
, 0);
1086 nfs_node_lock_force(np
);
1087 if (np
->n_flag
& NNEEDINVALIDATE
) {
1088 np
->n_flag
&= ~NNEEDINVALIDATE
;
1089 nfs_node_unlock(np
);
1090 nfs_vinvalbuf(vp
, V_SAVE
| V_IGNORE_WRITEERR
, ctx
, 1);
1091 nfs_node_lock_force(np
);
1093 if ((vtype
== VREG
) && (np
->n_flag
& NMODIFIED
) && (fflag
& FWRITE
)) {
1094 /* we're closing an open for write and the file is modified, so flush it */
1095 nfs_node_unlock(np
);
1096 if (nfsvers
!= NFS_VER2
) {
1097 error
= nfs_flush(np
, MNT_WAIT
, vfs_context_thread(ctx
), 0);
1099 error
= nfs_vinvalbuf(vp
, V_SAVE
, ctx
, 1);
1101 nfs_node_lock_force(np
);
1102 NATTRINVALIDATE(np
);
1104 if (np
->n_flag
& NWRITEERR
) {
1105 np
->n_flag
&= ~NWRITEERR
;
1106 error
= np
->n_error
;
1108 nfs_node_unlock(np
);
1110 if (vtype
!= VREG
) {
1111 /* Just mark that it was closed */
1112 lck_mtx_lock(&np
->n_openlock
);
1113 if (np
->n_openrefcnt
== 0) {
1114 if (fflag
& (FREAD
| FWRITE
)) {
1115 NP(np
, "nfs_vnop_close: open reference underrun");
1118 } else if (fflag
& (FREAD
| FWRITE
)) {
1121 /* No FREAD/FWRITE set - probably the final close */
1122 np
->n_openrefcnt
= 0;
1124 lck_mtx_unlock(&np
->n_openlock
);
1129 /* fflag should contain some combination of: FREAD, FWRITE, FHASLOCK */
1131 if (fflag
& FREAD
) {
1132 accessMode
|= NFS_OPEN_SHARE_ACCESS_READ
;
1134 if (fflag
& FWRITE
) {
1135 accessMode
|= NFS_OPEN_SHARE_ACCESS_WRITE
;
1137 // XXX It would be nice if we still had the O_EXLOCK/O_SHLOCK flags that were on the open
1138 // if (fflag & O_EXLOCK)
1139 // denyMode = NFS_OPEN_SHARE_DENY_BOTH;
1140 // else if (fflag & O_SHLOCK)
1141 // denyMode = NFS_OPEN_SHARE_DENY_WRITE;
1143 // denyMode = NFS_OPEN_SHARE_DENY_NONE;
1145 if (fflag
& FHASLOCK
) {
1146 /* XXX assume FHASLOCK is for the deny mode and not flock */
1147 /* FHASLOCK flock will be unlocked in the close path, but the flag is not cleared. */
1148 if (nofp
->nof_deny
& NFS_OPEN_SHARE_DENY_READ
) {
1149 denyMode
= NFS_OPEN_SHARE_DENY_BOTH
;
1150 } else if (nofp
->nof_deny
& NFS_OPEN_SHARE_DENY_WRITE
) {
1151 denyMode
= NFS_OPEN_SHARE_DENY_WRITE
;
1153 denyMode
= NFS_OPEN_SHARE_DENY_NONE
;
1156 denyMode
= NFS_OPEN_SHARE_DENY_NONE
;
1159 // XXX don't do deny modes just yet (and never do it for !v4)
1160 denyMode
= NFS_OPEN_SHARE_DENY_NONE
;
1165 * No mode given to close?
1166 * Guess this is the final close.
1167 * We should unlock all locks and close all opens.
1170 mount_t mp
= vnode_mount(vp
);
1171 int force
= (!mp
|| vfs_isforce(mp
));
1173 writers
= nfs_no_of_open_file_writers(np
);
1174 nfs_release_open_state_for_node(np
, force
);
1176 lck_mtx_lock(&nmp
->nm_lock
);
1177 if (writers
> nmp
->nm_writers
) {
1178 NP(np
, "nfs_vnop_close: number of write opens for mount underrun. Node has %d"
1179 " opens for write. Mount has total of %d opens for write\n",
1180 writers
, nmp
->nm_writers
);
1181 nmp
->nm_writers
= 0;
1183 nmp
->nm_writers
-= writers
;
1185 lck_mtx_unlock(&nmp
->nm_lock
);
1189 } else if (fflag
& FWRITE
) {
1190 lck_mtx_lock(&nmp
->nm_lock
);
1191 if (nmp
->nm_writers
== 0) {
1192 NP(np
, "nfs_vnop_close: removing open writer from mount, but mount has no files open for writing");
1196 lck_mtx_unlock(&nmp
->nm_lock
);
1200 noop
= nfs_open_owner_find(nmp
, vfs_context_ucred(ctx
), 0);
1202 // printf("nfs_vnop_close: can't get open owner!\n");
1207 error
= nfs_mount_state_in_use_start(nmp
, NULL
);
1209 nfs_open_owner_rele(noop
);
1213 error
= nfs_open_file_find(np
, noop
, &nofp
, 0, 0, 0);
1215 if (!error
&& (nofp
->nof_flags
& NFS_OPEN_FILE_REOPEN
)) {
1216 nfs_mount_state_in_use_end(nmp
, 0);
1217 error
= nfs4_reopen(nofp
, NULL
);
1225 NP(np
, "nfs_vnop_close: no open file for owner, error %d, %d", error
, kauth_cred_getuid(noop
->noo_cred
));
1229 error
= nfs_open_file_set_busy(nofp
, NULL
);
1235 error
= nfs_close(np
, nofp
, accessMode
, denyMode
, ctx
);
1237 NP(np
, "nfs_vnop_close: close error %d, %d", error
, kauth_cred_getuid(noop
->noo_cred
));
1242 nfs_open_file_clear_busy(nofp
);
1244 if (nfs_mount_state_in_use_end(nmp
, error
)) {
1252 NP(np
, "nfs_vnop_close: error %d, %d", error
, kauth_cred_getuid(noop
->noo_cred
));
1255 nfs_open_owner_rele(noop
);
1261 * nfs_close(): common function that does all the heavy lifting of file closure
1263 * Takes an open file structure and a set of access/deny modes and figures out how
1264 * to update the open file structure (and the state on the server) appropriately.
1269 struct nfs_open_file
*nofp
,
1270 uint32_t accessMode
,
1272 __unused vfs_context_t ctx
)
1275 struct nfs_lock_owner
*nlop
;
1277 int error
= 0, changed
= 0, delegated
= 0, closed
= 0, downgrade
= 0;
1278 uint32_t newAccessMode
, newDenyMode
;
1280 /* warn if modes don't match current state */
1281 if (((accessMode
& nofp
->nof_access
) != accessMode
) || ((denyMode
& nofp
->nof_deny
) != denyMode
)) {
1282 NP(np
, "nfs_close: mode mismatch %d %d, current %d %d, %d",
1283 accessMode
, denyMode
, nofp
->nof_access
, nofp
->nof_deny
,
1284 kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
1288 * If we're closing a write-only open, we may not have a write-only count
1289 * if we also grabbed read access. So, check the read-write count.
1291 if (denyMode
== NFS_OPEN_SHARE_DENY_NONE
) {
1292 if ((accessMode
== NFS_OPEN_SHARE_ACCESS_WRITE
) &&
1293 (nofp
->nof_w
== 0) && (nofp
->nof_d_w
== 0) &&
1294 (nofp
->nof_rw
|| nofp
->nof_d_rw
)) {
1295 accessMode
= NFS_OPEN_SHARE_ACCESS_BOTH
;
1297 } else if (denyMode
== NFS_OPEN_SHARE_DENY_WRITE
) {
1298 if ((accessMode
== NFS_OPEN_SHARE_ACCESS_WRITE
) &&
1299 (nofp
->nof_w_dw
== 0) && (nofp
->nof_d_w_dw
== 0) &&
1300 (nofp
->nof_rw_dw
|| nofp
->nof_d_rw_dw
)) {
1301 accessMode
= NFS_OPEN_SHARE_ACCESS_BOTH
;
1303 } else { /* NFS_OPEN_SHARE_DENY_BOTH */
1304 if ((accessMode
== NFS_OPEN_SHARE_ACCESS_WRITE
) &&
1305 (nofp
->nof_w_drw
== 0) && (nofp
->nof_d_w_drw
== 0) &&
1306 (nofp
->nof_rw_drw
|| nofp
->nof_d_rw_drw
)) {
1307 accessMode
= NFS_OPEN_SHARE_ACCESS_BOTH
;
1311 nfs_open_file_remove_open_find(nofp
, accessMode
, denyMode
, &newAccessMode
, &newDenyMode
, &delegated
);
1312 if ((newAccessMode
!= nofp
->nof_access
) || (newDenyMode
!= nofp
->nof_deny
)) {
1318 if (NFSTONMP(np
)->nm_vers
< NFS_VER4
) {
1319 /* NFS v2/v3 closes simply need to remove the open. */
1323 if ((newAccessMode
== 0) || (nofp
->nof_opencnt
== 1)) {
1325 * No more access after this close, so clean up and close it.
1326 * Don't send a close RPC if we're closing a delegated open.
1330 if (!delegated
&& !(nofp
->nof_flags
& NFS_OPEN_FILE_LOST
)) {
1331 error
= nfs4_close_rpc(np
, nofp
, vfs_context_thread(ctx
), vfs_context_ucred(ctx
), 0);
1333 if (error
== NFSERR_LOCKS_HELD
) {
1335 * Hmm... the server says we have locks we need to release first
1336 * Find the lock owner and try to unlock everything.
1338 nlop
= nfs_lock_owner_find(np
, vfs_context_proc(ctx
), 0);
1340 nfs4_unlock_rpc(np
, nlop
, F_WRLCK
, 0, UINT64_MAX
,
1341 0, vfs_context_thread(ctx
), vfs_context_ucred(ctx
));
1342 nfs_lock_owner_rele(nlop
);
1344 error
= nfs4_close_rpc(np
, nofp
, vfs_context_thread(ctx
), vfs_context_ucred(ctx
), 0);
1346 } else if (changed
) {
1348 * File is still open but with less access, so downgrade the open.
1349 * Don't send a downgrade RPC if we're closing a delegated open.
1351 if (!delegated
&& !(nofp
->nof_flags
& NFS_OPEN_FILE_LOST
)) {
1354 * If we have delegated opens, we should probably claim them before sending
1355 * the downgrade because the server may not know the open we are downgrading to.
1357 if (nofp
->nof_d_rw_drw
|| nofp
->nof_d_w_drw
|| nofp
->nof_d_r_drw
||
1358 nofp
->nof_d_rw_dw
|| nofp
->nof_d_w_dw
|| nofp
->nof_d_r_dw
||
1359 nofp
->nof_d_rw
|| nofp
->nof_d_w
|| nofp
->nof_d_r
) {
1360 nfs4_claim_delegated_state_for_open_file(nofp
, 0);
1362 /* need to remove the open before sending the downgrade */
1363 nfs_open_file_remove_open(nofp
, accessMode
, denyMode
);
1364 error
= nfs4_open_downgrade_rpc(np
, nofp
, ctx
);
1365 if (error
) { /* Hmm.. that didn't work. Add the open back in. */
1366 nfs_open_file_add_open(nofp
, accessMode
, denyMode
, delegated
);
1373 NP(np
, "nfs_close: error %d, %d", error
, kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
1378 nfs_open_file_remove_open(nofp
, accessMode
, denyMode
);
1382 lck_mtx_lock(&nofp
->nof_lock
);
1383 if (nofp
->nof_r
|| nofp
->nof_d_r
|| nofp
->nof_w
|| nofp
->nof_d_w
|| nofp
->nof_d_rw
||
1384 (nofp
->nof_rw
&& !((nofp
->nof_flags
& NFS_OPEN_FILE_CREATE
) && !nofp
->nof_creator
&& (nofp
->nof_rw
== 1))) ||
1385 nofp
->nof_r_dw
|| nofp
->nof_d_r_dw
|| nofp
->nof_w_dw
|| nofp
->nof_d_w_dw
||
1386 nofp
->nof_rw_dw
|| nofp
->nof_d_rw_dw
|| nofp
->nof_r_drw
|| nofp
->nof_d_r_drw
||
1387 nofp
->nof_w_drw
|| nofp
->nof_d_w_drw
|| nofp
->nof_rw_drw
|| nofp
->nof_d_rw_drw
) {
1388 NP(np
, "nfs_close: unexpected count: %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u flags 0x%x, %d",
1389 nofp
->nof_r
, nofp
->nof_d_r
, nofp
->nof_w
, nofp
->nof_d_w
,
1390 nofp
->nof_rw
, nofp
->nof_d_rw
, nofp
->nof_r_dw
, nofp
->nof_d_r_dw
,
1391 nofp
->nof_w_dw
, nofp
->nof_d_w_dw
, nofp
->nof_rw_dw
, nofp
->nof_d_rw_dw
,
1392 nofp
->nof_r_drw
, nofp
->nof_d_r_drw
, nofp
->nof_w_drw
, nofp
->nof_d_w_drw
,
1393 nofp
->nof_rw_drw
, nofp
->nof_d_rw_drw
, nofp
->nof_flags
,
1394 kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
1396 /* clear out all open info, just to be safe */
1397 nofp
->nof_access
= nofp
->nof_deny
= 0;
1398 nofp
->nof_mmap_access
= nofp
->nof_mmap_deny
= 0;
1399 nofp
->nof_r
= nofp
->nof_d_r
= 0;
1400 nofp
->nof_w
= nofp
->nof_d_w
= 0;
1401 nofp
->nof_rw
= nofp
->nof_d_rw
= 0;
1402 nofp
->nof_r_dw
= nofp
->nof_d_r_dw
= 0;
1403 nofp
->nof_w_dw
= nofp
->nof_d_w_dw
= 0;
1404 nofp
->nof_rw_dw
= nofp
->nof_d_rw_dw
= 0;
1405 nofp
->nof_r_drw
= nofp
->nof_d_r_drw
= 0;
1406 nofp
->nof_w_drw
= nofp
->nof_d_w_drw
= 0;
1407 nofp
->nof_rw_drw
= nofp
->nof_d_rw_drw
= 0;
1408 nofp
->nof_flags
&= ~NFS_OPEN_FILE_CREATE
;
1409 lck_mtx_unlock(&nofp
->nof_lock
);
1410 /* XXX we may potentially want to clean up idle/unused open file structures */
1412 if (nofp
->nof_flags
& NFS_OPEN_FILE_LOST
) {
1414 NP(np
, "nfs_close: LOST%s, %d", !nofp
->nof_opencnt
? " (last)" : "",
1415 kauth_cred_getuid(nofp
->nof_owner
->noo_cred
));
1430 struct nfs_vattr
*nvap
,
1433 struct nfsmount
*nmp
= mp
? VFSTONFS(mp
) : NFSTONMP(np
);
1434 int error
= 0, status
, nfsvers
, rpcflags
= 0;
1435 struct nfsm_chain nmreq
, nmrep
;
1437 if (nfs_mount_gone(nmp
)) {
1440 nfsvers
= nmp
->nm_vers
;
1442 if (flags
& NGA_MONITOR
) { /* vnode monitor requests should be soft */
1443 rpcflags
= R_RECOVER
;
1446 if (flags
& NGA_SOFT
) { /* Return ETIMEDOUT if server not responding */
1450 nfsm_chain_null(&nmreq
);
1451 nfsm_chain_null(&nmrep
);
1453 nfsm_chain_build_alloc_init(error
, &nmreq
, NFSX_FH(nfsvers
));
1454 if (nfsvers
!= NFS_VER2
) {
1455 nfsm_chain_add_32(error
, &nmreq
, fhsize
);
1457 nfsm_chain_add_opaque(error
, &nmreq
, fhp
, fhsize
);
1458 nfsm_chain_build_done(error
, &nmreq
);
1460 error
= nfs_request2(np
, mp
, &nmreq
, NFSPROC_GETATTR
,
1461 vfs_context_thread(ctx
), vfs_context_ucred(ctx
),
1462 NULL
, rpcflags
, &nmrep
, xidp
, &status
);
1467 error
= nfs_parsefattr(nmp
, &nmrep
, nfsvers
, nvap
);
1469 nfsm_chain_cleanup(&nmreq
);
1470 nfsm_chain_cleanup(&nmrep
);
1475 * nfs_refresh_fh will attempt to update the file handle for the node.
1477 * It only does this for symbolic links and regular files that are not currently opened.
1479 * On Success returns 0 and the nodes file handle is updated, or ESTALE on failure.
1482 nfs_refresh_fh(nfsnode_t np
, vfs_context_t ctx
)
1484 vnode_t dvp
, vp
= NFSTOV(np
);
1486 const char *v_name
= vnode_getname(vp
);
1488 int namelen
, fhsize
, refreshed
;
1489 int error
, wanted
= 0;
1491 struct timespec ts
= {.tv_sec
= 2, .tv_nsec
= 0};
1493 NFS_VNOP_DBG("vnode is %d\n", vnode_vtype(vp
));
1495 dvp
= vnode_parent(vp
);
1496 if ((vnode_vtype(vp
) != VREG
&& vnode_vtype(vp
) != VLNK
) ||
1497 v_name
== NULL
|| *v_name
== '\0' || dvp
== NULL
) {
1498 if (v_name
!= NULL
) {
1499 vnode_putname(v_name
);
1505 namelen
= strlen(v_name
);
1506 MALLOC(name
, char *, namelen
+ 1, M_TEMP
, M_WAITOK
);
1508 vnode_putname(v_name
);
1511 bcopy(v_name
, name
, namelen
+ 1);
1512 NFS_VNOP_DBG("Trying to refresh %s : %s\n", v_name
, name
);
1513 vnode_putname(v_name
);
1515 /* Allocate the maximum size file handle */
1516 MALLOC(fhp
, uint8_t *, NFS4_FHSIZE
, M_TEMP
, M_WAITOK
);
1522 if ((error
= nfs_node_lock(np
))) {
1528 fhsize
= np
->n_fhsize
;
1529 bcopy(np
->n_fhp
, fhp
, fhsize
);
1530 while (ISSET(np
->n_flag
, NREFRESH
)) {
1531 SET(np
->n_flag
, NREFRESHWANT
);
1532 NFS_VNOP_DBG("Waiting for refresh of %s\n", name
);
1533 msleep(np
, &np
->n_lock
, PZERO
- 1, "nfsrefreshwant", &ts
);
1534 if ((error
= nfs_sigintr(NFSTONMP(np
), NULL
, vfs_context_thread(ctx
), 0))) {
1538 refreshed
= error
? 0 : !NFS_CMPFH(np
, fhp
, fhsize
);
1539 SET(np
->n_flag
, NREFRESH
);
1540 nfs_node_unlock(np
);
1542 NFS_VNOP_DBG("error = %d, refreshed = %d\n", error
, refreshed
);
1543 if (error
|| refreshed
) {
1547 /* Check that there are no open references for this file */
1548 lck_mtx_lock(&np
->n_openlock
);
1549 if (np
->n_openrefcnt
|| !TAILQ_EMPTY(&np
->n_opens
) || !TAILQ_EMPTY(&np
->n_lock_owners
)) {
1551 struct nfs_open_file
*ofp
;
1553 TAILQ_FOREACH(ofp
, &np
->n_opens
, nof_link
) {
1554 cnt
+= ofp
->nof_opencnt
;
1557 lck_mtx_unlock(&np
->n_openlock
);
1558 NFS_VNOP_DBG("Can not refresh file handle for %s with open state\n", name
);
1559 NFS_VNOP_DBG("\topenrefcnt = %d, opens = %d lock_owners = %d\n",
1560 np
->n_openrefcnt
, cnt
, !TAILQ_EMPTY(&np
->n_lock_owners
));
1565 lck_mtx_unlock(&np
->n_openlock
);
1567 * Since the FH is currently stale we should not be able to
1568 * establish any open state until the FH is refreshed.
1571 error
= nfs_node_lock(np
);
1574 * Symlinks should never need invalidations and are holding
1575 * the one and only nfsbuf in an uncached acquired state
1576 * trying to do a readlink. So we will hang if we invalidate
1577 * in that case. Only in in the VREG case do we need to
1580 if (vnode_vtype(vp
) == VREG
) {
1581 np
->n_flag
&= ~NNEEDINVALIDATE
;
1582 nfs_node_unlock(np
);
1583 error
= nfs_vinvalbuf(vp
, V_IGNORE_WRITEERR
, ctx
, 1);
1585 NFS_VNOP_DBG("nfs_vinvalbuf returned %d\n", error
);
1589 nfs_node_unlock(np
);
1592 NFS_VNOP_DBG("Looking up %s\n", name
);
1593 error
= nfs_lookitup(dnp
, name
, namelen
, ctx
, &np
);
1595 NFS_VNOP_DBG("nfs_lookitup returned %d\n", error
);
1599 nfs_node_lock_force(np
);
1600 wanted
= ISSET(np
->n_flag
, NREFRESHWANT
);
1601 CLR(np
->n_flag
, NREFRESH
| NREFRESHWANT
);
1602 nfs_node_unlock(np
);
1608 NFS_VNOP_DBG("%s refreshed file handle\n", name
);
1614 return error
? ESTALE
: 0;
1618 nfs_getattr(nfsnode_t np
, struct nfs_vattr
*nvap
, vfs_context_t ctx
, int flags
)
1623 error
= nfs_getattr_internal(np
, nvap
, ctx
, flags
);
1624 if (error
== ESTALE
) {
1625 error
= nfs_refresh_fh(np
, ctx
);
1634 nfs_getattr_internal(nfsnode_t np
, struct nfs_vattr
*nvap
, vfs_context_t ctx
, int flags
)
1636 struct nfsmount
*nmp
;
1637 int error
= 0, nfsvers
, inprogset
= 0, wanted
= 0, avoidfloods
;
1638 struct nfs_vattr nvattr
;
1639 struct timespec ts
= { .tv_sec
= 2, .tv_nsec
= 0 };
1642 FSDBG_TOP(513, np
->n_size
, np
, np
->n_vattr
.nva_size
, np
->n_flag
);
1646 if (nfs_mount_gone(nmp
)) {
1649 nfsvers
= nmp
->nm_vers
;
1656 /* Update local times for special files. */
1657 if (np
->n_flag
& (NACC
| NUPD
)) {
1658 nfs_node_lock_force(np
);
1660 nfs_node_unlock(np
);
1662 /* Update size, if necessary */
1663 if (ISSET(np
->n_flag
, NUPDATESIZE
)) {
1664 nfs_data_update_size(np
, 0);
1667 error
= nfs_node_lock(np
);
1669 if (!(flags
& (NGA_UNCACHED
| NGA_MONITOR
)) || ((nfsvers
>= NFS_VER4
) && (np
->n_openflags
& N_DELEG_MASK
))) {
1671 * Use the cache or wait for any getattr in progress if:
1672 * - it's a cached request, or
1673 * - we have a delegation, or
1674 * - the server isn't responding
1677 error
= nfs_getattrcache(np
, nvap
, flags
);
1678 if (!error
|| (error
!= ENOENT
)) {
1679 nfs_node_unlock(np
);
1683 if (!ISSET(np
->n_flag
, NGETATTRINPROG
)) {
1686 if (flags
& NGA_MONITOR
) {
1687 /* no need to wait if a request is pending */
1688 error
= EINPROGRESS
;
1689 nfs_node_unlock(np
);
1692 SET(np
->n_flag
, NGETATTRWANT
);
1693 msleep(np
, &np
->n_lock
, PZERO
- 1, "nfsgetattrwant", &ts
);
1694 if ((error
= nfs_sigintr(NFSTONMP(np
), NULL
, vfs_context_thread(ctx
), 0))) {
1695 nfs_node_unlock(np
);
1699 SET(np
->n_flag
, NGETATTRINPROG
);
1701 } else if (!ISSET(np
->n_flag
, NGETATTRINPROG
)) {
1702 SET(np
->n_flag
, NGETATTRINPROG
);
1704 } else if (flags
& NGA_MONITOR
) {
1705 /* no need to make a request if one is pending */
1706 error
= EINPROGRESS
;
1708 nfs_node_unlock(np
);
1711 if (nfs_mount_gone(nmp
)) {
1719 * Return cached attributes if they are valid,
1720 * if the server doesn't respond, and this is
1721 * some softened up style of mount.
1723 if (NATTRVALID(np
) && nfs_use_cache(nmp
)) {
1728 * We might want to try to get both the attributes and access info by
1729 * making an ACCESS call and seeing if it returns updated attributes.
1730 * But don't bother if we aren't caching access info or if the
1731 * attributes returned wouldn't be cached.
1733 if (!(flags
& NGA_ACL
) && (nfsvers
!= NFS_VER2
) && nfs_access_for_getattr
&& (nfs_access_cache_timeout
> 0)) {
1734 if (nfs_attrcachetimeout(np
) > 0) {
1735 /* OSAddAtomic(1, &nfsstats.accesscache_misses); */
1736 u_int32_t access
= NFS_ACCESS_ALL
;
1739 /* Return cached attrs if server doesn't respond */
1740 if (flags
& NGA_SOFT
) {
1744 error
= nmp
->nm_funcs
->nf_access_rpc(np
, &access
, rpcflags
, ctx
);
1746 if (error
== ETIMEDOUT
) {
1753 nfs_node_lock_force(np
);
1754 error
= nfs_getattrcache(np
, nvap
, flags
);
1755 nfs_node_unlock(np
);
1756 if (!error
|| (error
!= ENOENT
)) {
1759 /* Well, that didn't work... just do a getattr... */
1767 error
= nmp
->nm_funcs
->nf_getattr_rpc(np
, NULL
, np
->n_fhp
, np
->n_fhsize
, flags
, ctx
, nvap
, &xid
);
1769 nfs_node_lock_force(np
);
1770 error
= nfs_loadattrcache(np
, nvap
, &xid
, 0);
1771 nfs_node_unlock(np
);
1775 * If the server didn't respond, return cached attributes.
1778 if ((flags
& NGA_SOFT
) && (error
== ETIMEDOUT
)) {
1779 nfs_node_lock_force(np
);
1780 error
= nfs_getattrcache(np
, nvap
, flags
);
1781 if (!error
|| (error
!= ENOENT
)) {
1782 nfs_node_unlock(np
);
1785 nfs_node_unlock(np
);
1789 if (!xid
) { /* out-of-order rpc - attributes were dropped */
1790 FSDBG(513, -1, np
, np
->n_xid
>> 32, np
->n_xid
);
1791 if (avoidfloods
++ < 20) {
1794 /* avoidfloods>1 is bizarre. at 20 pull the plug */
1795 /* just return the last attributes we got */
1798 nfs_node_lock_force(np
);
1800 wanted
= ISSET(np
->n_flag
, NGETATTRWANT
);
1801 CLR(np
->n_flag
, (NGETATTRINPROG
| NGETATTRWANT
));
1804 /* check if the node changed on us */
1805 vnode_t vp
= NFSTOV(np
);
1806 enum vtype vtype
= vnode_vtype(vp
);
1807 if ((vtype
== VDIR
) && NFS_CHANGED_NC(nfsvers
, np
, nvap
)) {
1808 FSDBG(513, -1, np
, 0, np
);
1809 np
->n_flag
&= ~NNEGNCENTRIES
;
1812 NFS_CHANGED_UPDATE_NC(nfsvers
, np
, nvap
);
1813 NFS_VNOP_DBG("Purge directory 0x%llx\n",
1814 (uint64_t)VM_KERNEL_ADDRPERM(vp
));
1816 if (NFS_CHANGED(nfsvers
, np
, nvap
)) {
1817 FSDBG(513, -1, np
, -1, np
);
1818 if (vtype
== VDIR
) {
1819 NFS_VNOP_DBG("Invalidate directory 0x%llx\n",
1820 (uint64_t)VM_KERNEL_ADDRPERM(vp
));
1823 nfs_node_unlock(np
);
1827 error
= nfs_vinvalbuf(vp
, V_SAVE
, ctx
, 1);
1828 FSDBG(513, -1, np
, -2, error
);
1830 nfs_node_lock_force(np
);
1831 NFS_CHANGED_UPDATE(nfsvers
, np
, nvap
);
1832 nfs_node_unlock(np
);
1835 nfs_node_unlock(np
);
1841 nfs_node_unlock(np
);
1847 if (nvap
== &nvattr
) {
1848 NVATTR_CLEANUP(nvap
);
1849 } else if (!(flags
& NGA_ACL
)) {
1850 /* make sure we don't return an ACL if it wasn't asked for */
1851 NFS_BITMAP_CLR(nvap
->nva_bitmap
, NFS_FATTR_ACL
);
1852 if (nvap
->nva_acl
) {
1853 kauth_acl_free(nvap
->nva_acl
);
1854 nvap
->nva_acl
= NULL
;
1857 FSDBG_BOT(513, np
->n_size
, error
, np
->n_vattr
.nva_size
, np
->n_flag
);
1863 * NFS getattr call from vfs.
1867 * The attributes we support over the wire.
1868 * We also get fsid but the vfs layer gets it out of the mount
1869 * structure after this calling us so there's no need to return it,
1870 * and Finder expects to call getattrlist just looking for the FSID
1871 * with out hanging on a non responsive server.
1873 #define NFS3_SUPPORTED_VATTRS \
1874 (VNODE_ATTR_va_rdev | \
1875 VNODE_ATTR_va_nlink | \
1876 VNODE_ATTR_va_data_size | \
1877 VNODE_ATTR_va_data_alloc | \
1878 VNODE_ATTR_va_uid | \
1879 VNODE_ATTR_va_gid | \
1880 VNODE_ATTR_va_mode | \
1881 VNODE_ATTR_va_modify_time | \
1882 VNODE_ATTR_va_change_time | \
1883 VNODE_ATTR_va_access_time | \
1884 VNODE_ATTR_va_fileid | \
1890 struct vnop_getattr_args
/* {
1891 * struct vnodeop_desc *a_desc;
1893 * struct vnode_attr *a_vap;
1894 * vfs_context_t a_context;
1899 uint64_t supported_attrs
;
1900 struct nfs_vattr nva
;
1901 struct vnode_attr
*vap
= ap
->a_vap
;
1902 struct nfsmount
*nmp
;
1905 nmp
= VTONMP(ap
->a_vp
);
1908 * Lets don't go over the wire if we don't support any of the attributes.
1909 * Just fall through at the VFS layer and let it cons up what it needs.
1911 /* Return the io size no matter what, since we don't go over the wire for this */
1912 VATTR_RETURN(vap
, va_iosize
, nfs_iosize
);
1914 supported_attrs
= NFS3_SUPPORTED_VATTRS
;
1916 if ((vap
->va_active
& supported_attrs
) == 0) {
1920 if (VATTR_IS_ACTIVE(ap
->a_vap
, va_name
)) {
1921 NFS_VNOP_DBG("Getting attrs for 0x%llx, vname is %s\n",
1922 (uint64_t)VM_KERNEL_ADDRPERM(ap
->a_vp
),
1923 ap
->a_vp
->v_name
? ap
->a_vp
->v_name
: "empty");
1927 * We should not go over the wire if only fileid was requested and has ever been populated.
1929 if ((vap
->va_active
& supported_attrs
) == VNODE_ATTR_va_fileid
) {
1930 np
= VTONFS(ap
->a_vp
);
1931 if (np
->n_attrstamp
) {
1932 VATTR_RETURN(vap
, va_fileid
, np
->n_vattr
.nva_fileid
);
1937 error
= nfs_getattr(VTONFS(ap
->a_vp
), &nva
, ap
->a_context
, NGA_CACHED
);
1942 /* copy nva to *a_vap */
1943 VATTR_RETURN(vap
, va_type
, nva
.nva_type
);
1944 VATTR_RETURN(vap
, va_mode
, nva
.nva_mode
);
1945 rdev
= makedev(nva
.nva_rawdev
.specdata1
, nva
.nva_rawdev
.specdata2
);
1946 VATTR_RETURN(vap
, va_rdev
, rdev
);
1947 VATTR_RETURN(vap
, va_uid
, nva
.nva_uid
);
1948 VATTR_RETURN(vap
, va_gid
, nva
.nva_gid
);
1949 VATTR_RETURN(vap
, va_nlink
, nva
.nva_nlink
);
1950 VATTR_RETURN(vap
, va_fileid
, nva
.nva_fileid
);
1951 VATTR_RETURN(vap
, va_data_size
, nva
.nva_size
);
1952 VATTR_RETURN(vap
, va_data_alloc
, nva
.nva_bytes
);
1953 vap
->va_access_time
.tv_sec
= nva
.nva_timesec
[NFSTIME_ACCESS
];
1954 vap
->va_access_time
.tv_nsec
= nva
.nva_timensec
[NFSTIME_ACCESS
];
1955 VATTR_SET_SUPPORTED(vap
, va_access_time
);
1956 vap
->va_modify_time
.tv_sec
= nva
.nva_timesec
[NFSTIME_MODIFY
];
1957 vap
->va_modify_time
.tv_nsec
= nva
.nva_timensec
[NFSTIME_MODIFY
];
1958 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
1959 vap
->va_change_time
.tv_sec
= nva
.nva_timesec
[NFSTIME_CHANGE
];
1960 vap
->va_change_time
.tv_nsec
= nva
.nva_timensec
[NFSTIME_CHANGE
];
1961 VATTR_SET_SUPPORTED(vap
, va_change_time
);
1964 // VATTR_RETURN(vap, va_encoding, 0xffff /* kTextEncodingUnknown */);
1973 struct vnop_setattr_args
/* {
1974 * struct vnodeop_desc *a_desc;
1976 * struct vnode_attr *a_vap;
1977 * vfs_context_t a_context;
1980 vfs_context_t ctx
= ap
->a_context
;
1981 vnode_t vp
= ap
->a_vp
;
1982 nfsnode_t np
= VTONFS(vp
);
1983 struct nfsmount
*nmp
;
1984 struct vnode_attr
*vap
= ap
->a_vap
;
1986 int biosize
, nfsvers
, namedattrs
;
1987 u_quad_t origsize
, vapsize
;
1988 struct nfs_dulookup dul
;
1989 nfsnode_t dnp
= NULL
;
1990 int dul_in_progress
= 0;
1992 const char *vname
= NULL
;
1994 struct nfs_open_owner
*noop
= NULL
;
1995 struct nfs_open_file
*nofp
= NULL
;
1998 if (nfs_mount_gone(nmp
)) {
2001 nfsvers
= nmp
->nm_vers
;
2002 namedattrs
= (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
);
2003 biosize
= nmp
->nm_biosize
;
2005 /* Disallow write attempts if the filesystem is mounted read-only. */
2006 if (vnode_vfsisrdonly(vp
)) {
2010 origsize
= np
->n_size
;
2011 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2012 switch (vnode_vtype(vp
)) {
2019 if (!VATTR_IS_ACTIVE(vap
, va_modify_time
) &&
2020 !VATTR_IS_ACTIVE(vap
, va_access_time
) &&
2021 !VATTR_IS_ACTIVE(vap
, va_mode
) &&
2022 !VATTR_IS_ACTIVE(vap
, va_uid
) &&
2023 !VATTR_IS_ACTIVE(vap
, va_gid
)) {
2026 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2030 * Disallow write attempts if the filesystem is
2031 * mounted read-only.
2033 if (vnode_vfsisrdonly(vp
)) {
2036 FSDBG_TOP(512, np
->n_size
, vap
->va_data_size
,
2037 np
->n_vattr
.nva_size
, np
->n_flag
);
2038 /* clear NNEEDINVALIDATE, if set */
2039 if ((error
= nfs_node_lock(np
))) {
2042 if (np
->n_flag
& NNEEDINVALIDATE
) {
2043 np
->n_flag
&= ~NNEEDINVALIDATE
;
2045 nfs_node_unlock(np
);
2046 /* flush everything */
2047 error
= nfs_vinvalbuf(vp
, (vap
->va_data_size
? V_SAVE
: 0), ctx
, 1);
2049 NP(np
, "nfs_setattr: nfs_vinvalbuf %d", error
);
2050 FSDBG_BOT(512, np
->n_size
, vap
->va_data_size
, np
->n_vattr
.nva_size
, -1);
2054 if (nfsvers
>= NFS_VER4
) {
2055 /* setting file size requires having the file open for write access */
2056 if (np
->n_flag
& NREVOKE
) {
2059 noop
= nfs_open_owner_find(nmp
, vfs_context_ucred(ctx
), 1);
2064 error
= nfs_mount_state_in_use_start(nmp
, vfs_context_thread(ctx
));
2068 if (np
->n_flag
& NREVOKE
) {
2069 nfs_mount_state_in_use_end(nmp
, 0);
2072 error
= nfs_open_file_find(np
, noop
, &nofp
, 0, 0, 1);
2073 if (!error
&& (nofp
->nof_flags
& NFS_OPEN_FILE_LOST
)) {
2076 if (!error
&& (nofp
->nof_flags
& NFS_OPEN_FILE_REOPEN
)) {
2077 nfs_mount_state_in_use_end(nmp
, 0);
2078 error
= nfs4_reopen(nofp
, vfs_context_thread(ctx
));
2085 error
= nfs_open_file_set_busy(nofp
, vfs_context_thread(ctx
));
2088 nfs_open_owner_rele(noop
);
2091 if (!(nofp
->nof_access
& NFS_OPEN_SHARE_ACCESS_WRITE
)) {
2092 /* we don't have the file open for write access, so open it */
2093 error
= nfs4_open(np
, nofp
, NFS_OPEN_SHARE_ACCESS_WRITE
, NFS_OPEN_SHARE_DENY_NONE
, ctx
);
2095 nofp
->nof_flags
|= NFS_OPEN_FILE_SETATTR
;
2097 if (nfs_mount_state_error_should_restart(error
)) {
2098 nfs_open_file_clear_busy(nofp
);
2100 if (nfs_mount_state_in_use_end(nmp
, error
)) {
2107 nfs_data_lock(np
, NFS_DATA_LOCK_EXCLUSIVE
);
2108 if (np
->n_size
> vap
->va_data_size
) { /* shrinking? */
2110 int neweofoff
, mustwrite
;
2113 obn
= (np
->n_size
- 1) / biosize
;
2114 bn
= vap
->va_data_size
/ biosize
;
2115 for (; obn
>= bn
; obn
--) {
2116 if (!nfs_buf_is_incore(np
, obn
)) {
2119 error
= nfs_buf_get(np
, obn
, biosize
, NULL
, NBLK_READ
, &bp
);
2124 FSDBG(512, bp
, bp
->nb_flags
, 0, obn
);
2125 SET(bp
->nb_flags
, NB_INVAL
);
2126 nfs_buf_release(bp
, 1);
2130 neweofoff
= vap
->va_data_size
- NBOFF(bp
);
2131 /* check for any dirty data before the new EOF */
2132 if ((bp
->nb_dirtyend
> 0) && (bp
->nb_dirtyoff
< neweofoff
)) {
2133 /* clip dirty range to EOF */
2134 if (bp
->nb_dirtyend
> neweofoff
) {
2135 bp
->nb_dirtyend
= neweofoff
;
2136 if (bp
->nb_dirtyoff
>= bp
->nb_dirtyend
) {
2137 bp
->nb_dirtyoff
= bp
->nb_dirtyend
= 0;
2140 if ((bp
->nb_dirtyend
> 0) && (bp
->nb_dirtyoff
< neweofoff
)) {
2144 bp
->nb_dirty
&= (1 << round_page_32(neweofoff
) / PAGE_SIZE
) - 1;
2149 FSDBG(512, bp
, bp
->nb_flags
, 0, obn
);
2150 SET(bp
->nb_flags
, NB_INVAL
);
2151 nfs_buf_release(bp
, 1);
2154 /* gotta write out dirty data before invalidating */
2155 /* (NB_STABLE indicates that data writes should be FILESYNC) */
2156 /* (NB_NOCACHE indicates buffer should be discarded) */
2157 CLR(bp
->nb_flags
, (NB_DONE
| NB_ERROR
| NB_INVAL
| NB_ASYNC
| NB_READ
));
2158 SET(bp
->nb_flags
, NB_STABLE
| NB_NOCACHE
);
2159 if (!IS_VALID_CRED(bp
->nb_wcred
)) {
2160 kauth_cred_t cred
= vfs_context_ucred(ctx
);
2161 kauth_cred_ref(cred
);
2162 bp
->nb_wcred
= cred
;
2164 error
= nfs_buf_write(bp
);
2165 // Note: bp has been released
2167 FSDBG(512, bp
, 0xd00dee, 0xbad, error
);
2168 nfs_node_lock_force(np
);
2169 np
->n_error
= error
;
2170 np
->n_flag
|= NWRITEERR
;
2172 * There was a write error and we need to
2173 * invalidate attrs and flush buffers in
2174 * order to sync up with the server.
2175 * (if this write was extending the file,
2176 * we may no longer know the correct size)
2178 NATTRINVALIDATE(np
);
2179 nfs_node_unlock(np
);
2180 nfs_data_unlock(np
);
2181 nfs_vinvalbuf(vp
, V_SAVE
| V_IGNORE_WRITEERR
, ctx
, 1);
2182 nfs_data_lock(np
, NFS_DATA_LOCK_EXCLUSIVE
);
2187 if (vap
->va_data_size
!= np
->n_size
) {
2188 ubc_setsize(vp
, (off_t
)vap
->va_data_size
); /* XXX error? */
2190 origsize
= np
->n_size
;
2191 np
->n_size
= np
->n_vattr
.nva_size
= vap
->va_data_size
;
2192 nfs_node_lock_force(np
);
2193 CLR(np
->n_flag
, NUPDATESIZE
);
2194 nfs_node_unlock(np
);
2195 FSDBG(512, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0001);
2197 } else if (VATTR_IS_ACTIVE(vap
, va_modify_time
) ||
2198 VATTR_IS_ACTIVE(vap
, va_access_time
) ||
2199 (vap
->va_vaflags
& VA_UTIMES_NULL
)) {
2200 if ((error
= nfs_node_lock(np
))) {
2203 if ((np
->n_flag
& NMODIFIED
) && (vnode_vtype(vp
) == VREG
)) {
2204 nfs_node_unlock(np
);
2205 error
= nfs_vinvalbuf(vp
, V_SAVE
, ctx
, 1);
2206 if (error
== EINTR
) {
2210 nfs_node_unlock(np
);
2213 if ((VATTR_IS_ACTIVE(vap
, va_mode
) || VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
) ||
2214 VATTR_IS_ACTIVE(vap
, va_acl
) || VATTR_IS_ACTIVE(vap
, va_uuuid
) || VATTR_IS_ACTIVE(vap
, va_guuid
)) &&
2215 !(error
= nfs_node_lock(np
))) {
2216 NACCESSINVALIDATE(np
);
2217 nfs_node_unlock(np
);
2219 dvp
= vnode_getparent(vp
);
2220 vname
= vnode_getname(vp
);
2221 dnp
= (dvp
&& vname
) ? VTONFS(dvp
) : NULL
;
2223 if (nfs_node_set_busy(dnp
, vfs_context_thread(ctx
))) {
2225 vnode_putname(vname
);
2227 nfs_dulookup_init(&dul
, dnp
, vname
, strlen(vname
), ctx
);
2228 nfs_dulookup_start(&dul
, dnp
, ctx
);
2229 dul_in_progress
= 1;
2236 vnode_putname(vname
);
2243 error
= nmp
->nm_funcs
->nf_setattr_rpc(np
, vap
, ctx
);
2246 if (dul_in_progress
) {
2247 nfs_dulookup_finish(&dul
, dnp
, ctx
);
2248 nfs_node_clear_busy(dnp
);
2250 vnode_putname(vname
);
2253 FSDBG_BOT(512, np
->n_size
, vap
->va_data_size
, np
->n_vattr
.nva_size
, error
);
2254 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2255 if (error
&& (origsize
!= np
->n_size
) &&
2256 ((nfsvers
< NFS_VER4
) || !nfs_mount_state_error_should_restart(error
))) {
2257 /* make every effort to resync file size w/ server... */
2258 /* (don't bother if we'll be restarting the operation) */
2259 int err
; /* preserve "error" for return */
2260 np
->n_size
= np
->n_vattr
.nva_size
= origsize
;
2261 nfs_node_lock_force(np
);
2262 CLR(np
->n_flag
, NUPDATESIZE
);
2263 nfs_node_unlock(np
);
2264 FSDBG(512, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0002);
2265 ubc_setsize(vp
, (off_t
)np
->n_size
); /* XXX check error */
2266 vapsize
= vap
->va_data_size
;
2267 vap
->va_data_size
= origsize
;
2268 err
= nmp
->nm_funcs
->nf_setattr_rpc(np
, vap
, ctx
);
2270 NP(np
, "nfs_vnop_setattr: nfs%d_setattr_rpc %d %d", nfsvers
, error
, err
);
2272 vap
->va_data_size
= vapsize
;
2274 nfs_node_lock_force(np
);
2276 * The size was just set. If the size is already marked for update, don't
2277 * trust the newsize (it may have been set while the setattr was in progress).
2278 * Clear the update flag and make sure we fetch new attributes so we are sure
2279 * we have the latest size.
2281 if (ISSET(np
->n_flag
, NUPDATESIZE
)) {
2282 CLR(np
->n_flag
, NUPDATESIZE
);
2283 NATTRINVALIDATE(np
);
2284 nfs_node_unlock(np
);
2285 nfs_getattr(np
, NULL
, ctx
, NGA_UNCACHED
);
2287 nfs_node_unlock(np
);
2289 nfs_data_unlock(np
);
2291 if (nfsvers
>= NFS_VER4
) {
2293 /* don't close our setattr open if we'll be restarting... */
2294 if (!nfs_mount_state_error_should_restart(error
) &&
2295 (nofp
->nof_flags
& NFS_OPEN_FILE_SETATTR
)) {
2296 int err
= nfs_close(np
, nofp
, NFS_OPEN_SHARE_ACCESS_WRITE
, NFS_OPEN_SHARE_DENY_NONE
, ctx
);
2298 NP(np
, "nfs_vnop_setattr: close error: %d", err
);
2300 nofp
->nof_flags
&= ~NFS_OPEN_FILE_SETATTR
;
2302 nfs_open_file_clear_busy(nofp
);
2305 if (nfs_mount_state_in_use_end(nmp
, error
)) {
2308 nfs_open_owner_rele(noop
);
2316 * Do an NFS setattr RPC.
2321 struct vnode_attr
*vap
,
2324 struct nfsmount
*nmp
= NFSTONMP(np
);
2325 int error
= 0, lockerror
= ENOENT
, status
, wccpostattr
= 0, nfsvers
;
2326 u_int64_t xid
, nextxid
;
2327 struct nfsm_chain nmreq
, nmrep
;
2329 if (nfs_mount_gone(nmp
)) {
2332 nfsvers
= nmp
->nm_vers
;
2334 VATTR_SET_SUPPORTED(vap
, va_mode
);
2335 VATTR_SET_SUPPORTED(vap
, va_uid
);
2336 VATTR_SET_SUPPORTED(vap
, va_gid
);
2337 VATTR_SET_SUPPORTED(vap
, va_data_size
);
2338 VATTR_SET_SUPPORTED(vap
, va_access_time
);
2339 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
2342 if (VATTR_IS_ACTIVE(vap
, va_flags
)
2344 if (vap
->va_flags
) { /* we don't support setting flags */
2345 if (vap
->va_active
& ~VNODE_ATTR_va_flags
) {
2346 return EINVAL
; /* return EINVAL if other attributes also set */
2348 return ENOTSUP
; /* return ENOTSUP for chflags(2) */
2351 /* no flags set, so we'll just ignore it */
2352 if (!(vap
->va_active
& ~VNODE_ATTR_va_flags
)) {
2353 return 0; /* no (other) attributes to set, so nothing to do */
2357 nfsm_chain_null(&nmreq
);
2358 nfsm_chain_null(&nmrep
);
2360 nfsm_chain_build_alloc_init(error
, &nmreq
,
2361 NFSX_FH(nfsvers
) + NFSX_SATTR(nfsvers
));
2362 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
2363 if (nfsvers
== NFS_VER3
) {
2364 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
2365 nfsm_chain_add_32(error
, &nmreq
, TRUE
);
2366 nfsm_chain_add_32(error
, &nmreq
, vap
->va_mode
);
2368 nfsm_chain_add_32(error
, &nmreq
, FALSE
);
2370 if (VATTR_IS_ACTIVE(vap
, va_uid
)) {
2371 nfsm_chain_add_32(error
, &nmreq
, TRUE
);
2372 nfsm_chain_add_32(error
, &nmreq
, vap
->va_uid
);
2374 nfsm_chain_add_32(error
, &nmreq
, FALSE
);
2376 if (VATTR_IS_ACTIVE(vap
, va_gid
)) {
2377 nfsm_chain_add_32(error
, &nmreq
, TRUE
);
2378 nfsm_chain_add_32(error
, &nmreq
, vap
->va_gid
);
2380 nfsm_chain_add_32(error
, &nmreq
, FALSE
);
2382 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2383 nfsm_chain_add_32(error
, &nmreq
, TRUE
);
2384 nfsm_chain_add_64(error
, &nmreq
, vap
->va_data_size
);
2386 nfsm_chain_add_32(error
, &nmreq
, FALSE
);
2388 if (vap
->va_vaflags
& VA_UTIMES_NULL
) {
2389 nfsm_chain_add_32(error
, &nmreq
, NFS_TIME_SET_TO_SERVER
);
2390 nfsm_chain_add_32(error
, &nmreq
, NFS_TIME_SET_TO_SERVER
);
2392 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
2393 nfsm_chain_add_32(error
, &nmreq
, NFS_TIME_SET_TO_CLIENT
);
2394 nfsm_chain_add_32(error
, &nmreq
, vap
->va_access_time
.tv_sec
);
2395 nfsm_chain_add_32(error
, &nmreq
, vap
->va_access_time
.tv_nsec
);
2397 nfsm_chain_add_32(error
, &nmreq
, NFS_TIME_DONT_CHANGE
);
2399 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
2400 nfsm_chain_add_32(error
, &nmreq
, NFS_TIME_SET_TO_CLIENT
);
2401 nfsm_chain_add_32(error
, &nmreq
, vap
->va_modify_time
.tv_sec
);
2402 nfsm_chain_add_32(error
, &nmreq
, vap
->va_modify_time
.tv_nsec
);
2404 nfsm_chain_add_32(error
, &nmreq
, NFS_TIME_DONT_CHANGE
);
2407 nfsm_chain_add_32(error
, &nmreq
, FALSE
);
2409 nfsm_chain_add_32(error
, &nmreq
, VATTR_IS_ACTIVE(vap
, va_mode
) ?
2410 vtonfsv2_mode(vnode_vtype(NFSTOV(np
)), vap
->va_mode
) : -1);
2411 nfsm_chain_add_32(error
, &nmreq
, VATTR_IS_ACTIVE(vap
, va_uid
) ?
2412 vap
->va_uid
: (uint32_t)-1);
2413 nfsm_chain_add_32(error
, &nmreq
, VATTR_IS_ACTIVE(vap
, va_gid
) ?
2414 vap
->va_gid
: (uint32_t)-1);
2415 nfsm_chain_add_32(error
, &nmreq
, VATTR_IS_ACTIVE(vap
, va_data_size
) ?
2416 vap
->va_data_size
: (uint32_t)-1);
2417 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
2418 nfsm_chain_add_32(error
, &nmreq
, vap
->va_access_time
.tv_sec
);
2419 nfsm_chain_add_32(error
, &nmreq
, (vap
->va_access_time
.tv_nsec
!= -1) ?
2420 ((uint32_t)vap
->va_access_time
.tv_nsec
/ 1000) : 0xffffffff);
2422 nfsm_chain_add_32(error
, &nmreq
, -1);
2423 nfsm_chain_add_32(error
, &nmreq
, -1);
2425 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
2426 nfsm_chain_add_32(error
, &nmreq
, vap
->va_modify_time
.tv_sec
);
2427 nfsm_chain_add_32(error
, &nmreq
, (vap
->va_modify_time
.tv_nsec
!= -1) ?
2428 ((uint32_t)vap
->va_modify_time
.tv_nsec
/ 1000) : 0xffffffff);
2430 nfsm_chain_add_32(error
, &nmreq
, -1);
2431 nfsm_chain_add_32(error
, &nmreq
, -1);
2434 nfsm_chain_build_done(error
, &nmreq
);
2436 error
= nfs_request(np
, NULL
, &nmreq
, NFSPROC_SETATTR
, ctx
, NULL
, &nmrep
, &xid
, &status
);
2437 if ((lockerror
= nfs_node_lock(np
))) {
2440 if (nfsvers
== NFS_VER3
) {
2441 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
2442 nfsm_chain_get_wcc_data(error
, &nmrep
, np
, &premtime
, &wccpostattr
, &xid
);
2444 /* if file hadn't changed, update cached mtime */
2445 if (nfstimespeccmp(&np
->n_mtime
, &premtime
, ==)) {
2446 NFS_CHANGED_UPDATE(nfsvers
, np
, &np
->n_vattr
);
2448 /* if directory hadn't changed, update namecache mtime */
2449 if ((vnode_vtype(NFSTOV(np
)) == VDIR
) &&
2450 nfstimespeccmp(&np
->n_ncmtime
, &premtime
, ==)) {
2451 NFS_CHANGED_UPDATE_NC(nfsvers
, np
, &np
->n_vattr
);
2454 NATTRINVALIDATE(np
);
2461 nfsm_chain_loadattr(error
, &nmrep
, np
, nfsvers
, &xid
);
2464 * We just changed the attributes and we want to make sure that we
2465 * see the latest attributes. Get the next XID. If it's not the
2466 * next XID after the SETATTR XID, then it's possible that another
2467 * RPC was in flight at the same time and it might put stale attributes
2468 * in the cache. In that case, we invalidate the attributes and set
2469 * the attribute cache XID to guarantee that newer attributes will
2473 nfs_get_xid(&nextxid
);
2474 if (nextxid
!= (xid
+ 1)) {
2475 np
->n_xid
= nextxid
;
2476 NATTRINVALIDATE(np
);
2480 nfs_node_unlock(np
);
2482 nfsm_chain_cleanup(&nmreq
);
2483 nfsm_chain_cleanup(&nmrep
);
2488 * NFS lookup call, one step at a time...
2489 * First look in cache
2490 * If not found, unlock the directory nfsnode and do the RPC
2494 struct vnop_lookup_args
/* {
2495 * struct vnodeop_desc *a_desc;
2498 * struct componentname *a_cnp;
2499 * vfs_context_t a_context;
2502 vfs_context_t ctx
= ap
->a_context
;
2503 struct componentname
*cnp
= ap
->a_cnp
;
2504 vnode_t dvp
= ap
->a_dvp
;
2505 vnode_t
*vpp
= ap
->a_vpp
;
2506 int flags
= cnp
->cn_flags
;
2509 struct nfsmount
*nmp
;
2511 int nfsvers
, error
, busyerror
= ENOENT
, isdot
, isdotdot
, negnamecache
;
2513 struct nfs_vattr nvattr
;
2515 struct vnop_access_args naa
;
2517 struct nfsreq rq
, *req
= &rq
;
2522 NVATTR_INIT(&nvattr
);
2524 mp
= vnode_mount(dvp
);
2526 if (nfs_mount_gone(nmp
)) {
2530 nfsvers
= nmp
->nm_vers
;
2531 negnamecache
= !NMFLAG(nmp
, NONEGNAMECACHE
);
2533 if ((error
= busyerror
= nfs_node_set_busy(dnp
, vfs_context_thread(ctx
)))) {
2536 /* nfs_getattr() will check changed and purge caches */
2537 if ((error
= nfs_getattr(dnp
, NULL
, ctx
, NGA_CACHED
))) {
2541 error
= cache_lookup(dvp
, vpp
, cnp
);
2544 /* negative cache entry */
2548 if ((nfsvers
> NFS_VER2
) && NMFLAG(nmp
, RDIRPLUS
)) {
2549 /* if rdirplus, try dir buf cache lookup */
2550 error
= nfs_dir_buf_cache_lookup(dnp
, &np
, cnp
, ctx
, 0);
2552 /* dir buf cache hit */
2557 if (error
!= -1) { /* cache miss */
2562 /* cache hit, not really an error */
2563 OSAddAtomic64(1, &nfsstats
.lookupcache_hits
);
2565 nfs_node_clear_busy(dnp
);
2568 /* check for directory access */
2569 naa
.a_desc
= &vnop_access_desc
;
2571 naa
.a_action
= KAUTH_VNODE_SEARCH
;
2572 naa
.a_context
= ctx
;
2574 /* compute actual success/failure based on accessibility */
2575 error
= nfs_vnop_access(&naa
);
2578 /* unexpected error from cache_lookup */
2582 /* skip lookup, if we know who we are: "." or ".." */
2583 isdot
= isdotdot
= 0;
2584 if (cnp
->cn_nameptr
[0] == '.') {
2585 if (cnp
->cn_namelen
== 1) {
2588 if ((cnp
->cn_namelen
== 2) && (cnp
->cn_nameptr
[1] == '.')) {
2592 if (isdotdot
|| isdot
) {
2597 if ((nfsvers
>= NFS_VER4
) && (dnp
->n_vattr
.nva_flags
& NFS_FFLAG_TRIGGER
)) {
2598 /* we should never be looking things up in a trigger directory, return nothing */
2604 /* do we know this name is too long? */
2606 if (nfs_mount_gone(nmp
)) {
2610 if (NFS_BITMAP_ISSET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_MAXNAME
) &&
2611 (cnp
->cn_namelen
> (int)nmp
->nm_fsattr
.nfsa_maxname
)) {
2612 error
= ENAMETOOLONG
;
2619 OSAddAtomic64(1, &nfsstats
.lookupcache_misses
);
2621 error
= nmp
->nm_funcs
->nf_lookup_rpc_async(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
, &req
);
2623 error
= nmp
->nm_funcs
->nf_lookup_rpc_async_finish(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
, req
, &xid
, &fh
, &nvattr
);
2626 /* is the file handle the same as this directory's file handle? */
2627 isdot
= NFS_CMPFH(dnp
, fh
.fh_data
, fh
.fh_len
);
2630 if (flags
& ISLASTCN
) {
2631 switch (cnp
->cn_nameiop
) {
2633 cnp
->cn_flags
&= ~MAKEENTRY
;
2636 cnp
->cn_flags
&= ~MAKEENTRY
;
2646 newvp
= vnode_getparent(dvp
);
2652 error
= vnode_get(dvp
);
2657 nfs_node_lock_force(dnp
);
2658 if (fh
.fh_len
&& (dnp
->n_xid
<= xid
)) {
2659 nfs_loadattrcache(dnp
, &nvattr
, &xid
, 0);
2661 nfs_node_unlock(dnp
);
2663 ngflags
= (cnp
->cn_flags
& MAKEENTRY
) ? NG_MAKEENTRY
: 0;
2664 error
= nfs_nget(mp
, dnp
, cnp
, fh
.fh_data
, fh
.fh_len
, &nvattr
, &xid
, rq
.r_auth
, ngflags
, &np
);
2669 nfs_node_unlock(np
);
2675 if (((cnp
->cn_nameiop
== CREATE
) || (cnp
->cn_nameiop
== RENAME
)) &&
2676 (flags
& ISLASTCN
) && (error
== ENOENT
)) {
2677 if (vnode_mount(dvp
) && vnode_vfsisrdonly(dvp
)) {
2680 error
= EJUSTRETURN
;
2684 if ((error
== ENOENT
) && (cnp
->cn_flags
& MAKEENTRY
) &&
2685 (cnp
->cn_nameiop
!= CREATE
) && negnamecache
) {
2686 /* add a negative entry in the name cache */
2687 nfs_node_lock_force(dnp
);
2688 cache_enter(dvp
, NULL
, cnp
);
2689 dnp
->n_flag
|= NNEGNCENTRIES
;
2690 nfs_node_unlock(dnp
);
2693 NVATTR_CLEANUP(&nvattr
);
2695 nfs_node_clear_busy(dnp
);
2697 if (error
&& *vpp
) {
2704 int nfs_readlink_nocache
= DEFAULT_READLINK_NOCACHE
;
2711 struct vnop_readlink_args
/* {
2712 * struct vnodeop_desc *a_desc;
2714 * struct uio *a_uio;
2715 * vfs_context_t a_context;
2718 vfs_context_t ctx
= ap
->a_context
;
2719 nfsnode_t np
= VTONFS(ap
->a_vp
);
2720 struct nfsmount
*nmp
;
2721 int error
= 0, nfsvers
;
2723 uio_t uio
= ap
->a_uio
;
2724 struct nfsbuf
*bp
= NULL
;
2728 if (vnode_vtype(ap
->a_vp
) != VLNK
) {
2732 if (uio_resid(uio
) == 0) {
2735 if (uio_offset(uio
) < 0) {
2739 nmp
= VTONMP(ap
->a_vp
);
2740 if (nfs_mount_gone(nmp
)) {
2743 nfsvers
= nmp
->nm_vers
;
2746 /* nfs_getattr() will check changed and purge caches */
2747 if ((error
= nfs_getattr(np
, NULL
, ctx
, nfs_readlink_nocache
? NGA_UNCACHED
: NGA_CACHED
))) {
2748 FSDBG(531, np
, 0xd1e0001, 0, error
);
2752 if (nfs_readlink_nocache
) {
2753 timeo
= nfs_attrcachetimeout(np
);
2758 OSAddAtomic64(1, &nfsstats
.biocache_readlinks
);
2759 error
= nfs_buf_get(np
, 0, NFS_MAXPATHLEN
, vfs_context_thread(ctx
), NBLK_META
, &bp
);
2761 FSDBG(531, np
, 0xd1e0002, 0, error
);
2765 if (nfs_readlink_nocache
) {
2766 NFS_VNOP_DBG("timeo = %d ts.tv_sec = %ld need refresh = %d cached = %d\n", timeo
, ts
.tv_sec
,
2767 (np
->n_rltim
.tv_sec
+ timeo
) < ts
.tv_sec
|| nfs_readlink_nocache
> 1,
2768 ISSET(bp
->nb_flags
, NB_CACHE
) == NB_CACHE
);
2769 /* n_rltim is synchronized by the associated nfs buf */
2770 if (ISSET(bp
->nb_flags
, NB_CACHE
) && ((nfs_readlink_nocache
> 1) || ((np
->n_rltim
.tv_sec
+ timeo
) < ts
.tv_sec
))) {
2771 SET(bp
->nb_flags
, NB_INVAL
);
2772 nfs_buf_release(bp
, 0);
2776 if (!ISSET(bp
->nb_flags
, NB_CACHE
)) {
2778 OSAddAtomic64(1, &nfsstats
.readlink_bios
);
2779 buflen
= bp
->nb_bufsize
;
2780 error
= nmp
->nm_funcs
->nf_readlink_rpc(np
, bp
->nb_data
, &buflen
, ctx
);
2782 if (error
== ESTALE
) {
2783 NFS_VNOP_DBG("Stale FH from readlink rpc\n");
2784 error
= nfs_refresh_fh(np
, ctx
);
2789 SET(bp
->nb_flags
, NB_ERROR
);
2790 bp
->nb_error
= error
;
2791 NFS_VNOP_DBG("readlink failed %d\n", error
);
2793 bp
->nb_validoff
= 0;
2794 bp
->nb_validend
= buflen
;
2796 NFS_VNOP_DBG("readlink of %.*s\n", bp
->nb_validend
, (char *)bp
->nb_data
);
2799 NFS_VNOP_DBG("got cached link of %.*s\n", bp
->nb_validend
, (char *)bp
->nb_data
);
2802 if (!error
&& (bp
->nb_validend
> 0)) {
2803 error
= uiomove(bp
->nb_data
, bp
->nb_validend
, uio
);
2805 FSDBG(531, np
, bp
->nb_validend
, 0, error
);
2806 nfs_buf_release(bp
, 1);
2811 * Do a readlink RPC.
2814 nfs3_readlink_rpc(nfsnode_t np
, char *buf
, uint32_t *buflenp
, vfs_context_t ctx
)
2816 struct nfsmount
*nmp
;
2817 int error
= 0, lockerror
= ENOENT
, nfsvers
, status
;
2820 struct nfsm_chain nmreq
, nmrep
;
2823 if (nfs_mount_gone(nmp
)) {
2826 nfsvers
= nmp
->nm_vers
;
2827 nfsm_chain_null(&nmreq
);
2828 nfsm_chain_null(&nmrep
);
2830 nfsm_chain_build_alloc_init(error
, &nmreq
, NFSX_FH(nfsvers
));
2831 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
2832 nfsm_chain_build_done(error
, &nmreq
);
2834 error
= nfs_request(np
, NULL
, &nmreq
, NFSPROC_READLINK
, ctx
, NULL
, &nmrep
, &xid
, &status
);
2835 if ((lockerror
= nfs_node_lock(np
))) {
2838 if (nfsvers
== NFS_VER3
) {
2839 nfsm_chain_postop_attr_update(error
, &nmrep
, np
, &xid
);
2844 nfsm_chain_get_32(error
, &nmrep
, len
);
2846 if ((nfsvers
== NFS_VER2
) && (len
> *buflenp
)) {
2850 if (len
>= *buflenp
) {
2851 if (np
->n_size
&& (np
->n_size
< *buflenp
)) {
2857 nfsm_chain_get_opaque(error
, &nmrep
, len
, buf
);
2863 nfs_node_unlock(np
);
2865 nfsm_chain_cleanup(&nmreq
);
2866 nfsm_chain_cleanup(&nmrep
);
2875 nfs_read_rpc(nfsnode_t np
, uio_t uio
, vfs_context_t ctx
)
2877 struct nfsmount
*nmp
;
2878 int error
= 0, nfsvers
, eof
= 0;
2879 size_t nmrsize
, len
, retlen
;
2882 struct nfsreq rq
, *req
= &rq
;
2884 uint32_t stategenid
= 0, restart
= 0;
2886 FSDBG_TOP(536, np
, uio_offset(uio
), uio_resid(uio
), 0);
2888 if (nfs_mount_gone(nmp
)) {
2891 nfsvers
= nmp
->nm_vers
;
2892 nmrsize
= nmp
->nm_rsize
;
2894 txoffset
= uio_offset(uio
);
2895 tsiz
= uio_resid(uio
);
2896 if ((nfsvers
== NFS_VER2
) && ((uint64_t)(txoffset
+ tsiz
) > 0xffffffffULL
)) {
2897 FSDBG_BOT(536, np
, uio_offset(uio
), uio_resid(uio
), EFBIG
);
2902 len
= retlen
= (tsiz
> (user_ssize_t
)nmrsize
) ? nmrsize
: (size_t)tsiz
;
2903 FSDBG(536, np
, txoffset
, len
, 0);
2904 if (np
->n_flag
& NREVOKE
) {
2909 if (nmp
->nm_vers
>= NFS_VER4
) {
2910 stategenid
= nmp
->nm_stategenid
;
2913 error
= nmp
->nm_funcs
->nf_read_rpc_async(np
, txoffset
, len
,
2914 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, &req
);
2916 error
= nmp
->nm_funcs
->nf_read_rpc_async_finish(np
, req
, uio
, &retlen
, &eof
);
2919 if ((nmp
->nm_vers
>= NFS_VER4
) && nfs_mount_state_error_should_restart(error
) &&
2920 (++restart
<= nfs_mount_state_max_restarts(nmp
))) { /* guard against no progress */
2921 lck_mtx_lock(&nmp
->nm_lock
);
2922 if ((error
!= NFSERR_GRACE
) && (stategenid
== nmp
->nm_stategenid
)) {
2923 NP(np
, "nfs_read_rpc: error %d, initiating recovery", error
);
2924 nfs_need_recover(nmp
, error
);
2926 lck_mtx_unlock(&nmp
->nm_lock
);
2927 if (np
->n_flag
& NREVOKE
) {
2930 if (error
== NFSERR_GRACE
) {
2931 tsleep(&nmp
->nm_state
, (PZERO
- 1), "nfsgrace", 2 * hz
);
2933 if (!(error
= nfs_mount_state_wait_for_recovery(nmp
))) {
2944 if (nfsvers
!= NFS_VER2
) {
2945 if (eof
|| (retlen
== 0)) {
2948 } else if (retlen
< len
) {
2953 FSDBG_BOT(536, np
, eof
, uio_resid(uio
), error
);
2958 nfs3_read_rpc_async(
2964 struct nfsreq_cbinfo
*cb
,
2965 struct nfsreq
**reqp
)
2967 struct nfsmount
*nmp
;
2968 int error
= 0, nfsvers
;
2969 struct nfsm_chain nmreq
;
2972 if (nfs_mount_gone(nmp
)) {
2975 nfsvers
= nmp
->nm_vers
;
2977 nfsm_chain_null(&nmreq
);
2978 nfsm_chain_build_alloc_init(error
, &nmreq
, NFSX_FH(nfsvers
) + 3 * NFSX_UNSIGNED
);
2979 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
2980 if (nfsvers
== NFS_VER3
) {
2981 nfsm_chain_add_64(error
, &nmreq
, offset
);
2982 nfsm_chain_add_32(error
, &nmreq
, len
);
2984 nfsm_chain_add_32(error
, &nmreq
, offset
);
2985 nfsm_chain_add_32(error
, &nmreq
, len
);
2986 nfsm_chain_add_32(error
, &nmreq
, 0);
2988 nfsm_chain_build_done(error
, &nmreq
);
2990 error
= nfs_request_async(np
, NULL
, &nmreq
, NFSPROC_READ
, thd
, cred
, NULL
, 0, cb
, reqp
);
2992 nfsm_chain_cleanup(&nmreq
);
2997 nfs3_read_rpc_async_finish(
3004 int error
= 0, lockerror
, nfsvers
, status
, eof
= 0;
3007 struct nfsmount
*nmp
;
3008 struct nfsm_chain nmrep
;
3011 if (nfs_mount_gone(nmp
)) {
3012 nfs_request_async_cancel(req
);
3015 nfsvers
= nmp
->nm_vers
;
3017 nfsm_chain_null(&nmrep
);
3019 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
3020 if (error
== EINPROGRESS
) { /* async request restarted */
3024 if ((lockerror
= nfs_node_lock(np
))) {
3027 if (nfsvers
== NFS_VER3
) {
3028 nfsm_chain_postop_attr_update(error
, &nmrep
, np
, &xid
);
3033 if (nfsvers
== NFS_VER3
) {
3034 nfsm_chain_adv(error
, &nmrep
, NFSX_UNSIGNED
);
3035 nfsm_chain_get_32(error
, &nmrep
, eof
);
3037 nfsm_chain_loadattr(error
, &nmrep
, np
, nfsvers
, &xid
);
3040 nfs_node_unlock(np
);
3042 nfsm_chain_get_32(error
, &nmrep
, retlen
);
3043 if ((nfsvers
== NFS_VER2
) && (retlen
> *lenp
)) {
3047 error
= nfsm_chain_get_uio(&nmrep
, MIN(retlen
, *lenp
), uio
);
3049 if (nfsvers
== NFS_VER3
) {
3050 if (!eof
&& !retlen
) {
3053 } else if (retlen
< *lenp
) {
3058 *lenp
= MIN(retlen
, *lenp
);
3060 nfsm_chain_cleanup(&nmrep
);
3069 struct vnop_write_args
/* {
3070 * struct vnodeop_desc *a_desc;
3072 * struct uio *a_uio;
3074 * vfs_context_t a_context;
3077 vfs_context_t ctx
= ap
->a_context
;
3078 uio_t uio
= ap
->a_uio
;
3079 vnode_t vp
= ap
->a_vp
;
3080 nfsnode_t np
= VTONFS(vp
);
3081 int ioflag
= ap
->a_ioflag
;
3083 struct nfsmount
*nmp
= VTONMP(vp
);
3086 int n
, on
, error
= 0;
3087 off_t boff
, start
, end
;
3089 char auio_buf
[UIO_SIZEOF(1)];
3093 FSDBG_TOP(515, np
, uio_offset(uio
), uio_resid(uio
), ioflag
);
3095 if (vnode_vtype(vp
) != VREG
) {
3096 FSDBG_BOT(515, np
, uio_offset(uio
), uio_resid(uio
), EIO
);
3100 thd
= vfs_context_thread(ctx
);
3101 cred
= vfs_context_ucred(ctx
);
3103 nfs_data_lock(np
, NFS_DATA_LOCK_SHARED
);
3105 if ((error
= nfs_node_lock(np
))) {
3106 nfs_data_unlock(np
);
3107 FSDBG_BOT(515, np
, uio_offset(uio
), uio_resid(uio
), error
);
3112 if (np
->n_flag
& NWRITEERR
) {
3113 error
= np
->n_error
;
3114 np
->n_flag
&= ~NWRITEERR
;
3116 if (np
->n_flag
& NNEEDINVALIDATE
) {
3117 np
->n_flag
&= ~NNEEDINVALIDATE
;
3118 nfs_node_unlock(np
);
3119 nfs_data_unlock(np
);
3120 nfs_vinvalbuf(vp
, V_SAVE
| V_IGNORE_WRITEERR
, ctx
, 1);
3121 nfs_data_lock(np
, NFS_DATA_LOCK_SHARED
);
3123 nfs_node_unlock(np
);
3129 biosize
= nmp
->nm_biosize
;
3131 if (ioflag
& (IO_APPEND
| IO_SYNC
)) {
3132 nfs_node_lock_force(np
);
3133 if (np
->n_flag
& NMODIFIED
) {
3134 NATTRINVALIDATE(np
);
3135 nfs_node_unlock(np
);
3136 nfs_data_unlock(np
);
3137 error
= nfs_vinvalbuf(vp
, V_SAVE
, ctx
, 1);
3138 nfs_data_lock(np
, NFS_DATA_LOCK_SHARED
);
3140 FSDBG(515, np
, uio_offset(uio
), 0x10bad01, error
);
3144 nfs_node_unlock(np
);
3146 if (ioflag
& IO_APPEND
) {
3147 nfs_data_unlock(np
);
3148 /* nfs_getattr() will check changed and purge caches */
3149 error
= nfs_getattr(np
, NULL
, ctx
, NGA_UNCACHED
);
3150 /* we'll be extending the file, so take the data lock exclusive */
3151 nfs_data_lock(np
, NFS_DATA_LOCK_EXCLUSIVE
);
3153 FSDBG(515, np
, uio_offset(uio
), 0x10bad02, error
);
3156 uio_setoffset(uio
, np
->n_size
);
3159 if (uio_offset(uio
) < 0) {
3161 FSDBG_BOT(515, np
, uio_offset(uio
), 0xbad0ff, error
);
3164 if (uio_resid(uio
) == 0) {
3168 if (((uio_offset(uio
) + uio_resid(uio
)) > (off_t
)np
->n_size
) && !(ioflag
& IO_APPEND
)) {
3170 * It looks like we'll be extending the file, so take the data lock exclusive.
3172 nfs_data_unlock(np
);
3173 nfs_data_lock(np
, NFS_DATA_LOCK_EXCLUSIVE
);
3176 * Also, if the write begins after the previous EOF buffer, make sure to zero
3177 * and validate the new bytes in that buffer.
3179 struct nfsbuf
*eofbp
= NULL
;
3180 daddr64_t eofbn
= np
->n_size
/ biosize
;
3181 int eofoff
= np
->n_size
% biosize
;
3182 lbn
= uio_offset(uio
) / biosize
;
3184 if (eofoff
&& (eofbn
< lbn
)) {
3185 if ((error
= nfs_buf_get(np
, eofbn
, biosize
, thd
, NBLK_WRITE
| NBLK_ONLYVALID
, &eofbp
))) {
3188 np
->n_size
+= (biosize
- eofoff
);
3189 nfs_node_lock_force(np
);
3190 CLR(np
->n_flag
, NUPDATESIZE
);
3191 np
->n_flag
|= NMODIFIED
;
3192 nfs_node_unlock(np
);
3193 FSDBG(516, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0001);
3194 ubc_setsize(vp
, (off_t
)np
->n_size
); /* XXX errors */
3197 * For the old last page, don't zero bytes if there
3198 * are invalid bytes in that page (i.e. the page isn't
3200 * For pages after the old last page, zero them and
3201 * mark them as valid.
3205 if (ioflag
& IO_NOCACHE
) {
3206 SET(eofbp
->nb_flags
, NB_NOCACHE
);
3209 FSDBG(516, eofbp
, eofoff
, biosize
- eofoff
, 0xe0fff01e);
3211 i
= eofoff
/ PAGE_SIZE
;
3212 while (eofoff
< biosize
) {
3213 int poff
= eofoff
& PAGE_MASK
;
3214 if (!poff
|| NBPGVALID(eofbp
, i
)) {
3215 bzero(d
+ eofoff
, PAGE_SIZE
- poff
);
3216 NBPGVALID_SET(eofbp
, i
);
3218 eofoff
+= PAGE_SIZE
- poff
;
3221 nfs_buf_release(eofbp
, 1);
3227 OSAddAtomic64(1, &nfsstats
.biocache_writes
);
3228 lbn
= uio_offset(uio
) / biosize
;
3229 on
= uio_offset(uio
) % biosize
;
3231 if (uio_resid(uio
) < n
) {
3236 * Get a cache block for writing. The range to be written is
3237 * (off..off+n) within the block. We ensure that the block
3238 * either has no dirty region or that the given range is
3239 * contiguous with the existing dirty region.
3241 error
= nfs_buf_get(np
, lbn
, biosize
, thd
, NBLK_WRITE
, &bp
);
3245 /* map the block because we know we're going to write to it */
3248 if (ioflag
& IO_NOCACHE
) {
3249 SET(bp
->nb_flags
, NB_NOCACHE
);
3252 if (!IS_VALID_CRED(bp
->nb_wcred
)) {
3253 kauth_cred_ref(cred
);
3254 bp
->nb_wcred
= cred
;
3258 * If there's already a dirty range AND dirty pages in this block we
3259 * need to send a commit AND write the dirty pages before continuing.
3261 * If there's already a dirty range OR dirty pages in this block
3262 * and the new write range is not contiguous with the existing range,
3263 * then force the buffer to be written out now.
3264 * (We used to just extend the dirty range to cover the valid,
3265 * but unwritten, data in between also. But writing ranges
3266 * of data that weren't actually written by an application
3267 * risks overwriting some other client's data with stale data
3268 * that's just masquerading as new written data.)
3270 if (bp
->nb_dirtyend
> 0) {
3271 if (on
> bp
->nb_dirtyend
|| (on
+ n
) < bp
->nb_dirtyoff
|| bp
->nb_dirty
) {
3272 FSDBG(515, np
, uio_offset(uio
), bp
, 0xd15c001);
3273 /* write/commit buffer "synchronously" */
3274 /* (NB_STABLE indicates that data writes should be FILESYNC) */
3275 CLR(bp
->nb_flags
, (NB_DONE
| NB_ERROR
| NB_INVAL
));
3276 SET(bp
->nb_flags
, (NB_ASYNC
| NB_STABLE
));
3277 error
= nfs_buf_write(bp
);
3283 } else if (bp
->nb_dirty
) {
3284 int firstpg
, lastpg
;
3286 /* calculate write range pagemask */
3287 firstpg
= on
/ PAGE_SIZE
;
3288 lastpg
= (on
+ n
- 1) / PAGE_SIZE
;
3289 pagemask
= ((1 << (lastpg
+ 1)) - 1) & ~((1 << firstpg
) - 1);
3290 /* check if there are dirty pages outside the write range */
3291 if (bp
->nb_dirty
& ~pagemask
) {
3292 FSDBG(515, np
, uio_offset(uio
), bp
, 0xd15c002);
3293 /* write/commit buffer "synchronously" */
3294 /* (NB_STABLE indicates that data writes should be FILESYNC) */
3295 CLR(bp
->nb_flags
, (NB_DONE
| NB_ERROR
| NB_INVAL
));
3296 SET(bp
->nb_flags
, (NB_ASYNC
| NB_STABLE
));
3297 error
= nfs_buf_write(bp
);
3303 /* if the first or last pages are already dirty */
3304 /* make sure that the dirty range encompasses those pages */
3305 if (NBPGDIRTY(bp
, firstpg
) || NBPGDIRTY(bp
, lastpg
)) {
3306 FSDBG(515, np
, uio_offset(uio
), bp
, 0xd15c003);
3307 bp
->nb_dirtyoff
= min(on
, firstpg
* PAGE_SIZE
);
3308 if (NBPGDIRTY(bp
, lastpg
)) {
3309 bp
->nb_dirtyend
= (lastpg
+ 1) * PAGE_SIZE
;
3311 if (NBOFF(bp
) + bp
->nb_dirtyend
> (off_t
)np
->n_size
) {
3312 bp
->nb_dirtyend
= np
->n_size
- NBOFF(bp
);
3313 if (bp
->nb_dirtyoff
>= bp
->nb_dirtyend
) {
3314 bp
->nb_dirtyoff
= bp
->nb_dirtyend
= 0;
3318 bp
->nb_dirtyend
= on
+ n
;
3324 * Are we extending the size of the file with this write?
3325 * If so, update file size now that we have the block.
3326 * If there was a partial buf at the old eof, validate
3327 * and zero the new bytes.
3329 if ((uio_offset(uio
) + n
) > (off_t
)np
->n_size
) {
3330 daddr64_t eofbn
= np
->n_size
/ biosize
;
3331 int neweofoff
= (uio_offset(uio
) + n
) % biosize
;
3333 FSDBG(515, 0xb1ffa000, uio_offset(uio
) + n
, eofoff
, neweofoff
);
3335 /* if we're extending within the same last block */
3336 /* and the block is flagged as being cached... */
3337 if ((lbn
== eofbn
) && ISSET(bp
->nb_flags
, NB_CACHE
)) {
3338 /* ...check that all pages in buffer are valid */
3339 int endpg
= ((neweofoff
? neweofoff
: biosize
) - 1) / PAGE_SIZE
;
3341 /* pagemask only has to extend to last page being written to */
3342 pagemask
= (1 << (endpg
+ 1)) - 1;
3343 FSDBG(515, 0xb1ffa001, bp
->nb_valid
, pagemask
, 0);
3344 if ((bp
->nb_valid
& pagemask
) != pagemask
) {
3345 /* zerofill any hole */
3346 if (on
> bp
->nb_validend
) {
3348 for (i
= bp
->nb_validend
/ PAGE_SIZE
; i
<= (on
- 1) / PAGE_SIZE
; i
++) {
3349 NBPGVALID_SET(bp
, i
);
3352 FSDBG(516, bp
, bp
->nb_validend
, on
- bp
->nb_validend
, 0xf01e);
3353 bzero((char *)bp
->nb_data
+ bp
->nb_validend
,
3354 on
- bp
->nb_validend
);
3356 /* zerofill any trailing data in the last page */
3359 FSDBG(516, bp
, neweofoff
, PAGE_SIZE
- (neweofoff
& PAGE_MASK
), 0xe0f);
3360 bzero((char *)bp
->nb_data
+ neweofoff
,
3361 PAGE_SIZE
- (neweofoff
& PAGE_MASK
));
3365 np
->n_size
= uio_offset(uio
) + n
;
3366 nfs_node_lock_force(np
);
3367 CLR(np
->n_flag
, NUPDATESIZE
);
3368 np
->n_flag
|= NMODIFIED
;
3369 nfs_node_unlock(np
);
3370 FSDBG(516, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0001);
3371 ubc_setsize(vp
, (off_t
)np
->n_size
); /* XXX errors */
3374 * If dirtyend exceeds file size, chop it down. This should
3375 * not occur unless there is a race.
3377 if (NBOFF(bp
) + bp
->nb_dirtyend
> (off_t
)np
->n_size
) {
3378 bp
->nb_dirtyend
= np
->n_size
- NBOFF(bp
);
3379 if (bp
->nb_dirtyoff
>= bp
->nb_dirtyend
) {
3380 bp
->nb_dirtyoff
= bp
->nb_dirtyend
= 0;
3384 * UBC doesn't handle partial pages, so we need to make sure
3385 * that any pages left in the page cache are completely valid.
3387 * Writes that are smaller than a block are delayed if they
3388 * don't extend to the end of the block.
3390 * If the block isn't (completely) cached, we may need to read
3391 * in some parts of pages that aren't covered by the write.
3392 * If the write offset (on) isn't page aligned, we'll need to
3393 * read the start of the first page being written to. Likewise,
3394 * if the offset of the end of the write (on+n) isn't page aligned,
3395 * we'll need to read the end of the last page being written to.
3398 * We don't want to read anything we're just going to write over.
3399 * We don't want to read anything we're just going drop when the
3400 * I/O is complete (i.e. don't do reads for NOCACHE requests).
3401 * We don't want to issue multiple I/Os if we don't have to
3402 * (because they're synchronous rpcs).
3403 * We don't want to read anything we already have modified in the
3406 if (!ISSET(bp
->nb_flags
, NB_CACHE
) && (n
< biosize
)) {
3407 int firstpg
, lastpg
, dirtypg
;
3408 int firstpgoff
, lastpgoff
;
3410 firstpg
= on
/ PAGE_SIZE
;
3411 firstpgoff
= on
& PAGE_MASK
;
3412 lastpg
= (on
+ n
- 1) / PAGE_SIZE
;
3413 lastpgoff
= (on
+ n
) & PAGE_MASK
;
3414 if (firstpgoff
&& !NBPGVALID(bp
, firstpg
)) {
3415 /* need to read start of first page */
3416 start
= firstpg
* PAGE_SIZE
;
3417 end
= start
+ firstpgoff
;
3419 if (lastpgoff
&& !NBPGVALID(bp
, lastpg
)) {
3420 /* need to read end of last page */
3422 start
= (lastpg
* PAGE_SIZE
) + lastpgoff
;
3424 end
= (lastpg
+ 1) * PAGE_SIZE
;
3426 if (ISSET(bp
->nb_flags
, NB_NOCACHE
)) {
3428 * For nocache writes, if there is any partial page at the
3429 * start or end of the write range, then we do the write
3430 * synchronously to make sure that we can drop the data
3431 * from the cache as soon as the WRITE finishes. Normally,
3432 * we would do an unstable write and not drop the data until
3433 * it was committed. But doing that here would risk allowing
3434 * invalid data to be read from the cache between the WRITE
3436 * (NB_STABLE indicates that data writes should be FILESYNC)
3439 SET(bp
->nb_flags
, NB_STABLE
);
3444 /* need to read the data in range: start...end-1 */
3446 /* first, check for dirty pages in between */
3447 /* if there are, we'll have to do two reads because */
3448 /* we don't want to overwrite the dirty pages. */
3449 for (dirtypg
= start
/ PAGE_SIZE
; dirtypg
<= (end
- 1) / PAGE_SIZE
; dirtypg
++) {
3450 if (NBPGDIRTY(bp
, dirtypg
)) {
3455 /* if start is at beginning of page, try */
3456 /* to get any preceeding pages as well. */
3457 if (!(start
& PAGE_MASK
)) {
3458 /* stop at next dirty/valid page or start of block */
3459 for (; start
> 0; start
-= PAGE_SIZE
) {
3460 if (NBPGVALID(bp
, ((start
- 1) / PAGE_SIZE
))) {
3467 /* setup uio for read(s) */
3469 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3470 &auio_buf
, sizeof(auio_buf
));
3472 if (dirtypg
<= (end
- 1) / PAGE_SIZE
) {
3473 /* there's a dirty page in the way, so just do two reads */
3474 /* we'll read the preceding data here */
3475 uio_reset(auio
, boff
+ start
, UIO_SYSSPACE
, UIO_READ
);
3476 uio_addiov(auio
, CAST_USER_ADDR_T(bp
->nb_data
+ start
), on
- start
);
3477 error
= nfs_read_rpc(np
, auio
, ctx
);
3479 /* couldn't read the data, so treat buffer as synchronous NOCACHE */
3480 SET(bp
->nb_flags
, (NB_NOCACHE
| NB_STABLE
));
3483 if (uio_resid(auio
) > 0) {
3484 FSDBG(516, bp
, (caddr_t
)uio_curriovbase(auio
) - bp
->nb_data
, uio_resid(auio
), 0xd00dee01);
3485 bzero(CAST_DOWN(caddr_t
, uio_curriovbase(auio
)), uio_resid(auio
));
3488 /* update validoff/validend if necessary */
3489 if ((bp
->nb_validoff
< 0) || (bp
->nb_validoff
> start
)) {
3490 bp
->nb_validoff
= start
;
3492 if ((bp
->nb_validend
< 0) || (bp
->nb_validend
< on
)) {
3493 bp
->nb_validend
= on
;
3495 if ((off_t
)np
->n_size
> boff
+ bp
->nb_validend
) {
3496 bp
->nb_validend
= min(np
->n_size
- (boff
+ start
), biosize
);
3498 /* validate any pages before the write offset */
3499 for (; start
< on
/ PAGE_SIZE
; start
+= PAGE_SIZE
) {
3500 NBPGVALID_SET(bp
, start
/ PAGE_SIZE
);
3503 /* adjust start to read any trailing data */
3507 /* if end is at end of page, try to */
3508 /* get any following pages as well. */
3509 if (!(end
& PAGE_MASK
)) {
3510 /* stop at next valid page or end of block */
3511 for (; end
< biosize
; end
+= PAGE_SIZE
) {
3512 if (NBPGVALID(bp
, end
/ PAGE_SIZE
)) {
3518 if (((boff
+ start
) >= (off_t
)np
->n_size
) ||
3519 ((start
>= on
) && ((boff
+ on
+ n
) >= (off_t
)np
->n_size
))) {
3521 * Either this entire read is beyond the current EOF
3522 * or the range that we won't be modifying (on+n...end)
3523 * is all beyond the current EOF.
3524 * No need to make a trip across the network to
3525 * read nothing. So, just zero the buffer instead.
3527 FSDBG(516, bp
, start
, end
- start
, 0xd00dee00);
3528 bzero(bp
->nb_data
+ start
, end
- start
);
3531 /* now we'll read the (rest of the) data */
3532 uio_reset(auio
, boff
+ start
, UIO_SYSSPACE
, UIO_READ
);
3533 uio_addiov(auio
, CAST_USER_ADDR_T(bp
->nb_data
+ start
), end
- start
);
3534 error
= nfs_read_rpc(np
, auio
, ctx
);
3536 /* couldn't read the data, so treat buffer as synchronous NOCACHE */
3537 SET(bp
->nb_flags
, (NB_NOCACHE
| NB_STABLE
));
3540 if (uio_resid(auio
) > 0) {
3541 FSDBG(516, bp
, (caddr_t
)uio_curriovbase(auio
) - bp
->nb_data
, uio_resid(auio
), 0xd00dee02);
3542 bzero(CAST_DOWN(caddr_t
, uio_curriovbase(auio
)), uio_resid(auio
));
3546 /* update validoff/validend if necessary */
3547 if ((bp
->nb_validoff
< 0) || (bp
->nb_validoff
> start
)) {
3548 bp
->nb_validoff
= start
;
3550 if ((bp
->nb_validend
< 0) || (bp
->nb_validend
< end
)) {
3551 bp
->nb_validend
= end
;
3553 if ((off_t
)np
->n_size
> boff
+ bp
->nb_validend
) {
3554 bp
->nb_validend
= min(np
->n_size
- (boff
+ start
), biosize
);
3556 /* validate any pages before the write offset's page */
3557 for (; start
< (off_t
)trunc_page_32(on
); start
+= PAGE_SIZE
) {
3558 NBPGVALID_SET(bp
, start
/ PAGE_SIZE
);
3560 /* validate any pages after the range of pages being written to */
3561 for (; (end
- 1) > (off_t
)round_page_32(on
+ n
- 1); end
-= PAGE_SIZE
) {
3562 NBPGVALID_SET(bp
, (end
- 1) / PAGE_SIZE
);
3565 /* Note: pages being written to will be validated when written */
3570 if (ISSET(bp
->nb_flags
, NB_ERROR
)) {
3571 error
= bp
->nb_error
;
3572 nfs_buf_release(bp
, 1);
3576 nfs_node_lock_force(np
);
3577 np
->n_flag
|= NMODIFIED
;
3578 nfs_node_unlock(np
);
3581 error
= uiomove((char *)bp
->nb_data
+ on
, n
, uio
);
3583 SET(bp
->nb_flags
, NB_ERROR
);
3584 nfs_buf_release(bp
, 1);
3588 /* validate any pages written to */
3589 start
= on
& ~PAGE_MASK
;
3590 for (; start
< on
+ n
; start
+= PAGE_SIZE
) {
3591 NBPGVALID_SET(bp
, start
/ PAGE_SIZE
);
3593 * This may seem a little weird, but we don't actually set the
3594 * dirty bits for writes. This is because we keep the dirty range
3595 * in the nb_dirtyoff/nb_dirtyend fields. Also, particularly for
3596 * delayed writes, when we give the pages back to the VM we don't
3597 * want to keep them marked dirty, because when we later write the
3598 * buffer we won't be able to tell which pages were written dirty
3599 * and which pages were mmapped and dirtied.
3602 if (bp
->nb_dirtyend
> 0) {
3603 bp
->nb_dirtyoff
= min(on
, bp
->nb_dirtyoff
);
3604 bp
->nb_dirtyend
= max((on
+ n
), bp
->nb_dirtyend
);
3606 bp
->nb_dirtyoff
= on
;
3607 bp
->nb_dirtyend
= on
+ n
;
3609 if (bp
->nb_validend
<= 0 || bp
->nb_validend
< bp
->nb_dirtyoff
||
3610 bp
->nb_validoff
> bp
->nb_dirtyend
) {
3611 bp
->nb_validoff
= bp
->nb_dirtyoff
;
3612 bp
->nb_validend
= bp
->nb_dirtyend
;
3614 bp
->nb_validoff
= min(bp
->nb_validoff
, bp
->nb_dirtyoff
);
3615 bp
->nb_validend
= max(bp
->nb_validend
, bp
->nb_dirtyend
);
3617 if (!ISSET(bp
->nb_flags
, NB_CACHE
)) {
3618 nfs_buf_normalize_valid_range(np
, bp
);
3622 * Since this block is being modified, it must be written
3623 * again and not just committed.
3625 if (ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
)) {
3626 nfs_node_lock_force(np
);
3627 if (ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
)) {
3628 np
->n_needcommitcnt
--;
3629 CHECK_NEEDCOMMITCNT(np
);
3631 CLR(bp
->nb_flags
, NB_NEEDCOMMIT
);
3632 nfs_node_unlock(np
);
3635 if (ioflag
& IO_SYNC
) {
3636 error
= nfs_buf_write(bp
);
3640 } else if (((n
+ on
) == biosize
) || (ioflag
& IO_APPEND
) ||
3641 (ioflag
& IO_NOCACHE
) || ISSET(bp
->nb_flags
, NB_NOCACHE
)) {
3642 SET(bp
->nb_flags
, NB_ASYNC
);
3643 error
= nfs_buf_write(bp
);
3648 /* If the block wasn't already delayed: charge for the write */
3649 if (!ISSET(bp
->nb_flags
, NB_DELWRI
)) {
3650 proc_t p
= vfs_context_proc(ctx
);
3651 if (p
&& p
->p_stats
) {
3652 OSIncrementAtomicLong(&p
->p_stats
->p_ru
.ru_oublock
);
3655 nfs_buf_write_delayed(bp
);
3659 if (np
->n_needcommitcnt
>= NFS_A_LOT_OF_NEEDCOMMITS
) {
3660 nfs_flushcommits(np
, 1);
3662 } while (uio_resid(uio
) > 0 && n
> 0);
3665 nfs_node_lock_force(np
);
3667 if ((ioflag
& IO_SYNC
) && !np
->n_wrbusy
&& !np
->n_numoutput
) {
3668 np
->n_flag
&= ~NMODIFIED
;
3670 nfs_node_unlock(np
);
3671 nfs_data_unlock(np
);
3672 FSDBG_BOT(515, np
, uio_offset(uio
), uio_resid(uio
), error
);
3688 return nfs_write_rpc2(np
, uio
, vfs_context_thread(ctx
), vfs_context_ucred(ctx
), iomodep
, wverfp
);
3700 struct nfsmount
*nmp
;
3701 int error
= 0, nfsvers
;
3702 int wverfset
, commit
, committed
;
3703 uint64_t wverf
= 0, wverf2
;
3704 size_t nmwsize
, totalsize
, tsiz
, len
, rlen
;
3705 struct nfsreq rq
, *req
= &rq
;
3707 uint32_t stategenid
= 0, restart
= 0;
3709 uint32_t vrestart
= 0;
3710 uio_t uio_save
= NULL
;
3713 /* XXX limitation based on need to back up uio on short write */
3714 if (uio_iovcnt(uio
) != 1) {
3715 panic("nfs3_write_rpc: iovcnt > 1");
3718 FSDBG_TOP(537, np
, uio_offset(uio
), uio_resid(uio
), *iomodep
);
3720 if (nfs_mount_gone(nmp
)) {
3723 nfsvers
= nmp
->nm_vers
;
3724 nmwsize
= nmp
->nm_wsize
;
3727 committed
= NFS_WRITE_FILESYNC
;
3729 totalsize
= tsiz
= uio_resid(uio
);
3730 if ((nfsvers
== NFS_VER2
) && ((uint64_t)(uio_offset(uio
) + tsiz
) > 0xffffffffULL
)) {
3731 FSDBG_BOT(537, np
, uio_offset(uio
), uio_resid(uio
), EFBIG
);
3735 uio_save
= uio_duplicate(uio
);
3736 if (uio_save
== NULL
) {
3741 len
= (tsiz
> nmwsize
) ? nmwsize
: tsiz
;
3742 FSDBG(537, np
, uio_offset(uio
), len
, 0);
3743 if (np
->n_flag
& NREVOKE
) {
3748 if (nmp
->nm_vers
>= NFS_VER4
) {
3749 stategenid
= nmp
->nm_stategenid
;
3752 error
= nmp
->nm_funcs
->nf_write_rpc_async(np
, uio
, len
, thd
, cred
, *iomodep
, NULL
, &req
);
3754 error
= nmp
->nm_funcs
->nf_write_rpc_async_finish(np
, req
, &commit
, &rlen
, &wverf2
);
3757 if (nfs_mount_gone(nmp
)) {
3761 if ((nmp
->nm_vers
>= NFS_VER4
) && nfs_mount_state_error_should_restart(error
) &&
3762 (++restart
<= nfs_mount_state_max_restarts(nmp
))) { /* guard against no progress */
3763 lck_mtx_lock(&nmp
->nm_lock
);
3764 if ((error
!= NFSERR_GRACE
) && (stategenid
== nmp
->nm_stategenid
)) {
3765 NP(np
, "nfs_write_rpc: error %d, initiating recovery", error
);
3766 nfs_need_recover(nmp
, error
);
3768 lck_mtx_unlock(&nmp
->nm_lock
);
3769 if (np
->n_flag
& NREVOKE
) {
3772 if (error
== NFSERR_GRACE
) {
3773 tsleep(&nmp
->nm_state
, (PZERO
- 1), "nfsgrace", 2 * hz
);
3775 if (!(error
= nfs_mount_state_wait_for_recovery(nmp
))) {
3784 if (nfsvers
== NFS_VER2
) {
3789 /* check for a short write */
3791 /* Reset the uio to reflect the actual transfer */
3793 uio_update(uio
, totalsize
- (tsiz
- rlen
));
3797 /* return lowest commit level returned */
3798 if (commit
< committed
) {
3804 /* check write verifier */
3808 } else if (wverf
!= wverf2
) {
3809 /* verifier changed, so we need to restart all the writes */
3810 if (++vrestart
> 100) {
3811 /* give up after too many restarts */
3815 *uio
= *uio_save
; // Reset the uio back to the start
3816 committed
= NFS_WRITE_FILESYNC
;
3824 if (wverfset
&& wverfp
) {
3827 *iomodep
= committed
;
3829 uio_setresid(uio
, tsiz
);
3831 FSDBG_BOT(537, np
, committed
, uio_resid(uio
), error
);
3836 nfs3_write_rpc_async(
3843 struct nfsreq_cbinfo
*cb
,
3844 struct nfsreq
**reqp
)
3846 struct nfsmount
*nmp
;
3848 int error
= 0, nfsvers
;
3849 struct nfsm_chain nmreq
;
3852 if (nfs_mount_gone(nmp
)) {
3855 nfsvers
= nmp
->nm_vers
;
3857 /* for async mounts, don't bother sending sync write requests */
3858 if ((iomode
!= NFS_WRITE_UNSTABLE
) && nfs_allow_async
&&
3859 ((mp
= NFSTOMP(np
))) && (vfs_flags(mp
) & MNT_ASYNC
)) {
3860 iomode
= NFS_WRITE_UNSTABLE
;
3863 nfsm_chain_null(&nmreq
);
3864 nfsm_chain_build_alloc_init(error
, &nmreq
,
3865 NFSX_FH(nfsvers
) + 5 * NFSX_UNSIGNED
+ nfsm_rndup(len
));
3866 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
3867 if (nfsvers
== NFS_VER3
) {
3868 nfsm_chain_add_64(error
, &nmreq
, uio_offset(uio
));
3869 nfsm_chain_add_32(error
, &nmreq
, len
);
3870 nfsm_chain_add_32(error
, &nmreq
, iomode
);
3872 nfsm_chain_add_32(error
, &nmreq
, 0);
3873 nfsm_chain_add_32(error
, &nmreq
, uio_offset(uio
));
3874 nfsm_chain_add_32(error
, &nmreq
, 0);
3876 nfsm_chain_add_32(error
, &nmreq
, len
);
3878 error
= nfsm_chain_add_uio(&nmreq
, uio
, len
);
3879 nfsm_chain_build_done(error
, &nmreq
);
3881 error
= nfs_request_async(np
, NULL
, &nmreq
, NFSPROC_WRITE
, thd
, cred
, NULL
, 0, cb
, reqp
);
3883 nfsm_chain_cleanup(&nmreq
);
3888 nfs3_write_rpc_async_finish(
3895 struct nfsmount
*nmp
;
3896 int error
= 0, lockerror
= ENOENT
, nfsvers
, status
;
3897 int updatemtime
= 0, wccpostattr
= 0, rlen
, committed
= NFS_WRITE_FILESYNC
;
3898 u_int64_t xid
, wverf
;
3900 struct nfsm_chain nmrep
;
3903 if (nfs_mount_gone(nmp
)) {
3904 nfs_request_async_cancel(req
);
3907 nfsvers
= nmp
->nm_vers
;
3909 nfsm_chain_null(&nmrep
);
3911 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
3912 if (error
== EINPROGRESS
) { /* async request restarted */
3916 if (nfs_mount_gone(nmp
)) {
3919 if (!error
&& (lockerror
= nfs_node_lock(np
))) {
3922 if (nfsvers
== NFS_VER3
) {
3923 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
3924 nfsm_chain_get_wcc_data(error
, &nmrep
, np
, &premtime
, &wccpostattr
, &xid
);
3925 if (nfstimespeccmp(&np
->n_mtime
, &premtime
, ==)) {
3931 nfsm_chain_get_32(error
, &nmrep
, rlen
);
3937 nfsm_chain_get_32(error
, &nmrep
, committed
);
3938 nfsm_chain_get_64(error
, &nmrep
, wverf
);
3943 lck_mtx_lock(&nmp
->nm_lock
);
3944 if (!(nmp
->nm_state
& NFSSTA_HASWRITEVERF
)) {
3945 nmp
->nm_verf
= wverf
;
3946 nmp
->nm_state
|= NFSSTA_HASWRITEVERF
;
3947 } else if (nmp
->nm_verf
!= wverf
) {
3948 nmp
->nm_verf
= wverf
;
3950 lck_mtx_unlock(&nmp
->nm_lock
);
3955 nfsm_chain_loadattr(error
, &nmrep
, np
, nfsvers
, &xid
);
3959 NFS_CHANGED_UPDATE(nfsvers
, np
, &np
->n_vattr
);
3963 nfs_node_unlock(np
);
3965 nfsm_chain_cleanup(&nmrep
);
3966 if ((committed
!= NFS_WRITE_FILESYNC
) && nfs_allow_async
&&
3967 ((mp
= NFSTOMP(np
))) && (vfs_flags(mp
) & MNT_ASYNC
)) {
3968 committed
= NFS_WRITE_FILESYNC
;
3970 *iomodep
= committed
;
3975 * NFS mknod vnode op
3977 * For NFS v2 this is a kludge. Use a create RPC but with the IFMT bits of the
3978 * mode set to specify the file type and the size field for rdev.
3982 struct vnop_mknod_args
/* {
3983 * struct vnodeop_desc *a_desc;
3986 * struct componentname *a_cnp;
3987 * struct vnode_attr *a_vap;
3988 * vfs_context_t a_context;
3991 vnode_t dvp
= ap
->a_dvp
;
3992 vnode_t
*vpp
= ap
->a_vpp
;
3993 struct componentname
*cnp
= ap
->a_cnp
;
3994 struct vnode_attr
*vap
= ap
->a_vap
;
3995 vfs_context_t ctx
= ap
->a_context
;
3996 vnode_t newvp
= NULL
;
3997 nfsnode_t np
= NULL
;
3998 struct nfsmount
*nmp
;
3999 nfsnode_t dnp
= VTONFS(dvp
);
4000 struct nfs_vattr nvattr
;
4002 int error
= 0, lockerror
= ENOENT
, busyerror
= ENOENT
, status
, wccpostattr
= 0;
4003 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
4005 u_int64_t xid
= 0, dxid
;
4006 int nfsvers
, gotuid
, gotgid
;
4007 struct nfsm_chain nmreq
, nmrep
;
4008 struct nfsreq rq
, *req
= &rq
;
4011 if (nfs_mount_gone(nmp
)) {
4014 nfsvers
= nmp
->nm_vers
;
4016 if (!VATTR_IS_ACTIVE(vap
, va_type
)) {
4019 if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
) {
4020 if (!VATTR_IS_ACTIVE(vap
, va_rdev
)) {
4023 rdev
= vap
->va_rdev
;
4024 } else if (vap
->va_type
== VFIFO
|| vap
->va_type
== VSOCK
) {
4029 if ((nfsvers
== NFS_VER2
) && (cnp
->cn_namelen
> NFS_MAXNAMLEN
)) {
4030 return ENAMETOOLONG
;
4033 nfs_avoid_needless_id_setting_on_create(dnp
, vap
, ctx
);
4035 VATTR_SET_SUPPORTED(vap
, va_mode
);
4036 VATTR_SET_SUPPORTED(vap
, va_uid
);
4037 VATTR_SET_SUPPORTED(vap
, va_gid
);
4038 VATTR_SET_SUPPORTED(vap
, va_data_size
);
4039 VATTR_SET_SUPPORTED(vap
, va_access_time
);
4040 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
4041 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
4042 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
4044 nfsm_chain_null(&nmreq
);
4045 nfsm_chain_null(&nmrep
);
4047 nfsm_chain_build_alloc_init(error
, &nmreq
,
4048 NFSX_FH(nfsvers
) + 4 * NFSX_UNSIGNED
+
4049 nfsm_rndup(cnp
->cn_namelen
) + NFSX_SATTR(nfsvers
));
4050 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
4051 nfsm_chain_add_name(error
, &nmreq
, cnp
->cn_nameptr
, cnp
->cn_namelen
, nmp
);
4052 if (nfsvers
== NFS_VER3
) {
4053 nfsm_chain_add_32(error
, &nmreq
, vtonfs_type(vap
->va_type
, nfsvers
));
4054 nfsm_chain_add_v3sattr(nmp
, error
, &nmreq
, vap
);
4055 if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
) {
4056 nfsm_chain_add_32(error
, &nmreq
, major(vap
->va_rdev
));
4057 nfsm_chain_add_32(error
, &nmreq
, minor(vap
->va_rdev
));
4060 nfsm_chain_add_v2sattr(error
, &nmreq
, vap
, rdev
);
4062 nfsm_chain_build_done(error
, &nmreq
);
4064 error
= busyerror
= nfs_node_set_busy(dnp
, vfs_context_thread(ctx
));
4068 error
= nfs_request_async(dnp
, NULL
, &nmreq
, NFSPROC_MKNOD
,
4069 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, 0, NULL
, &req
);
4071 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
4074 if ((lockerror
= nfs_node_lock(dnp
))) {
4077 /* XXX no EEXIST kludge here? */
4079 if (!error
&& !status
) {
4080 if (dnp
->n_flag
& NNEGNCENTRIES
) {
4081 dnp
->n_flag
&= ~NNEGNCENTRIES
;
4082 cache_purge_negatives(dvp
);
4084 error
= nfsm_chain_get_fh_attr(nmp
, &nmrep
, dnp
, ctx
, nfsvers
, &xid
, &fh
, &nvattr
);
4086 if (nfsvers
== NFS_VER3
) {
4087 nfsm_chain_get_wcc_data(error
, &nmrep
, dnp
, &premtime
, &wccpostattr
, &dxid
);
4093 nfsm_chain_cleanup(&nmreq
);
4094 nfsm_chain_cleanup(&nmrep
);
4097 dnp
->n_flag
|= NMODIFIED
;
4098 /* if directory hadn't changed, update namecache mtime */
4099 if (nfstimespeccmp(&dnp
->n_ncmtime
, &premtime
, ==)) {
4100 NFS_CHANGED_UPDATE_NC(nfsvers
, dnp
, &dnp
->n_vattr
);
4102 nfs_node_unlock(dnp
);
4103 /* nfs_getattr() will check changed and purge caches */
4104 nfs_getattr(dnp
, NULL
, ctx
, wccpostattr
? NGA_CACHED
: NGA_UNCACHED
);
4107 if (!error
&& fh
.fh_len
) {
4108 error
= nfs_nget(NFSTOMP(dnp
), dnp
, cnp
, fh
.fh_data
, fh
.fh_len
, &nvattr
, &xid
, rq
.r_auth
, NG_MAKEENTRY
, &np
);
4110 if (!error
&& !np
) {
4111 error
= nfs_lookitup(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
, &np
);
4117 nfs_node_clear_busy(dnp
);
4120 if (!error
&& (gotuid
|| gotgid
) &&
4121 (!newvp
|| nfs_getattrcache(np
, &nvattr
, 0) ||
4122 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
4123 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
4124 /* clear ID bits if server didn't use them (or we can't tell) */
4125 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
4126 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
4130 nfs_node_unlock(np
);
4135 nfs_node_unlock(np
);
4140 static uint32_t create_verf
;
4142 * NFS file create call
4146 struct vnop_create_args
/* {
4147 * struct vnodeop_desc *a_desc;
4150 * struct componentname *a_cnp;
4151 * struct vnode_attr *a_vap;
4152 * vfs_context_t a_context;
4155 vfs_context_t ctx
= ap
->a_context
;
4156 vnode_t dvp
= ap
->a_dvp
;
4157 struct vnode_attr
*vap
= ap
->a_vap
;
4158 struct componentname
*cnp
= ap
->a_cnp
;
4159 struct nfs_vattr nvattr
;
4161 nfsnode_t np
= NULL
;
4162 struct nfsmount
*nmp
;
4163 nfsnode_t dnp
= VTONFS(dvp
);
4164 vnode_t newvp
= NULL
;
4165 int error
= 0, lockerror
= ENOENT
, busyerror
= ENOENT
, status
, wccpostattr
= 0, fmode
= 0;
4166 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
4167 int nfsvers
, gotuid
, gotgid
;
4168 u_int64_t xid
, dxid
;
4170 struct nfsm_chain nmreq
, nmrep
;
4171 struct nfsreq rq
, *req
= &rq
;
4172 struct nfs_dulookup dul
;
4173 int dul_in_progress
= 0;
4177 if (nfs_mount_gone(nmp
)) {
4180 nfsvers
= nmp
->nm_vers
;
4181 namedattrs
= (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
);
4183 if ((nfsvers
== NFS_VER2
) && (cnp
->cn_namelen
> NFS_MAXNAMLEN
)) {
4184 return ENAMETOOLONG
;
4187 nfs_avoid_needless_id_setting_on_create(dnp
, vap
, ctx
);
4189 VATTR_SET_SUPPORTED(vap
, va_mode
);
4190 VATTR_SET_SUPPORTED(vap
, va_uid
);
4191 VATTR_SET_SUPPORTED(vap
, va_gid
);
4192 VATTR_SET_SUPPORTED(vap
, va_data_size
);
4193 VATTR_SET_SUPPORTED(vap
, va_access_time
);
4194 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
4195 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
4196 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
4198 if ((vap
->va_vaflags
& VA_EXCLUSIVE
)
4201 if (!VATTR_IS_ACTIVE(vap
, va_access_time
) || !VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
4202 vap
->va_vaflags
|= VA_UTIMES_NULL
;
4207 error
= busyerror
= nfs_node_set_busy(dnp
, vfs_context_thread(ctx
));
4209 nfs_dulookup_init(&dul
, dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
);
4212 nfsm_chain_null(&nmreq
);
4213 nfsm_chain_null(&nmrep
);
4215 nfsm_chain_build_alloc_init(error
, &nmreq
,
4216 NFSX_FH(nfsvers
) + 2 * NFSX_UNSIGNED
+
4217 nfsm_rndup(cnp
->cn_namelen
) + NFSX_SATTR(nfsvers
));
4218 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
4219 nfsm_chain_add_name(error
, &nmreq
, cnp
->cn_nameptr
, cnp
->cn_namelen
, nmp
);
4220 if (nfsvers
== NFS_VER3
) {
4221 if (fmode
& O_EXCL
) {
4222 nfsm_chain_add_32(error
, &nmreq
, NFS_CREATE_EXCLUSIVE
);
4223 lck_rw_lock_shared(in_ifaddr_rwlock
);
4224 if (!TAILQ_EMPTY(&in_ifaddrhead
)) {
4225 val
= IA_SIN(in_ifaddrhead
.tqh_first
)->sin_addr
.s_addr
;
4229 lck_rw_done(in_ifaddr_rwlock
);
4230 nfsm_chain_add_32(error
, &nmreq
, val
);
4232 nfsm_chain_add_32(error
, &nmreq
, create_verf
);
4234 nfsm_chain_add_32(error
, &nmreq
, NFS_CREATE_UNCHECKED
);
4235 nfsm_chain_add_v3sattr(nmp
, error
, &nmreq
, vap
);
4238 nfsm_chain_add_v2sattr(error
, &nmreq
, vap
, 0);
4240 nfsm_chain_build_done(error
, &nmreq
);
4243 error
= nfs_request_async(dnp
, NULL
, &nmreq
, NFSPROC_CREATE
,
4244 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, 0, NULL
, &req
);
4247 nfs_dulookup_start(&dul
, dnp
, ctx
);
4248 dul_in_progress
= 1;
4250 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
4253 if ((lockerror
= nfs_node_lock(dnp
))) {
4257 if (!error
&& !status
) {
4258 if (dnp
->n_flag
& NNEGNCENTRIES
) {
4259 dnp
->n_flag
&= ~NNEGNCENTRIES
;
4260 cache_purge_negatives(dvp
);
4262 error
= nfsm_chain_get_fh_attr(nmp
, &nmrep
, dnp
, ctx
, nfsvers
, &xid
, &fh
, &nvattr
);
4264 if (nfsvers
== NFS_VER3
) {
4265 nfsm_chain_get_wcc_data(error
, &nmrep
, dnp
, &premtime
, &wccpostattr
, &dxid
);
4271 nfsm_chain_cleanup(&nmreq
);
4272 nfsm_chain_cleanup(&nmrep
);
4275 dnp
->n_flag
|= NMODIFIED
;
4276 /* if directory hadn't changed, update namecache mtime */
4277 if (nfstimespeccmp(&dnp
->n_ncmtime
, &premtime
, ==)) {
4278 NFS_CHANGED_UPDATE_NC(nfsvers
, dnp
, &dnp
->n_vattr
);
4280 nfs_node_unlock(dnp
);
4281 /* nfs_getattr() will check changed and purge caches */
4282 nfs_getattr(dnp
, NULL
, ctx
, wccpostattr
? NGA_CACHED
: NGA_UNCACHED
);
4285 if (!error
&& fh
.fh_len
) {
4286 error
= nfs_nget(NFSTOMP(dnp
), dnp
, cnp
, fh
.fh_data
, fh
.fh_len
, &nvattr
, &xid
, rq
.r_auth
, NG_MAKEENTRY
, &np
);
4288 if (!error
&& !np
) {
4289 error
= nfs_lookitup(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
, &np
);
4295 if (dul_in_progress
) {
4296 nfs_dulookup_finish(&dul
, dnp
, ctx
);
4299 nfs_node_clear_busy(dnp
);
4303 if ((nfsvers
== NFS_VER3
) && (fmode
& O_EXCL
) && (error
== NFSERR_NOTSUPP
)) {
4308 nfs_node_unlock(np
);
4311 } else if ((nfsvers
== NFS_VER3
) && (fmode
& O_EXCL
)) {
4312 nfs_node_unlock(np
);
4313 error
= nfs3_setattr_rpc(np
, vap
, ctx
);
4314 if (error
&& (gotuid
|| gotgid
)) {
4315 /* it's possible the server didn't like our attempt to set IDs. */
4316 /* so, let's try it again without those */
4317 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
4318 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
4319 error
= nfs3_setattr_rpc(np
, vap
, ctx
);
4324 nfs_node_lock_force(np
);
4330 if (!error
&& (gotuid
|| gotgid
) &&
4331 (!newvp
|| nfs_getattrcache(np
, &nvattr
, 0) ||
4332 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
4333 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
4334 /* clear ID bits if server didn't use them (or we can't tell) */
4335 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
4336 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
4339 nfs_node_unlock(np
);
4345 * NFS file remove call
4346 * To try and make NFS semantics closer to UFS semantics, a file that has
4347 * other processes using the vnode is renamed instead of removed and then
4348 * removed later on the last close.
4349 * - If vnode_isinuse()
4350 * If a rename is not already in the works
4351 * call nfs_sillyrename() to set it up
4357 struct vnop_remove_args
/* {
4358 * struct vnodeop_desc *a_desc;
4361 * struct componentname *a_cnp;
4363 * vfs_context_t a_context;
4366 vfs_context_t ctx
= ap
->a_context
;
4367 vnode_t vp
= ap
->a_vp
;
4368 vnode_t dvp
= ap
->a_dvp
;
4369 struct componentname
*cnp
= ap
->a_cnp
;
4370 nfsnode_t dnp
= VTONFS(dvp
);
4371 nfsnode_t np
= VTONFS(vp
);
4372 int error
= 0, nfsvers
, namedattrs
, inuse
, gotattr
= 0, flushed
= 0, setsize
= 0;
4373 struct nfs_vattr nvattr
;
4374 struct nfsmount
*nmp
;
4375 struct nfs_dulookup dul
;
4377 /* XXX prevent removing a sillyrenamed file? */
4379 nmp
= NFSTONMP(dnp
);
4380 if (nfs_mount_gone(nmp
)) {
4383 nfsvers
= nmp
->nm_vers
;
4384 namedattrs
= (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
);
4387 error
= nfs_node_set_busy2(dnp
, np
, vfs_context_thread(ctx
));
4392 /* lock the node while we remove the file */
4393 lck_mtx_lock(nfs_node_hash_mutex
);
4394 while (np
->n_hflag
& NHLOCKED
) {
4395 np
->n_hflag
|= NHLOCKWANT
;
4396 msleep(np
, nfs_node_hash_mutex
, PINOD
, "nfs_remove", NULL
);
4398 np
->n_hflag
|= NHLOCKED
;
4399 lck_mtx_unlock(nfs_node_hash_mutex
);
4402 nfs_dulookup_init(&dul
, dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
);
4405 inuse
= vnode_isinuse(vp
, 0);
4406 if ((ap
->a_flags
& VNODE_REMOVE_NODELETEBUSY
) && inuse
) {
4407 /* Caller requested Carbon delete semantics, but file is busy */
4411 if (inuse
&& !gotattr
) {
4412 if (nfs_getattr(np
, &nvattr
, ctx
, NGA_CACHED
)) {
4413 nvattr
.nva_nlink
= 1;
4418 if (!inuse
|| (np
->n_sillyrename
&& (nvattr
.nva_nlink
> 1))) {
4419 if (!inuse
&& !flushed
) { /* flush all the buffers first */
4420 /* unlock the node */
4421 lck_mtx_lock(nfs_node_hash_mutex
);
4422 np
->n_hflag
&= ~NHLOCKED
;
4423 if (np
->n_hflag
& NHLOCKWANT
) {
4424 np
->n_hflag
&= ~NHLOCKWANT
;
4427 lck_mtx_unlock(nfs_node_hash_mutex
);
4428 nfs_node_clear_busy2(dnp
, np
);
4429 error
= nfs_vinvalbuf(vp
, V_SAVE
, ctx
, 1);
4430 FSDBG(260, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0011);
4432 if (error
== EINTR
) {
4433 nfs_node_lock_force(np
);
4434 NATTRINVALIDATE(np
);
4435 nfs_node_unlock(np
);
4439 nfs_dulookup_finish(&dul
, dnp
, ctx
);
4444 if ((nmp
->nm_vers
>= NFS_VER4
) && (np
->n_openflags
& N_DELEG_MASK
)) {
4445 nfs4_delegation_return(np
, 0, vfs_context_thread(ctx
), vfs_context_ucred(ctx
));
4449 * Purge the name cache so that the chance of a lookup for
4450 * the name succeeding while the remove is in progress is
4453 nfs_name_cache_purge(dnp
, np
, cnp
, ctx
);
4456 nfs_dulookup_start(&dul
, dnp
, ctx
);
4460 error
= nmp
->nm_funcs
->nf_remove_rpc(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
,
4461 vfs_context_thread(ctx
), vfs_context_ucred(ctx
));
4464 * Kludge City: If the first reply to the remove rpc is lost..
4465 * the reply to the retransmitted request will be ENOENT
4466 * since the file was in fact removed
4467 * Therefore, we cheat and return success.
4469 if (error
== ENOENT
) {
4473 if (!error
&& !inuse
&& !np
->n_sillyrename
) {
4475 * removal succeeded, it's not in use, and not silly renamed so
4476 * remove nfsnode from hash now so we can't accidentally find it
4477 * again if another object gets created with the same filehandle
4478 * before this vnode gets reclaimed
4480 lck_mtx_lock(nfs_node_hash_mutex
);
4481 if (np
->n_hflag
& NHHASHED
) {
4482 LIST_REMOVE(np
, n_hash
);
4483 np
->n_hflag
&= ~NHHASHED
;
4484 FSDBG(266, 0, np
, np
->n_flag
, 0xb1eb1e);
4486 lck_mtx_unlock(nfs_node_hash_mutex
);
4487 /* clear flags now: won't get nfs_vnop_inactive for recycled vnode */
4488 /* clear all flags other than these */
4489 nfs_node_lock_force(np
);
4490 np
->n_flag
&= (NMODIFIED
);
4491 NATTRINVALIDATE(np
);
4492 nfs_node_unlock(np
);
4496 nfs_node_lock_force(np
);
4497 NATTRINVALIDATE(np
);
4498 nfs_node_unlock(np
);
4500 } else if (!np
->n_sillyrename
) {
4502 nfs_dulookup_start(&dul
, dnp
, ctx
);
4504 error
= nfs_sillyrename(dnp
, np
, cnp
, ctx
);
4505 nfs_node_lock_force(np
);
4506 NATTRINVALIDATE(np
);
4507 nfs_node_unlock(np
);
4509 nfs_node_lock_force(np
);
4510 NATTRINVALIDATE(np
);
4511 nfs_node_unlock(np
);
4513 nfs_dulookup_start(&dul
, dnp
, ctx
);
4517 /* nfs_getattr() will check changed and purge caches */
4518 nfs_getattr(dnp
, NULL
, ctx
, NGA_CACHED
);
4520 nfs_dulookup_finish(&dul
, dnp
, ctx
);
4523 /* unlock the node */
4524 lck_mtx_lock(nfs_node_hash_mutex
);
4525 np
->n_hflag
&= ~NHLOCKED
;
4526 if (np
->n_hflag
& NHLOCKWANT
) {
4527 np
->n_hflag
&= ~NHLOCKWANT
;
4530 lck_mtx_unlock(nfs_node_hash_mutex
);
4531 nfs_node_clear_busy2(dnp
, np
);
4539 * NFS silly-renamed file removal function called from nfs_vnop_inactive
4542 nfs_removeit(struct nfs_sillyrename
*nsp
)
4544 struct nfsmount
*nmp
= NFSTONMP(nsp
->nsr_dnp
);
4545 if (nfs_mount_gone(nmp
)) {
4548 return nmp
->nm_funcs
->nf_remove_rpc(nsp
->nsr_dnp
, nsp
->nsr_name
, nsp
->nsr_namlen
, NULL
, nsp
->nsr_cred
);
4552 * NFS remove rpc, called from nfs_remove() and nfs_removeit().
4562 int error
= 0, lockerror
= ENOENT
, status
, wccpostattr
= 0;
4563 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
4564 struct nfsmount
*nmp
;
4567 struct nfsm_chain nmreq
, nmrep
;
4569 nmp
= NFSTONMP(dnp
);
4570 if (nfs_mount_gone(nmp
)) {
4573 nfsvers
= nmp
->nm_vers
;
4574 if ((nfsvers
== NFS_VER2
) && (namelen
> NFS_MAXNAMLEN
)) {
4575 return ENAMETOOLONG
;
4578 nfsm_chain_null(&nmreq
);
4579 nfsm_chain_null(&nmrep
);
4581 nfsm_chain_build_alloc_init(error
, &nmreq
,
4582 NFSX_FH(nfsvers
) + NFSX_UNSIGNED
+ nfsm_rndup(namelen
));
4583 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
4584 nfsm_chain_add_name(error
, &nmreq
, name
, namelen
, nmp
);
4585 nfsm_chain_build_done(error
, &nmreq
);
4588 error
= nfs_request2(dnp
, NULL
, &nmreq
, NFSPROC_REMOVE
, thd
, cred
, NULL
, 0, &nmrep
, &xid
, &status
);
4590 if ((lockerror
= nfs_node_lock(dnp
))) {
4593 if (nfsvers
== NFS_VER3
) {
4594 nfsm_chain_get_wcc_data(error
, &nmrep
, dnp
, &premtime
, &wccpostattr
, &xid
);
4597 dnp
->n_flag
|= NMODIFIED
;
4598 /* if directory hadn't changed, update namecache mtime */
4599 if (nfstimespeccmp(&dnp
->n_ncmtime
, &premtime
, ==)) {
4600 NFS_CHANGED_UPDATE_NC(nfsvers
, dnp
, &dnp
->n_vattr
);
4603 NATTRINVALIDATE(dnp
);
4610 nfs_node_unlock(dnp
);
4612 nfsm_chain_cleanup(&nmreq
);
4613 nfsm_chain_cleanup(&nmrep
);
4618 * NFS file rename call
4622 struct vnop_rename_args
/* {
4623 * struct vnodeop_desc *a_desc;
4626 * struct componentname *a_fcnp;
4629 * struct componentname *a_tcnp;
4630 * vfs_context_t a_context;
4633 vfs_context_t ctx
= ap
->a_context
;
4634 vnode_t fdvp
= ap
->a_fdvp
;
4635 vnode_t fvp
= ap
->a_fvp
;
4636 vnode_t tdvp
= ap
->a_tdvp
;
4637 vnode_t tvp
= ap
->a_tvp
;
4638 nfsnode_t fdnp
, fnp
, tdnp
, tnp
;
4639 struct componentname
*tcnp
= ap
->a_tcnp
;
4640 struct componentname
*fcnp
= ap
->a_fcnp
;
4641 int error
, nfsvers
, inuse
= 0, tvprecycle
= 0, locked
= 0;
4642 mount_t fmp
, tdmp
, tmp
;
4643 struct nfs_vattr nvattr
;
4644 struct nfsmount
*nmp
;
4646 fdnp
= VTONFS(fdvp
);
4648 tdnp
= VTONFS(tdvp
);
4649 tnp
= tvp
? VTONFS(tvp
) : NULL
;
4651 nmp
= NFSTONMP(fdnp
);
4652 if (nfs_mount_gone(nmp
)) {
4655 nfsvers
= nmp
->nm_vers
;
4657 error
= nfs_node_set_busy4(fdnp
, fnp
, tdnp
, tnp
, vfs_context_thread(ctx
));
4662 if (tvp
&& (tvp
!= fvp
)) {
4663 /* lock the node while we rename over the existing file */
4664 lck_mtx_lock(nfs_node_hash_mutex
);
4665 while (tnp
->n_hflag
& NHLOCKED
) {
4666 tnp
->n_hflag
|= NHLOCKWANT
;
4667 msleep(tnp
, nfs_node_hash_mutex
, PINOD
, "nfs_rename", NULL
);
4669 tnp
->n_hflag
|= NHLOCKED
;
4670 lck_mtx_unlock(nfs_node_hash_mutex
);
4674 /* Check for cross-device rename */
4675 fmp
= vnode_mount(fvp
);
4676 tmp
= tvp
? vnode_mount(tvp
) : NULL
;
4677 tdmp
= vnode_mount(tdvp
);
4678 if ((fmp
!= tdmp
) || (tvp
&& (fmp
!= tmp
))) {
4683 /* XXX prevent renaming from/over a sillyrenamed file? */
4686 * If the tvp exists and is in use, sillyrename it before doing the
4687 * rename of the new file over it.
4688 * XXX Can't sillyrename a directory.
4689 * Don't sillyrename if source and target are same vnode (hard
4690 * links or case-variants)
4692 if (tvp
&& (tvp
!= fvp
)) {
4693 inuse
= vnode_isinuse(tvp
, 0);
4695 if (inuse
&& !tnp
->n_sillyrename
&& (vnode_vtype(tvp
) != VDIR
)) {
4696 error
= nfs_sillyrename(tdnp
, tnp
, tcnp
, ctx
);
4698 /* sillyrename failed. Instead of pressing on, return error */
4699 goto out
; /* should not be ENOENT. */
4701 /* sillyrename succeeded.*/
4706 else if (tvp
&& (nmp
->nm_vers
>= NFS_VER4
) && (tnp
->n_openflags
& N_DELEG_MASK
)) {
4707 nfs4_delegation_return(tnp
, 0, vfs_context_thread(ctx
), vfs_context_ucred(ctx
));
4710 error
= nmp
->nm_funcs
->nf_rename_rpc(fdnp
, fcnp
->cn_nameptr
, fcnp
->cn_namelen
,
4711 tdnp
, tcnp
->cn_nameptr
, tcnp
->cn_namelen
, ctx
);
4714 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
4716 if (error
== ENOENT
) {
4720 if (tvp
&& (tvp
!= fvp
) && !tnp
->n_sillyrename
) {
4721 nfs_node_lock_force(tnp
);
4722 tvprecycle
= (!error
&& !vnode_isinuse(tvp
, 0) &&
4723 (nfs_getattrcache(tnp
, &nvattr
, 0) || (nvattr
.nva_nlink
== 1)));
4724 nfs_node_unlock(tnp
);
4725 lck_mtx_lock(nfs_node_hash_mutex
);
4726 if (tvprecycle
&& (tnp
->n_hflag
& NHHASHED
)) {
4728 * remove nfsnode from hash now so we can't accidentally find it
4729 * again if another object gets created with the same filehandle
4730 * before this vnode gets reclaimed
4732 LIST_REMOVE(tnp
, n_hash
);
4733 tnp
->n_hflag
&= ~NHHASHED
;
4734 FSDBG(266, 0, tnp
, tnp
->n_flag
, 0xb1eb1e);
4736 lck_mtx_unlock(nfs_node_hash_mutex
);
4739 /* purge the old name cache entries and enter the new one */
4740 nfs_name_cache_purge(fdnp
, fnp
, fcnp
, ctx
);
4742 nfs_name_cache_purge(tdnp
, tnp
, tcnp
, ctx
);
4744 /* clear flags now: won't get nfs_vnop_inactive for recycled vnode */
4745 /* clear all flags other than these */
4746 nfs_node_lock_force(tnp
);
4747 tnp
->n_flag
&= (NMODIFIED
);
4748 nfs_node_unlock(tnp
);
4753 nfs_node_lock_force(tdnp
);
4754 if (tdnp
->n_flag
& NNEGNCENTRIES
) {
4755 tdnp
->n_flag
&= ~NNEGNCENTRIES
;
4756 cache_purge_negatives(tdvp
);
4758 nfs_node_unlock(tdnp
);
4759 nfs_node_lock_force(fnp
);
4760 cache_enter(tdvp
, fvp
, tcnp
);
4761 if (tdvp
!= fdvp
) { /* update parent pointer */
4762 if (fnp
->n_parent
&& !vnode_get(fnp
->n_parent
)) {
4763 /* remove ref from old parent */
4764 vnode_rele(fnp
->n_parent
);
4765 vnode_put(fnp
->n_parent
);
4767 fnp
->n_parent
= tdvp
;
4768 if (tdvp
&& !vnode_get(tdvp
)) {
4769 /* add ref to new parent */
4773 fnp
->n_parent
= NULL
;
4776 nfs_node_unlock(fnp
);
4779 /* nfs_getattr() will check changed and purge caches */
4780 nfs_getattr(fdnp
, NULL
, ctx
, NGA_CACHED
);
4781 nfs_getattr(tdnp
, NULL
, ctx
, NGA_CACHED
);
4784 lck_mtx_lock(nfs_node_hash_mutex
);
4785 tnp
->n_hflag
&= ~NHLOCKED
;
4786 if (tnp
->n_hflag
& NHLOCKWANT
) {
4787 tnp
->n_hflag
&= ~NHLOCKWANT
;
4790 lck_mtx_unlock(nfs_node_hash_mutex
);
4792 nfs_node_clear_busy4(fdnp
, fnp
, tdnp
, tnp
);
4797 * Do an NFS rename rpc. Called from nfs_vnop_rename() and nfs_sillyrename().
4809 int error
= 0, lockerror
= ENOENT
, status
, fwccpostattr
= 0, twccpostattr
= 0;
4810 struct timespec fpremtime
= { .tv_sec
= 0, .tv_nsec
= 0 }, tpremtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
4811 struct nfsmount
*nmp
;
4813 u_int64_t xid
, txid
;
4814 struct nfsm_chain nmreq
, nmrep
;
4816 nmp
= NFSTONMP(fdnp
);
4817 if (nfs_mount_gone(nmp
)) {
4820 nfsvers
= nmp
->nm_vers
;
4821 if ((nfsvers
== NFS_VER2
) &&
4822 ((fnamelen
> NFS_MAXNAMLEN
) || (tnamelen
> NFS_MAXNAMLEN
))) {
4823 return ENAMETOOLONG
;
4826 nfsm_chain_null(&nmreq
);
4827 nfsm_chain_null(&nmrep
);
4829 nfsm_chain_build_alloc_init(error
, &nmreq
,
4830 (NFSX_FH(nfsvers
) + NFSX_UNSIGNED
) * 2 +
4831 nfsm_rndup(fnamelen
) + nfsm_rndup(tnamelen
));
4832 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, fdnp
->n_fhp
, fdnp
->n_fhsize
);
4833 nfsm_chain_add_name(error
, &nmreq
, fnameptr
, fnamelen
, nmp
);
4834 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, tdnp
->n_fhp
, tdnp
->n_fhsize
);
4835 nfsm_chain_add_name(error
, &nmreq
, tnameptr
, tnamelen
, nmp
);
4836 nfsm_chain_build_done(error
, &nmreq
);
4839 error
= nfs_request(fdnp
, NULL
, &nmreq
, NFSPROC_RENAME
, ctx
, NULL
, &nmrep
, &xid
, &status
);
4841 if ((lockerror
= nfs_node_lock2(fdnp
, tdnp
))) {
4844 if (nfsvers
== NFS_VER3
) {
4846 nfsm_chain_get_wcc_data(error
, &nmrep
, fdnp
, &fpremtime
, &fwccpostattr
, &xid
);
4847 nfsm_chain_get_wcc_data(error
, &nmrep
, tdnp
, &tpremtime
, &twccpostattr
, &txid
);
4853 nfsm_chain_cleanup(&nmreq
);
4854 nfsm_chain_cleanup(&nmrep
);
4856 fdnp
->n_flag
|= NMODIFIED
;
4857 /* if directory hadn't changed, update namecache mtime */
4858 if (nfstimespeccmp(&fdnp
->n_ncmtime
, &fpremtime
, ==)) {
4859 NFS_CHANGED_UPDATE_NC(nfsvers
, fdnp
, &fdnp
->n_vattr
);
4861 if (!fwccpostattr
) {
4862 NATTRINVALIDATE(fdnp
);
4864 tdnp
->n_flag
|= NMODIFIED
;
4865 /* if directory hadn't changed, update namecache mtime */
4866 if (nfstimespeccmp(&tdnp
->n_ncmtime
, &tpremtime
, ==)) {
4867 NFS_CHANGED_UPDATE_NC(nfsvers
, tdnp
, &tdnp
->n_vattr
);
4869 if (!twccpostattr
) {
4870 NATTRINVALIDATE(tdnp
);
4872 nfs_node_unlock2(fdnp
, tdnp
);
4878 * NFS hard link create call
4882 struct vnop_link_args
/* {
4883 * struct vnodeop_desc *a_desc;
4886 * struct componentname *a_cnp;
4887 * vfs_context_t a_context;
4890 vfs_context_t ctx
= ap
->a_context
;
4891 vnode_t vp
= ap
->a_vp
;
4892 vnode_t tdvp
= ap
->a_tdvp
;
4893 struct componentname
*cnp
= ap
->a_cnp
;
4894 int error
= 0, lockerror
= ENOENT
, status
, wccpostattr
= 0, attrflag
= 0;
4895 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
4896 struct nfsmount
*nmp
;
4897 nfsnode_t np
= VTONFS(vp
);
4898 nfsnode_t tdnp
= VTONFS(tdvp
);
4900 u_int64_t xid
, txid
;
4901 struct nfsm_chain nmreq
, nmrep
;
4903 if (vnode_mount(vp
) != vnode_mount(tdvp
)) {
4908 if (nfs_mount_gone(nmp
)) {
4911 nfsvers
= nmp
->nm_vers
;
4912 if ((nfsvers
== NFS_VER2
) && (cnp
->cn_namelen
> NFS_MAXNAMLEN
)) {
4913 return ENAMETOOLONG
;
4917 * Push all writes to the server, so that the attribute cache
4918 * doesn't get "out of sync" with the server.
4919 * XXX There should be a better way!
4921 nfs_flush(np
, MNT_WAIT
, vfs_context_thread(ctx
), V_IGNORE_WRITEERR
);
4923 error
= nfs_node_set_busy2(tdnp
, np
, vfs_context_thread(ctx
));
4928 nfsm_chain_null(&nmreq
);
4929 nfsm_chain_null(&nmrep
);
4931 nfsm_chain_build_alloc_init(error
, &nmreq
,
4932 NFSX_FH(nfsvers
) * 2 + NFSX_UNSIGNED
+ nfsm_rndup(cnp
->cn_namelen
));
4933 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
4934 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, tdnp
->n_fhp
, tdnp
->n_fhsize
);
4935 nfsm_chain_add_name(error
, &nmreq
, cnp
->cn_nameptr
, cnp
->cn_namelen
, nmp
);
4936 nfsm_chain_build_done(error
, &nmreq
);
4938 error
= nfs_request(np
, NULL
, &nmreq
, NFSPROC_LINK
, ctx
, NULL
, &nmrep
, &xid
, &status
);
4940 if ((lockerror
= nfs_node_lock2(tdnp
, np
))) {
4944 if (nfsvers
== NFS_VER3
) {
4946 nfsm_chain_postop_attr_update_flag(error
, &nmrep
, np
, attrflag
, &xid
);
4947 nfsm_chain_get_wcc_data(error
, &nmrep
, tdnp
, &premtime
, &wccpostattr
, &txid
);
4953 nfsm_chain_cleanup(&nmreq
);
4954 nfsm_chain_cleanup(&nmrep
);
4957 NATTRINVALIDATE(np
);
4959 tdnp
->n_flag
|= NMODIFIED
;
4960 /* if directory hadn't changed, update namecache mtime */
4961 if (nfstimespeccmp(&tdnp
->n_ncmtime
, &premtime
, ==)) {
4962 NFS_CHANGED_UPDATE_NC(nfsvers
, tdnp
, &tdnp
->n_vattr
);
4965 NATTRINVALIDATE(tdnp
);
4967 if (!error
&& (tdnp
->n_flag
& NNEGNCENTRIES
)) {
4968 tdnp
->n_flag
&= ~NNEGNCENTRIES
;
4969 cache_purge_negatives(tdvp
);
4971 nfs_node_unlock2(tdnp
, np
);
4973 nfs_node_clear_busy2(tdnp
, np
);
4975 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
4977 if (error
== EEXIST
) {
4984 * NFS symbolic link create call
4988 struct vnop_symlink_args
/* {
4989 * struct vnodeop_desc *a_desc;
4992 * struct componentname *a_cnp;
4993 * struct vnode_attr *a_vap;
4995 * vfs_context_t a_context;
4998 vfs_context_t ctx
= ap
->a_context
;
4999 vnode_t dvp
= ap
->a_dvp
;
5000 struct vnode_attr
*vap
= ap
->a_vap
;
5001 struct componentname
*cnp
= ap
->a_cnp
;
5002 struct nfs_vattr nvattr
;
5004 int slen
, error
= 0, lockerror
= ENOENT
, busyerror
= ENOENT
, status
, wccpostattr
= 0;
5005 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
5006 vnode_t newvp
= NULL
;
5007 int nfsvers
, gotuid
, gotgid
;
5008 u_int64_t xid
= 0, dxid
;
5009 nfsnode_t np
= NULL
;
5010 nfsnode_t dnp
= VTONFS(dvp
);
5011 struct nfsmount
*nmp
;
5012 struct nfsm_chain nmreq
, nmrep
;
5013 struct nfsreq rq
, *req
= &rq
;
5014 struct nfs_dulookup dul
;
5016 int dul_in_progress
= 0;
5019 if (nfs_mount_gone(nmp
)) {
5022 nfsvers
= nmp
->nm_vers
;
5023 namedattrs
= (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
);
5025 slen
= strlen(ap
->a_target
);
5026 if ((nfsvers
== NFS_VER2
) &&
5027 ((cnp
->cn_namelen
> NFS_MAXNAMLEN
) || (slen
> NFS_MAXPATHLEN
))) {
5028 return ENAMETOOLONG
;
5031 nfs_avoid_needless_id_setting_on_create(dnp
, vap
, ctx
);
5033 VATTR_SET_SUPPORTED(vap
, va_mode
);
5034 VATTR_SET_SUPPORTED(vap
, va_uid
);
5035 VATTR_SET_SUPPORTED(vap
, va_gid
);
5036 VATTR_SET_SUPPORTED(vap
, va_data_size
);
5037 VATTR_SET_SUPPORTED(vap
, va_access_time
);
5038 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
5039 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
5040 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
5042 error
= busyerror
= nfs_node_set_busy(dnp
, vfs_context_thread(ctx
));
5044 nfs_dulookup_init(&dul
, dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
);
5047 nfsm_chain_null(&nmreq
);
5048 nfsm_chain_null(&nmrep
);
5050 nfsm_chain_build_alloc_init(error
, &nmreq
,
5051 NFSX_FH(nfsvers
) + 2 * NFSX_UNSIGNED
+
5052 nfsm_rndup(cnp
->cn_namelen
) + nfsm_rndup(slen
) + NFSX_SATTR(nfsvers
));
5053 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
5054 nfsm_chain_add_name(error
, &nmreq
, cnp
->cn_nameptr
, cnp
->cn_namelen
, nmp
);
5055 if (nfsvers
== NFS_VER3
) {
5056 nfsm_chain_add_v3sattr(nmp
, error
, &nmreq
, vap
);
5058 nfsm_chain_add_name(error
, &nmreq
, ap
->a_target
, slen
, nmp
);
5059 if (nfsvers
== NFS_VER2
) {
5060 nfsm_chain_add_v2sattr(error
, &nmreq
, vap
, -1);
5062 nfsm_chain_build_done(error
, &nmreq
);
5065 error
= nfs_request_async(dnp
, NULL
, &nmreq
, NFSPROC_SYMLINK
,
5066 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, 0, NULL
, &req
);
5069 nfs_dulookup_start(&dul
, dnp
, ctx
);
5070 dul_in_progress
= 1;
5072 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
5075 if ((lockerror
= nfs_node_lock(dnp
))) {
5079 if (!error
&& !status
) {
5080 if (dnp
->n_flag
& NNEGNCENTRIES
) {
5081 dnp
->n_flag
&= ~NNEGNCENTRIES
;
5082 cache_purge_negatives(dvp
);
5084 if (nfsvers
== NFS_VER3
) {
5085 error
= nfsm_chain_get_fh_attr(nmp
, &nmrep
, dnp
, ctx
, nfsvers
, &xid
, &fh
, &nvattr
);
5090 if (nfsvers
== NFS_VER3
) {
5091 nfsm_chain_get_wcc_data(error
, &nmrep
, dnp
, &premtime
, &wccpostattr
, &dxid
);
5097 nfsm_chain_cleanup(&nmreq
);
5098 nfsm_chain_cleanup(&nmrep
);
5101 dnp
->n_flag
|= NMODIFIED
;
5102 /* if directory hadn't changed, update namecache mtime */
5103 if (nfstimespeccmp(&dnp
->n_ncmtime
, &premtime
, ==)) {
5104 NFS_CHANGED_UPDATE_NC(nfsvers
, dnp
, &dnp
->n_vattr
);
5106 nfs_node_unlock(dnp
);
5107 /* nfs_getattr() will check changed and purge caches */
5108 nfs_getattr(dnp
, NULL
, ctx
, wccpostattr
? NGA_CACHED
: NGA_UNCACHED
);
5111 if (!error
&& fh
.fh_len
) {
5112 error
= nfs_nget(NFSTOMP(dnp
), dnp
, cnp
, fh
.fh_data
, fh
.fh_len
, &nvattr
, &xid
, rq
.r_auth
, NG_MAKEENTRY
, &np
);
5118 if (dul_in_progress
) {
5119 nfs_dulookup_finish(&dul
, dnp
, ctx
);
5123 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
5124 * if we can succeed in looking up the symlink.
5126 if ((error
== EEXIST
) || (!error
&& !newvp
)) {
5128 nfs_node_unlock(np
);
5132 error
= nfs_lookitup(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
, &np
);
5135 if (vnode_vtype(newvp
) != VLNK
) {
5141 nfs_node_clear_busy(dnp
);
5143 if (!error
&& (gotuid
|| gotgid
) &&
5144 (!newvp
|| nfs_getattrcache(np
, &nvattr
, 0) ||
5145 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
5146 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
5147 /* clear ID bits if server didn't use them (or we can't tell) */
5148 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
5149 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
5153 nfs_node_unlock(np
);
5157 nfs_node_unlock(np
);
5168 struct vnop_mkdir_args
/* {
5169 * struct vnodeop_desc *a_desc;
5172 * struct componentname *a_cnp;
5173 * struct vnode_attr *a_vap;
5174 * vfs_context_t a_context;
5177 vfs_context_t ctx
= ap
->a_context
;
5178 vnode_t dvp
= ap
->a_dvp
;
5179 struct vnode_attr
*vap
= ap
->a_vap
;
5180 struct componentname
*cnp
= ap
->a_cnp
;
5181 struct nfs_vattr nvattr
;
5182 nfsnode_t np
= NULL
;
5183 struct nfsmount
*nmp
;
5184 nfsnode_t dnp
= VTONFS(dvp
);
5185 vnode_t newvp
= NULL
;
5186 int error
= 0, lockerror
= ENOENT
, busyerror
= ENOENT
, status
, wccpostattr
= 0;
5187 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
5188 int nfsvers
, gotuid
, gotgid
;
5189 u_int64_t xid
= 0, dxid
;
5191 struct nfsm_chain nmreq
, nmrep
;
5192 struct nfsreq rq
, *req
= &rq
;
5193 struct nfs_dulookup dul
;
5195 int dul_in_progress
= 0;
5198 if (nfs_mount_gone(nmp
)) {
5201 nfsvers
= nmp
->nm_vers
;
5202 namedattrs
= (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
);
5204 if ((nfsvers
== NFS_VER2
) && (cnp
->cn_namelen
> NFS_MAXNAMLEN
)) {
5205 return ENAMETOOLONG
;
5208 nfs_avoid_needless_id_setting_on_create(dnp
, vap
, ctx
);
5210 VATTR_SET_SUPPORTED(vap
, va_mode
);
5211 VATTR_SET_SUPPORTED(vap
, va_uid
);
5212 VATTR_SET_SUPPORTED(vap
, va_gid
);
5213 VATTR_SET_SUPPORTED(vap
, va_data_size
);
5214 VATTR_SET_SUPPORTED(vap
, va_access_time
);
5215 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
5216 gotuid
= VATTR_IS_ACTIVE(vap
, va_uid
);
5217 gotgid
= VATTR_IS_ACTIVE(vap
, va_gid
);
5219 error
= busyerror
= nfs_node_set_busy(dnp
, vfs_context_thread(ctx
));
5221 nfs_dulookup_init(&dul
, dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
);
5224 nfsm_chain_null(&nmreq
);
5225 nfsm_chain_null(&nmrep
);
5227 nfsm_chain_build_alloc_init(error
, &nmreq
,
5228 NFSX_FH(nfsvers
) + NFSX_UNSIGNED
+
5229 nfsm_rndup(cnp
->cn_namelen
) + NFSX_SATTR(nfsvers
));
5230 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
5231 nfsm_chain_add_name(error
, &nmreq
, cnp
->cn_nameptr
, cnp
->cn_namelen
, nmp
);
5232 if (nfsvers
== NFS_VER3
) {
5233 nfsm_chain_add_v3sattr(nmp
, error
, &nmreq
, vap
);
5235 nfsm_chain_add_v2sattr(error
, &nmreq
, vap
, -1);
5237 nfsm_chain_build_done(error
, &nmreq
);
5240 error
= nfs_request_async(dnp
, NULL
, &nmreq
, NFSPROC_MKDIR
,
5241 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, 0, NULL
, &req
);
5244 nfs_dulookup_start(&dul
, dnp
, ctx
);
5245 dul_in_progress
= 1;
5247 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
5250 if ((lockerror
= nfs_node_lock(dnp
))) {
5254 if (!error
&& !status
) {
5255 if (dnp
->n_flag
& NNEGNCENTRIES
) {
5256 dnp
->n_flag
&= ~NNEGNCENTRIES
;
5257 cache_purge_negatives(dvp
);
5259 error
= nfsm_chain_get_fh_attr(nmp
, &nmrep
, dnp
, ctx
, nfsvers
, &xid
, &fh
, &nvattr
);
5261 if (nfsvers
== NFS_VER3
) {
5262 nfsm_chain_get_wcc_data(error
, &nmrep
, dnp
, &premtime
, &wccpostattr
, &dxid
);
5268 nfsm_chain_cleanup(&nmreq
);
5269 nfsm_chain_cleanup(&nmrep
);
5272 dnp
->n_flag
|= NMODIFIED
;
5273 /* if directory hadn't changed, update namecache mtime */
5274 if (nfstimespeccmp(&dnp
->n_ncmtime
, &premtime
, ==)) {
5275 NFS_CHANGED_UPDATE_NC(nfsvers
, dnp
, &dnp
->n_vattr
);
5277 nfs_node_unlock(dnp
);
5278 /* nfs_getattr() will check changed and purge caches */
5279 nfs_getattr(dnp
, NULL
, ctx
, wccpostattr
? NGA_CACHED
: NGA_UNCACHED
);
5282 if (!error
&& fh
.fh_len
) {
5283 error
= nfs_nget(NFSTOMP(dnp
), dnp
, cnp
, fh
.fh_data
, fh
.fh_len
, &nvattr
, &xid
, rq
.r_auth
, NG_MAKEENTRY
, &np
);
5289 if (dul_in_progress
) {
5290 nfs_dulookup_finish(&dul
, dnp
, ctx
);
5294 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
5295 * if we can succeed in looking up the directory.
5297 if ((error
== EEXIST
) || (!error
&& !newvp
)) {
5299 nfs_node_unlock(np
);
5303 error
= nfs_lookitup(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
, &np
);
5306 if (vnode_vtype(newvp
) != VDIR
) {
5312 nfs_node_clear_busy(dnp
);
5314 if (!error
&& (gotuid
|| gotgid
) &&
5315 (!newvp
|| nfs_getattrcache(np
, &nvattr
, 0) ||
5316 (gotuid
&& (nvattr
.nva_uid
!= vap
->va_uid
)) ||
5317 (gotgid
&& (nvattr
.nva_gid
!= vap
->va_gid
)))) {
5318 /* clear ID bits if server didn't use them (or we can't tell) */
5319 VATTR_CLEAR_SUPPORTED(vap
, va_uid
);
5320 VATTR_CLEAR_SUPPORTED(vap
, va_gid
);
5324 nfs_node_unlock(np
);
5328 nfs_node_unlock(np
);
5335 * NFS remove directory call
5339 struct vnop_rmdir_args
/* {
5340 * struct vnodeop_desc *a_desc;
5343 * struct componentname *a_cnp;
5344 * vfs_context_t a_context;
5347 vfs_context_t ctx
= ap
->a_context
;
5348 vnode_t vp
= ap
->a_vp
;
5349 vnode_t dvp
= ap
->a_dvp
;
5350 struct componentname
*cnp
= ap
->a_cnp
;
5351 int error
= 0, lockerror
= ENOENT
, status
, wccpostattr
= 0;
5352 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
5353 struct nfsmount
*nmp
;
5354 nfsnode_t np
= VTONFS(vp
);
5355 nfsnode_t dnp
= VTONFS(dvp
);
5358 struct nfsm_chain nmreq
, nmrep
;
5359 struct nfsreq rq
, *req
= &rq
;
5360 struct nfs_dulookup dul
;
5362 int dul_in_progress
= 0;
5365 if (nfs_mount_gone(nmp
)) {
5368 nfsvers
= nmp
->nm_vers
;
5369 namedattrs
= (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
);
5371 if ((nfsvers
== NFS_VER2
) && (cnp
->cn_namelen
> NFS_MAXNAMLEN
)) {
5372 return ENAMETOOLONG
;
5375 if ((error
= nfs_node_set_busy2(dnp
, np
, vfs_context_thread(ctx
)))) {
5380 nfs_dulookup_init(&dul
, dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
, ctx
);
5383 nfsm_chain_null(&nmreq
);
5384 nfsm_chain_null(&nmrep
);
5386 nfsm_chain_build_alloc_init(error
, &nmreq
,
5387 NFSX_FH(nfsvers
) + NFSX_UNSIGNED
+ nfsm_rndup(cnp
->cn_namelen
));
5388 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
5389 nfsm_chain_add_name(error
, &nmreq
, cnp
->cn_nameptr
, cnp
->cn_namelen
, nmp
);
5390 nfsm_chain_build_done(error
, &nmreq
);
5393 error
= nfs_request_async(dnp
, NULL
, &nmreq
, NFSPROC_RMDIR
,
5394 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, 0, NULL
, &req
);
5397 nfs_dulookup_start(&dul
, dnp
, ctx
);
5398 dul_in_progress
= 1;
5400 error
= nfs_request_async_finish(req
, &nmrep
, &xid
, &status
);
5403 if ((lockerror
= nfs_node_lock(dnp
))) {
5406 if (nfsvers
== NFS_VER3
) {
5407 nfsm_chain_get_wcc_data(error
, &nmrep
, dnp
, &premtime
, &wccpostattr
, &xid
);
5413 nfsm_chain_cleanup(&nmreq
);
5414 nfsm_chain_cleanup(&nmrep
);
5417 dnp
->n_flag
|= NMODIFIED
;
5418 /* if directory hadn't changed, update namecache mtime */
5419 if (nfstimespeccmp(&dnp
->n_ncmtime
, &premtime
, ==)) {
5420 NFS_CHANGED_UPDATE_NC(nfsvers
, dnp
, &dnp
->n_vattr
);
5422 nfs_node_unlock(dnp
);
5423 nfs_name_cache_purge(dnp
, np
, cnp
, ctx
);
5424 /* nfs_getattr() will check changed and purge caches */
5425 nfs_getattr(dnp
, NULL
, ctx
, wccpostattr
? NGA_CACHED
: NGA_UNCACHED
);
5427 if (dul_in_progress
) {
5428 nfs_dulookup_finish(&dul
, dnp
, ctx
);
5430 nfs_node_clear_busy2(dnp
, np
);
5433 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
5435 if (error
== ENOENT
) {
5440 * remove nfsnode from hash now so we can't accidentally find it
5441 * again if another object gets created with the same filehandle
5442 * before this vnode gets reclaimed
5444 lck_mtx_lock(nfs_node_hash_mutex
);
5445 if (np
->n_hflag
& NHHASHED
) {
5446 LIST_REMOVE(np
, n_hash
);
5447 np
->n_hflag
&= ~NHHASHED
;
5448 FSDBG(266, 0, np
, np
->n_flag
, 0xb1eb1e);
5450 lck_mtx_unlock(nfs_node_hash_mutex
);
5458 * The incoming "offset" is a directory cookie indicating where in the
5459 * directory entries should be read from. A zero cookie means start at
5460 * the beginning of the directory. Any other cookie will be a cookie
5461 * returned from the server.
5463 * Using that cookie, determine which buffer (and where in that buffer)
5464 * to start returning entries from. Buffer logical block numbers are
5465 * the cookies they start at. If a buffer is found that is not full,
5466 * call into the bio/RPC code to fill it. The RPC code will probably
5467 * fill several buffers (dropping the first, requiring a re-get).
5469 * When done copying entries to the buffer, set the offset to the current
5470 * entry's cookie and enter that cookie in the cookie cache.
5472 * Note: because the getdirentries(2) API returns a long-typed offset,
5473 * the incoming offset is a potentially truncated cookie (ptc).
5474 * The cookie matching code is aware of this and will fall back to
5475 * matching only 32 bits of the cookie.
5479 struct vnop_readdir_args
/* {
5480 * struct vnodeop_desc *a_desc;
5482 * struct uio *a_uio;
5486 * vfs_context_t a_context;
5489 vfs_context_t ctx
= ap
->a_context
;
5490 vnode_t dvp
= ap
->a_vp
;
5491 nfsnode_t dnp
= VTONFS(dvp
);
5492 struct nfsmount
*nmp
;
5493 uio_t uio
= ap
->a_uio
;
5494 int error
, nfsvers
, extended
, numdirent
, bigcookies
, ptc
, done
, attrcachetimeout
;
5495 uint16_t i
, iptc
, rlen
, nlen
;
5496 uint64_t cookie
, nextcookie
, lbn
= 0;
5497 struct nfsbuf
*bp
= NULL
;
5498 struct nfs_dir_buf_header
*ndbhp
;
5499 struct direntry
*dp
, *dpptc
;
5506 if (nfs_mount_gone(nmp
)) {
5509 nfsvers
= nmp
->nm_vers
;
5510 bigcookies
= (nmp
->nm_state
& NFSSTA_BIGCOOKIES
);
5511 extended
= (ap
->a_flags
& VNODE_READDIR_EXTENDED
);
5513 if (vnode_vtype(dvp
) != VDIR
) {
5517 if (ap
->a_eofflag
) {
5521 if (uio_resid(uio
) == 0) {
5525 if ((nfsvers
>= NFS_VER4
) && (dnp
->n_vattr
.nva_flags
& NFS_FFLAG_TRIGGER
)) {
5526 /* trigger directories should never be read, return nothing */
5530 thd
= vfs_context_thread(ctx
);
5531 numdirent
= done
= 0;
5532 nextcookie
= uio_offset(uio
);
5533 ptc
= bigcookies
&& NFS_DIR_COOKIE_POTENTIALLY_TRUNCATED(nextcookie
);
5535 if ((error
= nfs_node_lock(dnp
))) {
5539 if (dnp
->n_flag
& NNEEDINVALIDATE
) {
5540 dnp
->n_flag
&= ~NNEEDINVALIDATE
;
5542 nfs_node_unlock(dnp
);
5543 error
= nfs_vinvalbuf(dvp
, 0, ctx
, 1);
5545 error
= nfs_node_lock(dnp
);
5552 if (dnp
->n_rdirplusstamp_eof
&& dnp
->n_rdirplusstamp_sof
) {
5553 attrcachetimeout
= nfs_attrcachetimeout(dnp
);
5555 if (attrcachetimeout
&& (now
.tv_sec
- dnp
->n_rdirplusstamp_sof
> attrcachetimeout
- 1)) {
5556 dnp
->n_rdirplusstamp_eof
= dnp
->n_rdirplusstamp_sof
= 0;
5558 nfs_node_unlock(dnp
);
5559 error
= nfs_vinvalbuf(dvp
, 0, ctx
, 1);
5561 error
= nfs_node_lock(dnp
);
5570 * check for need to invalidate when (re)starting at beginning
5573 if (dnp
->n_flag
& NMODIFIED
) {
5575 nfs_node_unlock(dnp
);
5576 if ((error
= nfs_vinvalbuf(dvp
, 0, ctx
, 1))) {
5580 nfs_node_unlock(dnp
);
5582 /* nfs_getattr() will check changed and purge caches */
5583 if ((error
= nfs_getattr(dnp
, NULL
, ctx
, NGA_UNCACHED
))) {
5587 nfs_node_unlock(dnp
);
5590 error
= nfs_dir_cookie_to_lbn(dnp
, nextcookie
, &ptc
, &lbn
);
5592 if (error
< 0) { /* just hit EOF cookie */
5596 if (ap
->a_eofflag
) {
5601 while (!error
&& !done
) {
5602 OSAddAtomic64(1, &nfsstats
.biocache_readdirs
);
5603 cookie
= nextcookie
;
5605 error
= nfs_buf_get(dnp
, lbn
, NFS_DIRBLKSIZ
, thd
, NBLK_READ
, &bp
);
5609 ndbhp
= (struct nfs_dir_buf_header
*)bp
->nb_data
;
5610 if (!ISSET(bp
->nb_flags
, NB_CACHE
) || !ISSET(ndbhp
->ndbh_flags
, NDB_FULL
)) {
5611 if (!ISSET(bp
->nb_flags
, NB_CACHE
)) { /* initialize the buffer */
5612 ndbhp
->ndbh_flags
= 0;
5613 ndbhp
->ndbh_count
= 0;
5614 ndbhp
->ndbh_entry_end
= sizeof(*ndbhp
);
5615 ndbhp
->ndbh_ncgen
= dnp
->n_ncgen
;
5617 error
= nfs_buf_readdir(bp
, ctx
);
5618 if (error
== NFSERR_DIRBUFDROPPED
) {
5622 nfs_buf_release(bp
, 1);
5624 if (error
&& (error
!= ENXIO
) && (error
!= ETIMEDOUT
) && (error
!= EINTR
) && (error
!= ERESTART
)) {
5625 if (!nfs_node_lock(dnp
)) {
5627 nfs_node_unlock(dnp
);
5629 nfs_vinvalbuf(dvp
, 0, ctx
, 1);
5630 if (error
== NFSERR_BAD_COOKIE
) {
5639 /* find next entry to return */
5640 dp
= NFS_DIR_BUF_FIRST_DIRENTRY(bp
);
5642 if ((lbn
!= cookie
) && !(ptc
&& NFS_DIR_COOKIE_SAME32(lbn
, cookie
))) {
5645 for (; (i
< ndbhp
->ndbh_count
) && (cookie
!= dp
->d_seekoff
); i
++) {
5646 if (ptc
&& !dpptc
&& NFS_DIR_COOKIE_SAME32(cookie
, dp
->d_seekoff
)) {
5650 nextcookie
= dp
->d_seekoff
;
5651 dp
= NFS_DIRENTRY_NEXT(dp
);
5653 if ((i
== ndbhp
->ndbh_count
) && dpptc
) {
5657 if (i
< ndbhp
->ndbh_count
) {
5658 nextcookie
= dp
->d_seekoff
;
5659 dp
= NFS_DIRENTRY_NEXT(dp
);
5663 ptc
= 0; /* only have to deal with ptc on first cookie */
5665 /* return as many entries as we can */
5666 for (; i
< ndbhp
->ndbh_count
; i
++) {
5668 rlen
= dp
->d_reclen
;
5673 bzero(cp
, sizeof(dent
));
5675 if (dp
->d_namlen
> (sizeof(dent
.d_name
) - 1)) {
5676 nlen
= sizeof(dent
.d_name
) - 1;
5678 nlen
= dp
->d_namlen
;
5680 rlen
= NFS_DIRENT_LEN(nlen
);
5681 dent
.d_reclen
= rlen
;
5682 dent
.d_ino
= dp
->d_ino
;
5683 dent
.d_type
= dp
->d_type
;
5684 dent
.d_namlen
= nlen
;
5685 strlcpy(dent
.d_name
, dp
->d_name
, nlen
+ 1);
5687 /* check that the record fits */
5688 if (rlen
> uio_resid(uio
)) {
5692 if ((error
= uiomove(cp
, rlen
, uio
))) {
5696 nextcookie
= dp
->d_seekoff
;
5697 dp
= NFS_DIRENTRY_NEXT(dp
);
5700 if (i
== ndbhp
->ndbh_count
) {
5701 /* hit end of buffer, move to next buffer */
5703 /* if we also hit EOF, we're done */
5704 if (ISSET(ndbhp
->ndbh_flags
, NDB_EOF
)) {
5706 if (ap
->a_eofflag
) {
5712 uio_setoffset(uio
, nextcookie
);
5714 if (!error
&& !done
&& (nextcookie
== cookie
)) {
5715 printf("nfs readdir cookie didn't change 0x%llx, %d/%d\n", cookie
, i
, ndbhp
->ndbh_count
);
5718 nfs_buf_release(bp
, 1);
5722 nfs_dir_cookie_cache(dnp
, nextcookie
, lbn
);
5725 if (ap
->a_numdirent
) {
5726 *ap
->a_numdirent
= numdirent
;
5734 * Invalidate cached directory information, except for the actual directory
5735 * blocks (which are invalidated separately).
5738 nfs_invaldir(nfsnode_t dnp
)
5740 if (vnode_vtype(NFSTOV(dnp
)) != VDIR
) {
5743 dnp
->n_eofcookie
= 0;
5744 dnp
->n_cookieverf
= 0;
5745 if (!dnp
->n_cookiecache
) {
5748 dnp
->n_cookiecache
->free
= 0;
5749 dnp
->n_cookiecache
->mru
= -1;
5750 memset(dnp
->n_cookiecache
->next
, -1, NFSNUMCOOKIES
);
5754 * calculate how much space is available for additional directory entries.
5757 nfs_dir_buf_freespace(struct nfsbuf
*bp
, int rdirplus
)
5759 struct nfs_dir_buf_header
*ndbhp
= (struct nfs_dir_buf_header
*)bp
->nb_data
;
5765 space
= bp
->nb_bufsize
- ndbhp
->ndbh_entry_end
;
5767 space
-= ndbhp
->ndbh_count
* sizeof(struct nfs_vattr
);
5773 * add/update a cookie->lbn entry in the directory cookie cache
5776 nfs_dir_cookie_cache(nfsnode_t dnp
, uint64_t cookie
, uint64_t lbn
)
5778 struct nfsdmap
*ndcc
;
5785 if (nfs_node_lock(dnp
)) {
5789 if (cookie
== dnp
->n_eofcookie
) { /* EOF cookie */
5790 nfs_node_unlock(dnp
);
5794 ndcc
= dnp
->n_cookiecache
;
5796 /* allocate the cookie cache structure */
5797 MALLOC_ZONE(dnp
->n_cookiecache
, struct nfsdmap
*,
5798 sizeof(struct nfsdmap
), M_NFSDIROFF
, M_WAITOK
);
5799 if (!dnp
->n_cookiecache
) {
5800 nfs_node_unlock(dnp
);
5803 ndcc
= dnp
->n_cookiecache
;
5806 memset(ndcc
->next
, -1, NFSNUMCOOKIES
);
5810 * Search the list for this cookie.
5811 * Keep track of previous and last entries.
5815 while ((i
!= -1) && (cookie
!= ndcc
->cookies
[i
].key
)) {
5816 if (ndcc
->next
[i
] == -1) { /* stop on last entry so we can reuse */
5822 if ((i
!= -1) && (cookie
== ndcc
->cookies
[i
].key
)) {
5823 /* found it, remove from list */
5825 ndcc
->next
[prev
] = ndcc
->next
[i
];
5827 ndcc
->mru
= ndcc
->next
[i
];
5830 /* not found, use next free entry or reuse last entry */
5831 if (ndcc
->free
!= NFSNUMCOOKIES
) {
5834 ndcc
->next
[prev
] = -1;
5836 ndcc
->cookies
[i
].key
= cookie
;
5837 ndcc
->cookies
[i
].lbn
= lbn
;
5839 /* insert cookie at head of MRU list */
5840 ndcc
->next
[i
] = ndcc
->mru
;
5842 nfs_node_unlock(dnp
);
5846 * Try to map the given directory cookie to a directory buffer (return lbn).
5847 * If we have a possibly truncated cookie (ptc), check for 32-bit matches too.
5850 nfs_dir_cookie_to_lbn(nfsnode_t dnp
, uint64_t cookie
, int *ptc
, uint64_t *lbnp
)
5852 struct nfsdmap
*ndcc
= dnp
->n_cookiecache
;
5853 int8_t eofptc
, found
;
5855 struct nfsmount
*nmp
;
5856 struct nfsbuf
*bp
, *lastbp
;
5857 struct nfsbuflists blist
;
5858 struct direntry
*dp
, *dpptc
;
5859 struct nfs_dir_buf_header
*ndbhp
;
5861 if (!cookie
) { /* initial cookie */
5867 if (nfs_node_lock(dnp
)) {
5871 if (cookie
== dnp
->n_eofcookie
) { /* EOF cookie */
5872 nfs_node_unlock(dnp
);
5873 OSAddAtomic64(1, &nfsstats
.direofcache_hits
);
5877 /* note if cookie is a 32-bit match with the EOF cookie */
5878 eofptc
= *ptc
? NFS_DIR_COOKIE_SAME32(cookie
, dnp
->n_eofcookie
) : 0;
5881 /* search the list for the cookie */
5882 for (i
= ndcc
? ndcc
->mru
: -1; i
>= 0; i
= ndcc
->next
[i
]) {
5883 if (ndcc
->cookies
[i
].key
== cookie
) {
5884 /* found a match for this cookie */
5885 *lbnp
= ndcc
->cookies
[i
].lbn
;
5886 nfs_node_unlock(dnp
);
5887 OSAddAtomic64(1, &nfsstats
.direofcache_hits
);
5891 /* check for 32-bit match */
5892 if (*ptc
&& (iptc
== -1) && NFS_DIR_COOKIE_SAME32(ndcc
->cookies
[i
].key
, cookie
)) {
5896 /* exact match not found */
5898 /* but 32-bit match hit the EOF cookie */
5899 nfs_node_unlock(dnp
);
5900 OSAddAtomic64(1, &nfsstats
.direofcache_hits
);
5904 /* but 32-bit match got a hit */
5905 *lbnp
= ndcc
->cookies
[iptc
].lbn
;
5906 nfs_node_unlock(dnp
);
5907 OSAddAtomic64(1, &nfsstats
.direofcache_hits
);
5910 nfs_node_unlock(dnp
);
5913 * No match found in the cookie cache... hmm...
5914 * Let's search the directory's buffers for the cookie.
5916 nmp
= NFSTONMP(dnp
);
5917 if (nfs_mount_gone(nmp
)) {
5923 lck_mtx_lock(nfs_buf_mutex
);
5925 * Scan the list of buffers, keeping them in order.
5926 * Note that itercomplete inserts each of the remaining buffers
5927 * into the head of list (thus reversing the elements). So, we
5928 * make sure to iterate through all buffers, inserting them after
5929 * each other, to keep them in order.
5930 * Also note: the LIST_INSERT_AFTER(lastbp) is only safe because
5931 * we don't drop nfs_buf_mutex.
5933 if (!nfs_buf_iterprepare(dnp
, &blist
, NBI_CLEAN
)) {
5935 while ((bp
= LIST_FIRST(&blist
))) {
5936 LIST_REMOVE(bp
, nb_vnbufs
);
5938 LIST_INSERT_HEAD(&dnp
->n_cleanblkhd
, bp
, nb_vnbufs
);
5940 LIST_INSERT_AFTER(lastbp
, bp
, nb_vnbufs
);
5947 if (nfs_buf_acquire(bp
, NBAC_NOWAIT
, 0, 0)) {
5948 /* just skip this buffer */
5949 nfs_buf_refrele(bp
);
5952 nfs_buf_refrele(bp
);
5954 /* scan the buffer for the cookie */
5955 ndbhp
= (struct nfs_dir_buf_header
*)bp
->nb_data
;
5956 dp
= NFS_DIR_BUF_FIRST_DIRENTRY(bp
);
5958 for (i
= 0; (i
< ndbhp
->ndbh_count
) && (cookie
!= dp
->d_seekoff
); i
++) {
5959 if (*ptc
&& !dpptc
&& NFS_DIR_COOKIE_SAME32(cookie
, dp
->d_seekoff
)) {
5963 dp
= NFS_DIRENTRY_NEXT(dp
);
5965 if ((i
== ndbhp
->ndbh_count
) && dpptc
) {
5966 /* found only a PTC match */
5969 } else if (i
< ndbhp
->ndbh_count
) {
5972 if (i
< (ndbhp
->ndbh_count
- 1)) {
5973 /* next entry is *in* this buffer: return this block */
5974 *lbnp
= bp
->nb_lblkno
;
5976 } else if (i
== (ndbhp
->ndbh_count
- 1)) {
5977 /* next entry refers to *next* buffer: return next block */
5978 *lbnp
= dp
->d_seekoff
;
5983 nfs_buf_itercomplete(dnp
, &blist
, NBI_CLEAN
);
5985 lck_mtx_unlock(nfs_buf_mutex
);
5987 OSAddAtomic64(1, &nfsstats
.direofcache_hits
);
5991 /* still not found... oh well, just start a new block */
5993 OSAddAtomic64(1, &nfsstats
.direofcache_misses
);
5998 * scan a directory buffer for the given name
5999 * Returns: ESRCH if not found, ENOENT if found invalid, 0 if found
6000 * Note: should only be called with RDIRPLUS directory buffers
6003 #define NDBS_PURGE 1
6004 #define NDBS_UPDATE 2
6009 struct componentname
*cnp
,
6011 struct nfs_vattr
*nvap
,
6014 daddr64_t
*nextlbnp
,
6017 struct direntry
*dp
;
6018 struct nfs_dir_buf_header
*ndbhp
;
6019 struct nfs_vattr
*nvattrp
;
6020 daddr64_t nextlbn
= 0;
6021 int i
, error
= ESRCH
;
6024 /* scan the buffer for the name */
6025 ndbhp
= (struct nfs_dir_buf_header
*)bp
->nb_data
;
6026 dp
= NFS_DIR_BUF_FIRST_DIRENTRY(bp
);
6027 for (i
= 0; i
< ndbhp
->ndbh_count
; i
++) {
6028 nextlbn
= dp
->d_seekoff
;
6029 if ((cnp
->cn_namelen
== dp
->d_namlen
) && !strcmp(cnp
->cn_nameptr
, dp
->d_name
)) {
6030 fhlen
= dp
->d_name
[dp
->d_namlen
+ 1];
6031 nvattrp
= NFS_DIR_BUF_NVATTR(bp
, i
);
6032 if ((ndbhp
->ndbh_ncgen
!= bp
->nb_np
->n_ncgen
) || (fhlen
== 0) ||
6033 (nvattrp
->nva_type
== VNON
) || (nvattrp
->nva_fileid
== 0)) {
6034 /* entry is not valid */
6038 if (flags
== NDBS_PURGE
) {
6040 bzero(nvattrp
, sizeof(*nvattrp
));
6044 if (flags
== NDBS_UPDATE
) {
6045 /* update direntry's attrs if fh matches */
6046 if ((fhp
->fh_len
== fhlen
) && !bcmp(&dp
->d_name
[dp
->d_namlen
+ 2], fhp
->fh_data
, fhlen
)) {
6047 bcopy(nvap
, nvattrp
, sizeof(*nvap
));
6048 dp
->d_fileno
= nvattrp
->nva_fileid
;
6049 nvattrp
->nva_fileid
= *xidp
;
6050 *(time_t*)(&dp
->d_name
[dp
->d_namlen
+ 2 + fhp
->fh_len
]) = *attrstampp
;
6055 /* copy out fh, attrs, attrstamp, and xid */
6056 fhp
->fh_len
= fhlen
;
6057 bcopy(&dp
->d_name
[dp
->d_namlen
+ 2], fhp
->fh_data
, MAX(fhp
->fh_len
, (int)sizeof(fhp
->fh_data
)));
6058 *attrstampp
= *(time_t*)(&dp
->d_name
[dp
->d_namlen
+ 2 + fhp
->fh_len
]);
6059 bcopy(nvattrp
, nvap
, sizeof(*nvap
));
6060 *xidp
= nvap
->nva_fileid
;
6061 nvap
->nva_fileid
= dp
->d_fileno
;
6065 dp
= NFS_DIRENTRY_NEXT(dp
);
6068 *nextlbnp
= nextlbn
;
6074 * Look up a name in a directory's buffers.
6075 * Note: should only be called with RDIRPLUS directory buffers
6078 nfs_dir_buf_cache_lookup(nfsnode_t dnp
, nfsnode_t
*npp
, struct componentname
*cnp
, vfs_context_t ctx
, int purge
)
6081 struct nfsmount
*nmp
;
6082 int error
= 0, i
, found
= 0, count
= 0;
6084 struct nfs_vattr nvattr
;
6086 time_t attrstamp
= 0;
6087 thread_t thd
= vfs_context_thread(ctx
);
6088 struct nfsbuf
*bp
, *lastbp
, *foundbp
;
6089 struct nfsbuflists blist
;
6090 daddr64_t lbn
, nextlbn
;
6091 int dotunder
= (cnp
->cn_namelen
> 2) && (cnp
->cn_nameptr
[0] == '.') && (cnp
->cn_nameptr
[1] == '_');
6092 int isdot
= (cnp
->cn_namelen
== 1) && (cnp
->cn_nameptr
[0] == '.');
6093 int isdotdot
= (cnp
->cn_namelen
== 2) && (cnp
->cn_nameptr
[0] == '.') && (cnp
->cn_nameptr
[1] == '.');
6095 nmp
= NFSTONMP(dnp
);
6096 if (nfs_mount_gone(nmp
)) {
6103 if (isdot
|| isdotdot
) {
6107 /* first check most recent buffer (and next one too) */
6108 lbn
= dnp
->n_lastdbl
;
6109 for (i
= 0; i
< 2; i
++) {
6110 if ((error
= nfs_buf_get(dnp
, lbn
, NFS_DIRBLKSIZ
, thd
, NBLK_READ
| NBLK_ONLYVALID
, &bp
))) {
6117 error
= nfs_dir_buf_search(bp
, cnp
, &fh
, &nvattr
, &xid
, &attrstamp
, &nextlbn
, purge
? NDBS_PURGE
: 0);
6118 nfs_buf_release(bp
, 0);
6119 if (error
== ESRCH
) {
6128 lck_mtx_lock(nfs_buf_mutex
);
6130 dnp
->n_lastdbl
= lbn
;
6135 * Scan the list of buffers, keeping them in order.
6136 * Note that itercomplete inserts each of the remaining buffers
6137 * into the head of list (thus reversing the elements). So, we
6138 * make sure to iterate through all buffers, inserting them after
6139 * each other, to keep them in order.
6140 * Also note: the LIST_INSERT_AFTER(lastbp) is only safe because
6141 * we don't drop nfs_buf_mutex.
6143 if (!nfs_buf_iterprepare(dnp
, &blist
, NBI_CLEAN
)) {
6144 lastbp
= foundbp
= NULL
;
6145 while ((bp
= LIST_FIRST(&blist
))) {
6146 LIST_REMOVE(bp
, nb_vnbufs
);
6148 LIST_INSERT_HEAD(&dnp
->n_cleanblkhd
, bp
, nb_vnbufs
);
6150 LIST_INSERT_AFTER(lastbp
, bp
, nb_vnbufs
);
6153 if (error
|| found
) {
6156 if (!purge
&& dotunder
&& (count
> 100)) { /* don't waste too much time looking for ._ files */
6160 lbn
= bp
->nb_lblkno
;
6161 if (nfs_buf_acquire(bp
, NBAC_NOWAIT
, 0, 0)) {
6162 /* just skip this buffer */
6163 nfs_buf_refrele(bp
);
6166 nfs_buf_refrele(bp
);
6168 error
= nfs_dir_buf_search(bp
, cnp
, &fh
, &nvattr
, &xid
, &attrstamp
, NULL
, purge
? NDBS_PURGE
: 0);
6169 if (error
== ESRCH
) {
6178 LIST_REMOVE(foundbp
, nb_vnbufs
);
6179 LIST_INSERT_HEAD(&dnp
->n_cleanblkhd
, foundbp
, nb_vnbufs
);
6180 dnp
->n_lastdbl
= foundbp
->nb_lblkno
;
6182 nfs_buf_itercomplete(dnp
, &blist
, NBI_CLEAN
);
6185 lck_mtx_unlock(nfs_buf_mutex
);
6187 if (!error
&& found
&& !purge
) {
6188 error
= nfs_nget(NFSTOMP(dnp
), dnp
, cnp
, fh
.fh_data
, fh
.fh_len
,
6189 &nvattr
, &xid
, dnp
->n_auth
, NG_MAKEENTRY
, &newnp
);
6193 newnp
->n_attrstamp
= attrstamp
;
6195 nfs_node_unlock(newnp
);
6196 /* check if the dir buffer's attrs are out of date */
6197 if (!nfs_getattr(newnp
, &nvattr
, ctx
, NGA_CACHED
) &&
6198 (newnp
->n_attrstamp
!= attrstamp
)) {
6199 /* they are, so update them */
6200 error
= nfs_buf_get(dnp
, lbn
, NFS_DIRBLKSIZ
, thd
, NBLK_READ
| NBLK_ONLYVALID
, &bp
);
6202 attrstamp
= newnp
->n_attrstamp
;
6204 nfs_dir_buf_search(bp
, cnp
, &fh
, &nvattr
, &xid
, &attrstamp
, NULL
, NDBS_UPDATE
);
6205 nfs_buf_release(bp
, 0);
6215 * Purge name cache entries for the given node.
6216 * For RDIRPLUS, also invalidate the entry in the directory's buffers.
6219 nfs_name_cache_purge(nfsnode_t dnp
, nfsnode_t np
, struct componentname
*cnp
, vfs_context_t ctx
)
6221 struct nfsmount
*nmp
= NFSTONMP(dnp
);
6223 cache_purge(NFSTOV(np
));
6224 if (nmp
&& (nmp
->nm_vers
> NFS_VER2
) && NMFLAG(nmp
, RDIRPLUS
)) {
6225 nfs_dir_buf_cache_lookup(dnp
, NULL
, cnp
, ctx
, 1);
6230 * NFS V3 readdir (plus) RPC.
6233 nfs3_readdir_rpc(nfsnode_t dnp
, struct nfsbuf
*bp
, vfs_context_t ctx
)
6235 struct nfsmount
*nmp
;
6236 int error
= 0, lockerror
, nfsvers
, rdirplus
, bigcookies
;
6237 int i
, status
, attrflag
, fhflag
, more_entries
= 1, eof
, bp_dropped
= 0;
6238 uint32_t nmreaddirsize
, nmrsize
;
6239 uint32_t namlen
, skiplen
, fhlen
, xlen
, attrlen
, reclen
, space_free
, space_needed
;
6240 uint64_t cookie
, lastcookie
, xid
, savedxid
, fileno
;
6241 struct nfsm_chain nmreq
, nmrep
, nmrepsave
;
6243 struct nfs_vattr
*nvattrp
;
6244 struct nfs_dir_buf_header
*ndbhp
;
6245 struct direntry
*dp
;
6246 char *padstart
, padlen
;
6249 nmp
= NFSTONMP(dnp
);
6250 if (nfs_mount_gone(nmp
)) {
6253 nfsvers
= nmp
->nm_vers
;
6254 nmreaddirsize
= nmp
->nm_readdirsize
;
6255 nmrsize
= nmp
->nm_rsize
;
6256 bigcookies
= nmp
->nm_state
& NFSSTA_BIGCOOKIES
;
6258 rdirplus
= ((nfsvers
> NFS_VER2
) && NMFLAG(nmp
, RDIRPLUS
)) ? 1 : 0;
6260 if ((lockerror
= nfs_node_lock(dnp
))) {
6264 /* determine cookie to use, and move dp to the right offset */
6265 ndbhp
= (struct nfs_dir_buf_header
*)bp
->nb_data
;
6266 dp
= NFS_DIR_BUF_FIRST_DIRENTRY(bp
);
6267 if (ndbhp
->ndbh_count
) {
6268 for (i
= 0; i
< ndbhp
->ndbh_count
- 1; i
++) {
6269 dp
= NFS_DIRENTRY_NEXT(dp
);
6271 cookie
= dp
->d_seekoff
;
6272 dp
= NFS_DIRENTRY_NEXT(dp
);
6274 cookie
= bp
->nb_lblkno
;
6275 /* increment with every buffer read */
6276 OSAddAtomic64(1, &nfsstats
.readdir_bios
);
6278 lastcookie
= cookie
;
6281 * Loop around doing readdir(plus) RPCs of size nm_readdirsize until
6282 * the buffer is full (or we hit EOF). Then put the remainder of the
6283 * results in the next buffer(s).
6285 nfsm_chain_null(&nmreq
);
6286 nfsm_chain_null(&nmrep
);
6287 while (nfs_dir_buf_freespace(bp
, rdirplus
) && !(ndbhp
->ndbh_flags
& NDB_FULL
)) {
6288 nfsm_chain_build_alloc_init(error
, &nmreq
,
6289 NFSX_FH(nfsvers
) + NFSX_READDIR(nfsvers
) + NFSX_UNSIGNED
);
6290 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
6291 if (nfsvers
== NFS_VER3
) {
6292 /* opaque values don't need swapping, but as long */
6293 /* as we are consistent about it, it should be ok */
6294 nfsm_chain_add_64(error
, &nmreq
, cookie
);
6295 nfsm_chain_add_64(error
, &nmreq
, dnp
->n_cookieverf
);
6297 nfsm_chain_add_32(error
, &nmreq
, cookie
);
6299 nfsm_chain_add_32(error
, &nmreq
, nmreaddirsize
);
6301 nfsm_chain_add_32(error
, &nmreq
, nmrsize
);
6303 nfsm_chain_build_done(error
, &nmreq
);
6304 nfs_node_unlock(dnp
);
6308 error
= nfs_request(dnp
, NULL
, &nmreq
,
6309 rdirplus
? NFSPROC_READDIRPLUS
: NFSPROC_READDIR
,
6310 ctx
, NULL
, &nmrep
, &xid
, &status
);
6312 if ((lockerror
= nfs_node_lock(dnp
))) {
6317 if (nfsvers
== NFS_VER3
) {
6318 nfsm_chain_postop_attr_update(error
, &nmrep
, dnp
, &xid
);
6323 if (nfsvers
== NFS_VER3
) {
6324 nfsm_chain_get_64(error
, &nmrep
, dnp
->n_cookieverf
);
6326 nfsm_chain_get_32(error
, &nmrep
, more_entries
);
6329 nfs_node_unlock(dnp
);
6332 if (error
== NFSERR_NOTSUPP
) {
6333 /* oops... it doesn't look like readdirplus is supported */
6334 lck_mtx_lock(&nmp
->nm_lock
);
6335 NFS_BITMAP_CLR(nmp
->nm_flags
, NFS_MFLAG_RDIRPLUS
);
6336 lck_mtx_unlock(&nmp
->nm_lock
);
6343 if (lastcookie
== 0) {
6344 dnp
->n_rdirplusstamp_sof
= now
.tv_sec
;
6345 dnp
->n_rdirplusstamp_eof
= 0;
6349 /* loop through the entries packing them into the buffer */
6350 while (more_entries
) {
6351 if (nfsvers
== NFS_VER3
) {
6352 nfsm_chain_get_64(error
, &nmrep
, fileno
);
6354 nfsm_chain_get_32(error
, &nmrep
, fileno
);
6356 nfsm_chain_get_32(error
, &nmrep
, namlen
);
6358 /* just truncate names that don't fit in direntry.d_name */
6363 if (namlen
> (sizeof(dp
->d_name
) - 1)) {
6364 skiplen
= namlen
- sizeof(dp
->d_name
) + 1;
6365 namlen
= sizeof(dp
->d_name
) - 1;
6369 /* guess that fh size will be same as parent */
6370 fhlen
= rdirplus
? (1 + dnp
->n_fhsize
) : 0;
6371 xlen
= rdirplus
? (fhlen
+ sizeof(time_t)) : 0;
6372 attrlen
= rdirplus
? sizeof(struct nfs_vattr
) : 0;
6373 reclen
= NFS_DIRENTRY_LEN(namlen
+ xlen
);
6374 space_needed
= reclen
+ attrlen
;
6375 space_free
= nfs_dir_buf_freespace(bp
, rdirplus
);
6376 if (space_needed
> space_free
) {
6378 * We still have entries to pack, but we've
6379 * run out of room in the current buffer.
6380 * So we need to move to the next buffer.
6381 * The block# for the next buffer is the
6382 * last cookie in the current buffer.
6385 ndbhp
->ndbh_flags
|= NDB_FULL
;
6386 nfs_buf_release(bp
, 0);
6389 error
= nfs_buf_get(dnp
, lastcookie
, NFS_DIRBLKSIZ
, vfs_context_thread(ctx
), NBLK_READ
, &bp
);
6391 /* initialize buffer */
6392 ndbhp
= (struct nfs_dir_buf_header
*)bp
->nb_data
;
6393 ndbhp
->ndbh_flags
= 0;
6394 ndbhp
->ndbh_count
= 0;
6395 ndbhp
->ndbh_entry_end
= sizeof(*ndbhp
);
6396 ndbhp
->ndbh_ncgen
= dnp
->n_ncgen
;
6397 space_free
= nfs_dir_buf_freespace(bp
, rdirplus
);
6398 dp
= NFS_DIR_BUF_FIRST_DIRENTRY(bp
);
6399 /* increment with every buffer read */
6400 OSAddAtomic64(1, &nfsstats
.readdir_bios
);
6403 dp
->d_fileno
= fileno
;
6404 dp
->d_namlen
= namlen
;
6405 dp
->d_reclen
= reclen
;
6406 dp
->d_type
= DT_UNKNOWN
;
6407 nfsm_chain_get_opaque(error
, &nmrep
, namlen
, dp
->d_name
);
6409 dp
->d_name
[namlen
] = '\0';
6411 nfsm_chain_adv(error
, &nmrep
,
6412 nfsm_rndup(namlen
+ skiplen
) - nfsm_rndup(namlen
));
6414 if (nfsvers
== NFS_VER3
) {
6415 nfsm_chain_get_64(error
, &nmrep
, cookie
);
6417 nfsm_chain_get_32(error
, &nmrep
, cookie
);
6420 dp
->d_seekoff
= cookie
;
6421 if (!bigcookies
&& (cookie
>> 32) && (nmp
== NFSTONMP(dnp
))) {
6422 /* we've got a big cookie, make sure flag is set */
6423 lck_mtx_lock(&nmp
->nm_lock
);
6424 nmp
->nm_state
|= NFSSTA_BIGCOOKIES
;
6425 lck_mtx_unlock(&nmp
->nm_lock
);
6429 nvattrp
= NFS_DIR_BUF_NVATTR(bp
, ndbhp
->ndbh_count
);
6430 /* check for attributes */
6431 nfsm_chain_get_32(error
, &nmrep
, attrflag
);
6434 /* grab attributes */
6435 error
= nfs_parsefattr(nmp
, &nmrep
, NFS_VER3
, nvattrp
);
6437 dp
->d_type
= IFTODT(VTTOIF(nvattrp
->nva_type
));
6438 /* fileid is already in d_fileno, so stash xid in attrs */
6439 nvattrp
->nva_fileid
= savedxid
;
6441 /* mark the attributes invalid */
6442 bzero(nvattrp
, sizeof(struct nfs_vattr
));
6444 /* check for file handle */
6445 nfsm_chain_get_32(error
, &nmrep
, fhflag
);
6448 nfsm_chain_get_fh(error
, &nmrep
, NFS_VER3
, &fh
);
6450 fhlen
= fh
.fh_len
+ 1;
6451 xlen
= fhlen
+ sizeof(time_t);
6452 reclen
= NFS_DIRENTRY_LEN(namlen
+ xlen
);
6453 space_needed
= reclen
+ attrlen
;
6454 if (space_needed
> space_free
) {
6455 /* didn't actually have the room... move on to next buffer */
6459 /* pack the file handle into the record */
6460 dp
->d_name
[dp
->d_namlen
+ 1] = fh
.fh_len
;
6461 bcopy(fh
.fh_data
, &dp
->d_name
[dp
->d_namlen
+ 2], fh
.fh_len
);
6463 /* mark the file handle invalid */
6465 fhlen
= fh
.fh_len
+ 1;
6466 xlen
= fhlen
+ sizeof(time_t);
6467 reclen
= NFS_DIRENTRY_LEN(namlen
+ xlen
);
6468 bzero(&dp
->d_name
[dp
->d_namlen
+ 1], fhlen
);
6470 *(time_t*)(&dp
->d_name
[dp
->d_namlen
+ 1 + fhlen
]) = now
.tv_sec
;
6471 dp
->d_reclen
= reclen
;
6472 nfs_rdirplus_update_node_attrs(dnp
, dp
, &fh
, nvattrp
, &savedxid
);
6474 padstart
= dp
->d_name
+ dp
->d_namlen
+ 1 + xlen
;
6475 ndbhp
->ndbh_count
++;
6476 lastcookie
= cookie
;
6477 /* advance to next direntry in buffer */
6478 dp
= NFS_DIRENTRY_NEXT(dp
);
6479 ndbhp
->ndbh_entry_end
= (char*)dp
- bp
->nb_data
;
6480 /* zero out the pad bytes */
6481 padlen
= (char*)dp
- padstart
;
6483 bzero(padstart
, padlen
);
6485 /* check for more entries */
6486 nfsm_chain_get_32(error
, &nmrep
, more_entries
);
6489 /* Finally, get the eof boolean */
6490 nfsm_chain_get_32(error
, &nmrep
, eof
);
6493 ndbhp
->ndbh_flags
|= (NDB_FULL
| NDB_EOF
);
6494 nfs_node_lock_force(dnp
);
6495 dnp
->n_eofcookie
= lastcookie
;
6497 dnp
->n_rdirplusstamp_eof
= now
.tv_sec
;
6499 nfs_node_unlock(dnp
);
6504 nfs_buf_release(bp
, 0);
6508 if ((lockerror
= nfs_node_lock(dnp
))) {
6512 nfsm_chain_cleanup(&nmrep
);
6513 nfsm_chain_null(&nmreq
);
6516 if (bp_dropped
&& bp
) {
6517 nfs_buf_release(bp
, 0);
6520 nfs_node_unlock(dnp
);
6522 nfsm_chain_cleanup(&nmreq
);
6523 nfsm_chain_cleanup(&nmrep
);
6524 return bp_dropped
? NFSERR_DIRBUFDROPPED
: error
;
6528 * Silly rename. To make the NFS filesystem that is stateless look a little
6529 * more like the "ufs" a remove of an active vnode is translated to a rename
6530 * to a funny looking filename that is removed by nfs_vnop_inactive on the
6531 * nfsnode. There is the potential for another process on a different client
6532 * to create the same funny name between when the lookitup() fails and the
6533 * rename() completes, but...
6536 /* format of "random" silly names - includes a number and pid */
6537 /* (note: shouldn't exceed size of nfs_sillyrename.nsr_name) */
6538 #define NFS_SILLYNAME_FORMAT ".nfs.%08x.%04x"
6539 /* starting from zero isn't silly enough */
6540 static uint32_t nfs_sillyrename_number
= 0x20051025;
6546 struct componentname
*cnp
,
6549 struct nfs_sillyrename
*nsp
;
6554 struct nfsmount
*nmp
;
6556 nmp
= NFSTONMP(dnp
);
6557 if (nfs_mount_gone(nmp
)) {
6561 nfs_name_cache_purge(dnp
, np
, cnp
, ctx
);
6563 MALLOC_ZONE(nsp
, struct nfs_sillyrename
*,
6564 sizeof(struct nfs_sillyrename
), M_NFSREQ
, M_WAITOK
);
6568 cred
= vfs_context_ucred(ctx
);
6569 kauth_cred_ref(cred
);
6570 nsp
->nsr_cred
= cred
;
6572 error
= vnode_ref(NFSTOV(dnp
));
6577 /* Fudge together a funny name */
6578 pid
= vfs_context_pid(ctx
);
6579 num
= OSAddAtomic(1, &nfs_sillyrename_number
);
6580 nsp
->nsr_namlen
= snprintf(nsp
->nsr_name
, sizeof(nsp
->nsr_name
),
6581 NFS_SILLYNAME_FORMAT
, num
, (pid
& 0xffff));
6582 if (nsp
->nsr_namlen
>= (int)sizeof(nsp
->nsr_name
)) {
6583 nsp
->nsr_namlen
= sizeof(nsp
->nsr_name
) - 1;
6586 /* Try lookitups until we get one that isn't there */
6587 while (nfs_lookitup(dnp
, nsp
->nsr_name
, nsp
->nsr_namlen
, ctx
, NULL
) == 0) {
6588 num
= OSAddAtomic(1, &nfs_sillyrename_number
);
6589 nsp
->nsr_namlen
= snprintf(nsp
->nsr_name
, sizeof(nsp
->nsr_name
),
6590 NFS_SILLYNAME_FORMAT
, num
, (pid
& 0xffff));
6591 if (nsp
->nsr_namlen
>= (int)sizeof(nsp
->nsr_name
)) {
6592 nsp
->nsr_namlen
= sizeof(nsp
->nsr_name
) - 1;
6596 /* now, do the rename */
6597 error
= nmp
->nm_funcs
->nf_rename_rpc(dnp
, cnp
->cn_nameptr
, cnp
->cn_namelen
,
6598 dnp
, nsp
->nsr_name
, nsp
->nsr_namlen
, ctx
);
6600 /* Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */
6601 if (error
== ENOENT
) {
6605 nfs_node_lock_force(dnp
);
6606 if (dnp
->n_flag
& NNEGNCENTRIES
) {
6607 dnp
->n_flag
&= ~NNEGNCENTRIES
;
6608 cache_purge_negatives(NFSTOV(dnp
));
6610 nfs_node_unlock(dnp
);
6612 FSDBG(267, dnp
, np
, num
, error
);
6616 error
= nfs_lookitup(dnp
, nsp
->nsr_name
, nsp
->nsr_namlen
, ctx
, &np
);
6617 nfs_node_lock_force(np
);
6618 np
->n_sillyrename
= nsp
;
6619 nfs_node_unlock(np
);
6622 vnode_rele(NFSTOV(dnp
));
6624 nsp
->nsr_cred
= NOCRED
;
6625 kauth_cred_unref(&cred
);
6626 FREE_ZONE(nsp
, sizeof(*nsp
), M_NFSREQ
);
6631 nfs3_lookup_rpc_async(
6636 struct nfsreq
**reqp
)
6638 struct nfsmount
*nmp
;
6639 struct nfsm_chain nmreq
;
6640 int error
= 0, nfsvers
;
6642 nmp
= NFSTONMP(dnp
);
6643 if (nfs_mount_gone(nmp
)) {
6646 nfsvers
= nmp
->nm_vers
;
6648 nfsm_chain_null(&nmreq
);
6650 nfsm_chain_build_alloc_init(error
, &nmreq
,
6651 NFSX_FH(nfsvers
) + NFSX_UNSIGNED
+ nfsm_rndup(namelen
));
6652 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, dnp
->n_fhp
, dnp
->n_fhsize
);
6653 nfsm_chain_add_name(error
, &nmreq
, name
, namelen
, nmp
);
6654 nfsm_chain_build_done(error
, &nmreq
);
6656 error
= nfs_request_async(dnp
, NULL
, &nmreq
, NFSPROC_LOOKUP
,
6657 vfs_context_thread(ctx
), vfs_context_ucred(ctx
), NULL
, 0, NULL
, reqp
);
6659 nfsm_chain_cleanup(&nmreq
);
6664 nfs3_lookup_rpc_async_finish(
6666 __unused
char *name
,
6667 __unused
int namelen
,
6672 struct nfs_vattr
*nvap
)
6674 int error
= 0, lockerror
= ENOENT
, status
, nfsvers
, attrflag
;
6676 struct nfsmount
*nmp
;
6677 struct nfsm_chain nmrep
;
6679 nmp
= NFSTONMP(dnp
);
6683 nfsvers
= nmp
->nm_vers
;
6685 nfsm_chain_null(&nmrep
);
6687 error
= nfs_request_async_finish(req
, &nmrep
, xidp
, &status
);
6689 if ((lockerror
= nfs_node_lock(dnp
))) {
6693 if (error
|| status
) {
6694 if (nfsvers
== NFS_VER3
) {
6695 nfsm_chain_postop_attr_update(error
, &nmrep
, dnp
, &xid
);
6703 nfsmout_if(error
|| !fhp
|| !nvap
);
6705 /* get the file handle */
6706 nfsm_chain_get_fh(error
, &nmrep
, nfsvers
, fhp
);
6708 /* get the attributes */
6709 if (nfsvers
== NFS_VER3
) {
6710 nfsm_chain_postop_attr_get(nmp
, error
, &nmrep
, attrflag
, nvap
);
6711 nfsm_chain_postop_attr_update(error
, &nmrep
, dnp
, &xid
);
6712 if (!error
&& !attrflag
) {
6713 error
= nfs3_getattr_rpc(NULL
, NFSTOMP(dnp
), fhp
->fh_data
, fhp
->fh_len
, 0, ctx
, nvap
, xidp
);
6716 error
= nfs_parsefattr(nmp
, &nmrep
, nfsvers
, nvap
);
6720 nfs_node_unlock(dnp
);
6722 nfsm_chain_cleanup(&nmrep
);
6727 * Look up a file name and optionally either update the file handle or
6728 * allocate an nfsnode, depending on the value of npp.
6729 * npp == NULL --> just do the lookup
6730 * *npp == NULL --> allocate a new nfsnode and make sure attributes are
6732 * *npp != NULL --> update the file handle in the vnode
6743 nfsnode_t np
, newnp
= NULL
;
6746 struct nfsmount
*nmp
;
6747 struct nfs_vattr nvattr
;
6748 struct nfsreq rq
, *req
= &rq
;
6750 nmp
= NFSTONMP(dnp
);
6751 if (nfs_mount_gone(nmp
)) {
6755 if (NFS_BITMAP_ISSET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_MAXNAME
) &&
6756 (namelen
> (int)nmp
->nm_fsattr
.nfsa_maxname
)) {
6757 return ENAMETOOLONG
;
6760 NVATTR_INIT(&nvattr
);
6762 /* check for lookup of "." */
6763 if ((name
[0] == '.') && (namelen
== 1)) {
6764 /* skip lookup, we know who we are */
6770 error
= nmp
->nm_funcs
->nf_lookup_rpc_async(dnp
, name
, namelen
, ctx
, &req
);
6772 error
= nmp
->nm_funcs
->nf_lookup_rpc_async_finish(dnp
, name
, namelen
, ctx
, req
, &xid
, &fh
, &nvattr
);
6773 nfsmout_if(!npp
|| error
);
6777 if (fh
.fh_len
!= np
->n_fhsize
) {
6778 u_char
*oldbuf
= (np
->n_fhsize
> NFS_SMALLFH
) ? np
->n_fhp
: NULL
;
6779 if (fh
.fh_len
> NFS_SMALLFH
) {
6780 MALLOC_ZONE(np
->n_fhp
, u_char
*, fh
.fh_len
, M_NFSBIGFH
, M_WAITOK
);
6787 np
->n_fhp
= &np
->n_fh
[0];
6790 FREE_ZONE(oldbuf
, np
->n_fhsize
, M_NFSBIGFH
);
6793 bcopy(fh
.fh_data
, np
->n_fhp
, fh
.fh_len
);
6794 np
->n_fhsize
= fh
.fh_len
;
6795 nfs_node_lock_force(np
);
6796 error
= nfs_loadattrcache(np
, &nvattr
, &xid
, 0);
6797 nfs_node_unlock(np
);
6800 } else if (NFS_CMPFH(dnp
, fh
.fh_data
, fh
.fh_len
)) {
6801 nfs_node_lock_force(dnp
);
6802 if (dnp
->n_xid
<= xid
) {
6803 error
= nfs_loadattrcache(dnp
, &nvattr
, &xid
, 0);
6805 nfs_node_unlock(dnp
);
6809 struct componentname cn
, *cnp
= &cn
;
6810 bzero(cnp
, sizeof(*cnp
));
6811 cnp
->cn_nameptr
= name
;
6812 cnp
->cn_namelen
= namelen
;
6813 error
= nfs_nget(NFSTOMP(dnp
), dnp
, cnp
, fh
.fh_data
, fh
.fh_len
,
6814 &nvattr
, &xid
, rq
.r_auth
, NG_MAKEENTRY
, &np
);
6820 if (npp
&& !*npp
&& !error
) {
6823 NVATTR_CLEANUP(&nvattr
);
6828 * set up and initialize a "._" file lookup structure used for
6829 * performing async lookups.
6832 nfs_dulookup_init(struct nfs_dulookup
*dulp
, nfsnode_t dnp
, const char *name
, int namelen
, vfs_context_t ctx
)
6834 int error
, du_namelen
;
6836 struct nfsmount
*nmp
= NFSTONMP(dnp
);
6838 /* check for ._ file in name cache */
6840 bzero(&dulp
->du_cn
, sizeof(dulp
->du_cn
));
6841 du_namelen
= namelen
+ 2;
6842 if (!nmp
|| NMFLAG(nmp
, NONEGNAMECACHE
)) {
6845 if ((namelen
>= 2) && (name
[0] == '.') && (name
[1] == '_')) {
6848 if (du_namelen
>= (int)sizeof(dulp
->du_smallname
)) {
6849 MALLOC(dulp
->du_cn
.cn_nameptr
, char *, du_namelen
+ 1, M_TEMP
, M_WAITOK
);
6851 dulp
->du_cn
.cn_nameptr
= dulp
->du_smallname
;
6853 if (!dulp
->du_cn
.cn_nameptr
) {
6856 dulp
->du_cn
.cn_namelen
= du_namelen
;
6857 snprintf(dulp
->du_cn
.cn_nameptr
, du_namelen
+ 1, "._%s", name
);
6858 dulp
->du_cn
.cn_nameptr
[du_namelen
] = '\0';
6859 dulp
->du_cn
.cn_nameiop
= LOOKUP
;
6860 dulp
->du_cn
.cn_flags
= MAKEENTRY
;
6862 error
= cache_lookup(NFSTOV(dnp
), &du_vp
, &dulp
->du_cn
);
6865 } else if (!error
) {
6866 nmp
= NFSTONMP(dnp
);
6867 if (nmp
&& (nmp
->nm_vers
> NFS_VER2
) && NMFLAG(nmp
, RDIRPLUS
)) {
6868 /* if rdirplus, try dir buf cache lookup */
6869 nfsnode_t du_np
= NULL
;
6870 if (!nfs_dir_buf_cache_lookup(dnp
, &du_np
, &dulp
->du_cn
, ctx
, 0) && du_np
) {
6871 /* dir buf cache hit */
6872 du_vp
= NFSTOV(du_np
);
6878 dulp
->du_flags
|= NFS_DULOOKUP_DOIT
;
6884 * start an async "._" file lookup request
6887 nfs_dulookup_start(struct nfs_dulookup
*dulp
, nfsnode_t dnp
, vfs_context_t ctx
)
6889 struct nfsmount
*nmp
= NFSTONMP(dnp
);
6890 struct nfsreq
*req
= &dulp
->du_req
;
6892 if (!nmp
|| !(dulp
->du_flags
& NFS_DULOOKUP_DOIT
) || (dulp
->du_flags
& NFS_DULOOKUP_INPROG
)) {
6895 if (!nmp
->nm_funcs
->nf_lookup_rpc_async(dnp
, dulp
->du_cn
.cn_nameptr
,
6896 dulp
->du_cn
.cn_namelen
, ctx
, &req
)) {
6897 dulp
->du_flags
|= NFS_DULOOKUP_INPROG
;
6902 * finish an async "._" file lookup request and clean up the structure
6905 nfs_dulookup_finish(struct nfs_dulookup
*dulp
, nfsnode_t dnp
, vfs_context_t ctx
)
6907 struct nfsmount
*nmp
= NFSTONMP(dnp
);
6912 struct nfs_vattr nvattr
;
6914 if (!nmp
|| !(dulp
->du_flags
& NFS_DULOOKUP_INPROG
)) {
6918 NVATTR_INIT(&nvattr
);
6919 error
= nmp
->nm_funcs
->nf_lookup_rpc_async_finish(dnp
, dulp
->du_cn
.cn_nameptr
,
6920 dulp
->du_cn
.cn_namelen
, ctx
, &dulp
->du_req
, &xid
, &fh
, &nvattr
);
6921 dulp
->du_flags
&= ~NFS_DULOOKUP_INPROG
;
6922 if (error
== ENOENT
) {
6923 /* add a negative entry in the name cache */
6924 nfs_node_lock_force(dnp
);
6925 cache_enter(NFSTOV(dnp
), NULL
, &dulp
->du_cn
);
6926 dnp
->n_flag
|= NNEGNCENTRIES
;
6927 nfs_node_unlock(dnp
);
6928 } else if (!error
) {
6929 error
= nfs_nget(NFSTOMP(dnp
), dnp
, &dulp
->du_cn
, fh
.fh_data
, fh
.fh_len
,
6930 &nvattr
, &xid
, dulp
->du_req
.r_auth
, NG_MAKEENTRY
, &du_np
);
6932 nfs_node_unlock(du_np
);
6933 vnode_put(NFSTOV(du_np
));
6936 NVATTR_CLEANUP(&nvattr
);
6938 if (dulp
->du_flags
& NFS_DULOOKUP_INPROG
) {
6939 nfs_request_async_cancel(&dulp
->du_req
);
6941 if (dulp
->du_cn
.cn_nameptr
&& (dulp
->du_cn
.cn_nameptr
!= dulp
->du_smallname
)) {
6942 FREE(dulp
->du_cn
.cn_nameptr
, M_TEMP
);
6948 * NFS Version 3 commit RPC
6958 struct nfsmount
*nmp
;
6959 int error
= 0, lockerror
, status
, wccpostattr
= 0, nfsvers
;
6960 struct timespec premtime
= { .tv_sec
= 0, .tv_nsec
= 0 };
6961 u_int64_t xid
, newwverf
;
6963 struct nfsm_chain nmreq
, nmrep
;
6966 FSDBG(521, np
, offset
, count
, nmp
? nmp
->nm_state
: 0);
6967 if (nfs_mount_gone(nmp
)) {
6970 if (!(nmp
->nm_state
& NFSSTA_HASWRITEVERF
)) {
6973 nfsvers
= nmp
->nm_vers
;
6975 if (count
> UINT32_MAX
) {
6981 nfsm_chain_null(&nmreq
);
6982 nfsm_chain_null(&nmrep
);
6984 nfsm_chain_build_alloc_init(error
, &nmreq
, NFSX_FH(NFS_VER3
));
6985 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
6986 nfsm_chain_add_64(error
, &nmreq
, offset
);
6987 nfsm_chain_add_32(error
, &nmreq
, count32
);
6988 nfsm_chain_build_done(error
, &nmreq
);
6990 error
= nfs_request2(np
, NULL
, &nmreq
, NFSPROC_COMMIT
,
6991 current_thread(), cred
, NULL
, 0, &nmrep
, &xid
, &status
);
6992 if ((lockerror
= nfs_node_lock(np
))) {
6995 /* can we do anything useful with the wcc info? */
6996 nfsm_chain_get_wcc_data(error
, &nmrep
, np
, &premtime
, &wccpostattr
, &xid
);
6998 nfs_node_unlock(np
);
7003 nfsm_chain_get_64(error
, &nmrep
, newwverf
);
7005 lck_mtx_lock(&nmp
->nm_lock
);
7006 if (nmp
->nm_verf
!= newwverf
) {
7007 nmp
->nm_verf
= newwverf
;
7009 if (wverf
!= newwverf
) {
7010 error
= NFSERR_STALEWRITEVERF
;
7012 lck_mtx_unlock(&nmp
->nm_lock
);
7014 nfsm_chain_cleanup(&nmreq
);
7015 nfsm_chain_cleanup(&nmrep
);
7022 __unused
struct vnop_blockmap_args
/* {
7023 * struct vnodeop_desc *a_desc;
7038 * fsync vnode op. Just call nfs_flush().
7043 struct vnop_fsync_args
/* {
7044 * struct vnodeop_desc *a_desc;
7047 * vfs_context_t a_context;
7050 return nfs_flush(VTONFS(ap
->a_vp
), ap
->a_waitfor
, vfs_context_thread(ap
->a_context
), 0);
7055 * Do an NFS pathconf RPC.
7060 struct nfs_fsattr
*nfsap
,
7064 int error
= 0, lockerror
, status
, nfsvers
;
7065 struct nfsm_chain nmreq
, nmrep
;
7066 struct nfsmount
*nmp
= NFSTONMP(np
);
7069 if (nfs_mount_gone(nmp
)) {
7072 nfsvers
= nmp
->nm_vers
;
7074 nfsm_chain_null(&nmreq
);
7075 nfsm_chain_null(&nmrep
);
7077 /* fetch pathconf info from server */
7078 nfsm_chain_build_alloc_init(error
, &nmreq
, NFSX_FH(NFS_VER3
));
7079 nfsm_chain_add_fh(error
, &nmreq
, nfsvers
, np
->n_fhp
, np
->n_fhsize
);
7080 nfsm_chain_build_done(error
, &nmreq
);
7082 error
= nfs_request(np
, NULL
, &nmreq
, NFSPROC_PATHCONF
, ctx
, NULL
, &nmrep
, &xid
, &status
);
7083 if ((lockerror
= nfs_node_lock(np
))) {
7086 nfsm_chain_postop_attr_update(error
, &nmrep
, np
, &xid
);
7088 nfs_node_unlock(np
);
7093 nfsm_chain_get_32(error
, &nmrep
, nfsap
->nfsa_maxlink
);
7094 nfsm_chain_get_32(error
, &nmrep
, nfsap
->nfsa_maxname
);
7095 nfsap
->nfsa_flags
&= ~(NFS_FSFLAG_NO_TRUNC
| NFS_FSFLAG_CHOWN_RESTRICTED
| NFS_FSFLAG_CASE_INSENSITIVE
| NFS_FSFLAG_CASE_PRESERVING
);
7096 nfsm_chain_get_32(error
, &nmrep
, val
);
7098 nfsap
->nfsa_flags
|= NFS_FSFLAG_NO_TRUNC
;
7100 nfsm_chain_get_32(error
, &nmrep
, val
);
7102 nfsap
->nfsa_flags
|= NFS_FSFLAG_CHOWN_RESTRICTED
;
7104 nfsm_chain_get_32(error
, &nmrep
, val
);
7106 nfsap
->nfsa_flags
|= NFS_FSFLAG_CASE_INSENSITIVE
;
7108 nfsm_chain_get_32(error
, &nmrep
, val
);
7110 nfsap
->nfsa_flags
|= NFS_FSFLAG_CASE_PRESERVING
;
7112 NFS_BITMAP_SET(nfsap
->nfsa_bitmap
, NFS_FATTR_MAXLINK
);
7113 NFS_BITMAP_SET(nfsap
->nfsa_bitmap
, NFS_FATTR_MAXNAME
);
7114 NFS_BITMAP_SET(nfsap
->nfsa_bitmap
, NFS_FATTR_NO_TRUNC
);
7115 NFS_BITMAP_SET(nfsap
->nfsa_bitmap
, NFS_FATTR_CHOWN_RESTRICTED
);
7116 NFS_BITMAP_SET(nfsap
->nfsa_bitmap
, NFS_FATTR_CASE_INSENSITIVE
);
7117 NFS_BITMAP_SET(nfsap
->nfsa_bitmap
, NFS_FATTR_CASE_PRESERVING
);
7119 nfsm_chain_cleanup(&nmreq
);
7120 nfsm_chain_cleanup(&nmrep
);
7124 /* save pathconf info for NFSv3 mount */
7126 nfs3_pathconf_cache(struct nfsmount
*nmp
, struct nfs_fsattr
*nfsap
)
7128 nmp
->nm_fsattr
.nfsa_maxlink
= nfsap
->nfsa_maxlink
;
7129 nmp
->nm_fsattr
.nfsa_maxname
= nfsap
->nfsa_maxname
;
7130 nmp
->nm_fsattr
.nfsa_flags
&= ~(NFS_FSFLAG_NO_TRUNC
| NFS_FSFLAG_CHOWN_RESTRICTED
| NFS_FSFLAG_CASE_INSENSITIVE
| NFS_FSFLAG_CASE_PRESERVING
);
7131 nmp
->nm_fsattr
.nfsa_flags
|= nfsap
->nfsa_flags
& NFS_FSFLAG_NO_TRUNC
;
7132 nmp
->nm_fsattr
.nfsa_flags
|= nfsap
->nfsa_flags
& NFS_FSFLAG_CHOWN_RESTRICTED
;
7133 nmp
->nm_fsattr
.nfsa_flags
|= nfsap
->nfsa_flags
& NFS_FSFLAG_CASE_INSENSITIVE
;
7134 nmp
->nm_fsattr
.nfsa_flags
|= nfsap
->nfsa_flags
& NFS_FSFLAG_CASE_PRESERVING
;
7135 NFS_BITMAP_SET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_MAXLINK
);
7136 NFS_BITMAP_SET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_MAXNAME
);
7137 NFS_BITMAP_SET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_NO_TRUNC
);
7138 NFS_BITMAP_SET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_CHOWN_RESTRICTED
);
7139 NFS_BITMAP_SET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_CASE_INSENSITIVE
);
7140 NFS_BITMAP_SET(nmp
->nm_fsattr
.nfsa_bitmap
, NFS_FATTR_CASE_PRESERVING
);
7141 nmp
->nm_state
|= NFSSTA_GOTPATHCONF
;
7145 * Return POSIX pathconf information applicable to nfs.
7147 * The NFS V2 protocol doesn't support this, so just return EINVAL
7153 struct vnop_pathconf_args
/* {
7154 * struct vnodeop_desc *a_desc;
7157 * int32_t *a_retval;
7158 * vfs_context_t a_context;
7161 vnode_t vp
= ap
->a_vp
;
7162 nfsnode_t np
= VTONFS(vp
);
7163 struct nfsmount
*nmp
;
7164 struct nfs_fsattr nfsa
, *nfsap
;
7166 uint64_t maxFileSize
;
7170 if (nfs_mount_gone(nmp
)) {
7174 switch (ap
->a_name
) {
7177 case _PC_CHOWN_RESTRICTED
:
7179 case _PC_CASE_SENSITIVE
:
7180 case _PC_CASE_PRESERVING
:
7182 case _PC_FILESIZEBITS
:
7183 if (nmp
->nm_vers
== NFS_VER2
) {
7188 case _PC_XATTR_SIZE_BITS
:
7189 /* Do we support xattrs natively? */
7190 if (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_NAMED_ATTR
) {
7193 /* No... so just return an error */
7196 /* don't bother contacting the server if we know the answer */
7200 if (nmp
->nm_vers
== NFS_VER2
) {
7204 lck_mtx_lock(&nmp
->nm_lock
);
7205 if (nmp
->nm_vers
== NFS_VER3
) {
7206 if (!(nmp
->nm_state
& NFSSTA_GOTPATHCONF
)) {
7207 /* no pathconf info cached */
7208 lck_mtx_unlock(&nmp
->nm_lock
);
7209 NFS_CLEAR_ATTRIBUTES(nfsa
.nfsa_bitmap
);
7210 error
= nfs3_pathconf_rpc(np
, &nfsa
, ap
->a_context
);
7215 if (nfs_mount_gone(nmp
)) {
7218 lck_mtx_lock(&nmp
->nm_lock
);
7219 if (nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_HOMOGENEOUS
) {
7220 /* all files have the same pathconf info, */
7221 /* so cache a copy of the results */
7222 nfs3_pathconf_cache(nmp
, &nfsa
);
7226 nfsap
= &nmp
->nm_fsattr
;
7230 else if (!(nmp
->nm_fsattr
.nfsa_flags
& NFS_FSFLAG_HOMOGENEOUS
)) {
7231 /* no pathconf info cached */
7232 lck_mtx_unlock(&nmp
->nm_lock
);
7233 NFS_CLEAR_ATTRIBUTES(nfsa
.nfsa_bitmap
);
7234 error
= nfs4_pathconf_rpc(np
, &nfsa
, ap
->a_context
);
7239 if (nfs_mount_gone(nmp
)) {
7242 lck_mtx_lock(&nmp
->nm_lock
);
7247 nfsap
= &nmp
->nm_fsattr
;
7249 switch (ap
->a_name
) {
7251 if (NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_MAXLINK
)) {
7252 *ap
->a_retval
= nfsap
->nfsa_maxlink
;
7254 } else if ((nmp
->nm_vers
== NFS_VER4
) && NFS_BITMAP_ISSET(np
->n_vattr
.nva_bitmap
, NFS_FATTR_MAXLINK
)) {
7255 *ap
->a_retval
= np
->n_vattr
.nva_maxlink
;
7262 if (NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_MAXNAME
)) {
7263 *ap
->a_retval
= nfsap
->nfsa_maxname
;
7268 case _PC_CHOWN_RESTRICTED
:
7269 if (NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_CHOWN_RESTRICTED
)) {
7270 *ap
->a_retval
= (nfsap
->nfsa_flags
& NFS_FSFLAG_CHOWN_RESTRICTED
) ? 200112 /* _POSIX_CHOWN_RESTRICTED */ : 0;
7276 if (NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_NO_TRUNC
)) {
7277 *ap
->a_retval
= (nfsap
->nfsa_flags
& NFS_FSFLAG_NO_TRUNC
) ? 200112 /* _POSIX_NO_TRUNC */ : 0;
7282 case _PC_CASE_SENSITIVE
:
7283 if (NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_CASE_INSENSITIVE
)) {
7284 *ap
->a_retval
= (nfsap
->nfsa_flags
& NFS_FSFLAG_CASE_INSENSITIVE
) ? 0 : 1;
7289 case _PC_CASE_PRESERVING
:
7290 if (NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_CASE_PRESERVING
)) {
7291 *ap
->a_retval
= (nfsap
->nfsa_flags
& NFS_FSFLAG_CASE_PRESERVING
) ? 1 : 0;
7296 case _PC_XATTR_SIZE_BITS
: /* same as file size bits if named attrs supported */
7297 case _PC_FILESIZEBITS
:
7298 if (!NFS_BITMAP_ISSET(nfsap
->nfsa_bitmap
, NFS_FATTR_MAXFILESIZE
)) {
7303 maxFileSize
= nfsap
->nfsa_maxfilesize
;
7305 if (maxFileSize
& 0xffffffff00000000ULL
) {
7309 if (maxFileSize
& 0xffff0000) {
7313 if (maxFileSize
& 0xff00) {
7317 if (maxFileSize
& 0xf0) {
7321 if (maxFileSize
& 0xc) {
7325 if (maxFileSize
& 0x2) {
7328 *ap
->a_retval
= nbits
;
7334 lck_mtx_unlock(&nmp
->nm_lock
);
7340 * Read wrapper for special devices.
7344 struct vnop_read_args
/* {
7345 * struct vnodeop_desc *a_desc;
7347 * struct uio *a_uio;
7349 * vfs_context_t a_context;
7352 nfsnode_t np
= VTONFS(ap
->a_vp
);
7353 struct timespec now
;
7359 if ((error
= nfs_node_lock(np
))) {
7364 np
->n_atim
.tv_sec
= now
.tv_sec
;
7365 np
->n_atim
.tv_nsec
= now
.tv_nsec
;
7366 nfs_node_unlock(np
);
7367 return VOCALL(spec_vnodeop_p
, VOFFSET(vnop_read
), ap
);
7371 * Write wrapper for special devices.
7375 struct vnop_write_args
/* {
7376 * struct vnodeop_desc *a_desc;
7378 * struct uio *a_uio;
7380 * vfs_context_t a_context;
7383 nfsnode_t np
= VTONFS(ap
->a_vp
);
7384 struct timespec now
;
7390 if ((error
= nfs_node_lock(np
))) {
7395 np
->n_mtim
.tv_sec
= now
.tv_sec
;
7396 np
->n_mtim
.tv_nsec
= now
.tv_nsec
;
7397 nfs_node_unlock(np
);
7398 return VOCALL(spec_vnodeop_p
, VOFFSET(vnop_write
), ap
);
7402 * Close wrapper for special devices.
7404 * Update the times on the nfsnode then do device close.
7408 struct vnop_close_args
/* {
7409 * struct vnodeop_desc *a_desc;
7412 * vfs_context_t a_context;
7415 vnode_t vp
= ap
->a_vp
;
7416 nfsnode_t np
= VTONFS(vp
);
7417 struct vnode_attr vattr
;
7421 if ((error
= nfs_node_lock(np
))) {
7424 if (np
->n_flag
& (NACC
| NUPD
)) {
7426 if (!vnode_isinuse(vp
, 0) && (mp
= vnode_mount(vp
)) && !vfs_isrdonly(mp
)) {
7428 if (np
->n_flag
& NACC
) {
7429 vattr
.va_access_time
= np
->n_atim
;
7430 VATTR_SET_ACTIVE(&vattr
, va_access_time
);
7432 if (np
->n_flag
& NUPD
) {
7433 vattr
.va_modify_time
= np
->n_mtim
;
7434 VATTR_SET_ACTIVE(&vattr
, va_modify_time
);
7436 nfs_node_unlock(np
);
7437 vnode_setattr(vp
, &vattr
, ap
->a_context
);
7439 nfs_node_unlock(np
);
7442 nfs_node_unlock(np
);
7444 return VOCALL(spec_vnodeop_p
, VOFFSET(vnop_close
), ap
);
7448 extern vnop_t
**fifo_vnodeop_p
;
7451 * Read wrapper for fifos.
7455 struct vnop_read_args
/* {
7456 * struct vnodeop_desc *a_desc;
7458 * struct uio *a_uio;
7460 * vfs_context_t a_context;
7463 nfsnode_t np
= VTONFS(ap
->a_vp
);
7464 struct timespec now
;
7470 if ((error
= nfs_node_lock(np
))) {
7475 np
->n_atim
.tv_sec
= now
.tv_sec
;
7476 np
->n_atim
.tv_nsec
= now
.tv_nsec
;
7477 nfs_node_unlock(np
);
7478 return VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_read
), ap
);
7482 * Write wrapper for fifos.
7486 struct vnop_write_args
/* {
7487 * struct vnodeop_desc *a_desc;
7489 * struct uio *a_uio;
7491 * vfs_context_t a_context;
7494 nfsnode_t np
= VTONFS(ap
->a_vp
);
7495 struct timespec now
;
7501 if ((error
= nfs_node_lock(np
))) {
7506 np
->n_mtim
.tv_sec
= now
.tv_sec
;
7507 np
->n_mtim
.tv_nsec
= now
.tv_nsec
;
7508 nfs_node_unlock(np
);
7509 return VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_write
), ap
);
7513 * Close wrapper for fifos.
7515 * Update the times on the nfsnode then do fifo close.
7519 struct vnop_close_args
/* {
7520 * struct vnodeop_desc *a_desc;
7523 * vfs_context_t a_context;
7526 vnode_t vp
= ap
->a_vp
;
7527 nfsnode_t np
= VTONFS(vp
);
7528 struct vnode_attr vattr
;
7529 struct timespec now
;
7533 if ((error
= nfs_node_lock(np
))) {
7536 if (np
->n_flag
& (NACC
| NUPD
)) {
7538 if (np
->n_flag
& NACC
) {
7539 np
->n_atim
.tv_sec
= now
.tv_sec
;
7540 np
->n_atim
.tv_nsec
= now
.tv_nsec
;
7542 if (np
->n_flag
& NUPD
) {
7543 np
->n_mtim
.tv_sec
= now
.tv_sec
;
7544 np
->n_mtim
.tv_nsec
= now
.tv_nsec
;
7547 if (!vnode_isinuse(vp
, 1) && (mp
= vnode_mount(vp
)) && !vfs_isrdonly(mp
)) {
7549 if (np
->n_flag
& NACC
) {
7550 vattr
.va_access_time
= np
->n_atim
;
7551 VATTR_SET_ACTIVE(&vattr
, va_access_time
);
7553 if (np
->n_flag
& NUPD
) {
7554 vattr
.va_modify_time
= np
->n_mtim
;
7555 VATTR_SET_ACTIVE(&vattr
, va_modify_time
);
7557 nfs_node_unlock(np
);
7558 vnode_setattr(vp
, &vattr
, ap
->a_context
);
7560 nfs_node_unlock(np
);
7563 nfs_node_unlock(np
);
7565 return VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_close
), ap
);
7572 struct vnop_ioctl_args
/* {
7573 * struct vnodeop_desc *a_desc;
7575 * u_int32_t a_command;
7578 * vfs_context_t a_context;
7581 vfs_context_t ctx
= ap
->a_context
;
7582 vnode_t vp
= ap
->a_vp
;
7583 struct nfsmount
*mp
= VTONMP(vp
);
7586 struct user_nfs_gss_principal gprinc
= {};
7593 switch (ap
->a_command
) {
7595 if (vnode_vfsisrdonly(vp
)) {
7598 error
= nfs_flush(VTONFS(vp
), MNT_WAIT
, vfs_context_thread(ctx
), 0);
7601 case NFS_IOC_DESTROY_CRED
:
7602 if (!auth_is_kerberized(mp
->nm_auth
)) {
7605 error
= nfs_gss_clnt_ctx_remove(mp
, vfs_context_ucred(ctx
));
7607 case NFS_IOC_SET_CRED
:
7608 case NFS_IOC_SET_CRED64
:
7609 if (!auth_is_kerberized(mp
->nm_auth
)) {
7612 if ((ap
->a_command
== NFS_IOC_SET_CRED
&& vfs_context_is64bit(ctx
)) ||
7613 (ap
->a_command
== NFS_IOC_SET_CRED64
&& !vfs_context_is64bit(ctx
))) {
7616 if (vfs_context_is64bit(ctx
)) {
7617 gprinc
= *(struct user_nfs_gss_principal
*)ap
->a_data
;
7619 struct nfs_gss_principal
*tp
;
7620 tp
= (struct nfs_gss_principal
*)ap
->a_data
;
7621 gprinc
.princlen
= tp
->princlen
;
7622 gprinc
.nametype
= tp
->nametype
;
7623 gprinc
.principal
= CAST_USER_ADDR_T(tp
->principal
);
7625 NFS_DBG(NFS_FAC_GSS
, 7, "Enter NFS_FSCTL_SET_CRED (64-bit=%d): principal length %d name type %d usr pointer 0x%llx\n", vfs_context_is64bit(ctx
), gprinc
.princlen
, gprinc
.nametype
, (unsigned long long)gprinc
.principal
);
7626 if (gprinc
.princlen
> MAXPATHLEN
) {
7630 MALLOC(p
, uint8_t *, gprinc
.princlen
+ 1, M_TEMP
, M_WAITOK
| M_ZERO
);
7634 error
= copyin(gprinc
.principal
, p
, gprinc
.princlen
);
7636 NFS_DBG(NFS_FAC_GSS
, 7, "NFS_FSCTL_SET_CRED could not copy in princiapl data of len %d: %d\n",
7637 gprinc
.princlen
, error
);
7641 NFS_DBG(NFS_FAC_GSS
, 7, "Seting credential to principal %s\n", p
);
7642 error
= nfs_gss_clnt_ctx_set_principal(mp
, ctx
, p
, gprinc
.princlen
, gprinc
.nametype
);
7643 NFS_DBG(NFS_FAC_GSS
, 7, "Seting credential to principal %s returned %d\n", p
, error
);
7646 case NFS_IOC_GET_CRED
:
7647 case NFS_IOC_GET_CRED64
:
7648 if (!auth_is_kerberized(mp
->nm_auth
)) {
7651 if ((ap
->a_command
== NFS_IOC_GET_CRED
&& vfs_context_is64bit(ctx
)) ||
7652 (ap
->a_command
== NFS_IOC_GET_CRED64
&& !vfs_context_is64bit(ctx
))) {
7655 error
= nfs_gss_clnt_ctx_get_principal(mp
, ctx
, &gprinc
);
7659 if (vfs_context_is64bit(ctx
)) {
7660 struct user_nfs_gss_principal
*upp
= (struct user_nfs_gss_principal
*)ap
->a_data
;
7661 len
= upp
->princlen
;
7662 if (gprinc
.princlen
< len
) {
7663 len
= gprinc
.princlen
;
7665 upp
->princlen
= gprinc
.princlen
;
7666 upp
->nametype
= gprinc
.nametype
;
7667 upp
->flags
= gprinc
.flags
;
7668 if (gprinc
.principal
) {
7669 error
= copyout((void *)gprinc
.principal
, upp
->principal
, len
);
7671 upp
->principal
= USER_ADDR_NULL
;
7674 struct nfs_gss_principal
*u32pp
= (struct nfs_gss_principal
*)ap
->a_data
;
7675 len
= u32pp
->princlen
;
7676 if (gprinc
.princlen
< len
) {
7677 len
= gprinc
.princlen
;
7679 u32pp
->princlen
= gprinc
.princlen
;
7680 u32pp
->nametype
= gprinc
.nametype
;
7681 u32pp
->flags
= gprinc
.flags
;
7682 if (gprinc
.principal
) {
7683 error
= copyout((void *)gprinc
.principal
, u32pp
->principal
, len
);
7685 u32pp
->principal
= (user32_addr_t
)0;
7689 NFS_DBG(NFS_FAC_GSS
, 7, "NFS_FSCTL_GET_CRED could not copy out princiapl data of len %d: %d\n",
7690 gprinc
.princlen
, error
);
7692 if (gprinc
.principal
) {
7693 FREE(gprinc
.principal
, M_TEMP
);
7695 #endif /* CONFIG_NFS_GSS */
7704 __unused
struct vnop_select_args
/* {
7705 * struct vnodeop_desc *a_desc;
7710 * vfs_context_t a_context;
7714 * We were once bogusly seltrue() which returns 1. Is this right?
7720 * vnode OP for pagein using UPL
7722 * No buffer I/O, just RPCs straight into the mapped pages.
7726 struct vnop_pagein_args
/* {
7727 * struct vnodeop_desc *a_desc;
7730 * vm_offset_t a_pl_offset;
7734 * vfs_context_t a_context;
7737 vnode_t vp
= ap
->a_vp
;
7738 upl_t pl
= ap
->a_pl
;
7739 size_t size
= ap
->a_size
;
7740 off_t f_offset
= ap
->a_f_offset
;
7741 vm_offset_t pl_offset
= ap
->a_pl_offset
;
7742 int flags
= ap
->a_flags
;
7745 nfsnode_t np
= VTONFS(vp
);
7746 size_t nmrsize
, iosize
, txsize
, rxsize
, retsize
;
7748 struct nfsmount
*nmp
;
7750 vm_offset_t ioaddr
, rxaddr
;
7752 char uio_buf
[UIO_SIZEOF(1)];
7753 int nofreeupl
= flags
& UPL_NOCOMMIT
;
7754 upl_page_info_t
*plinfo
;
7755 #define MAXPAGINGREQS 16 /* max outstanding RPCs for pagein/pageout */
7756 struct nfsreq
*req
[MAXPAGINGREQS
];
7757 int nextsend
, nextwait
;
7759 uint32_t stategenid
= 0;
7761 uint32_t restart
= 0;
7764 FSDBG(322, np
, f_offset
, size
, flags
);
7765 if (pl
== (upl_t
)NULL
) {
7766 panic("nfs_pagein: no upl");
7770 printf("nfs_pagein: invalid size %ld", size
);
7772 (void) ubc_upl_abort_range(pl
, pl_offset
, size
, 0);
7776 if (f_offset
< 0 || f_offset
>= (off_t
)np
->n_size
|| (f_offset
& PAGE_MASK_64
)) {
7778 ubc_upl_abort_range(pl
, pl_offset
, size
,
7779 UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
7784 thd
= vfs_context_thread(ap
->a_context
);
7785 cred
= ubc_getcred(vp
);
7786 if (!IS_VALID_CRED(cred
)) {
7787 cred
= vfs_context_ucred(ap
->a_context
);
7790 uio
= uio_createwithbuffer(1, f_offset
, UIO_SYSSPACE
, UIO_READ
,
7791 &uio_buf
, sizeof(uio_buf
));
7794 if (nfs_mount_gone(nmp
)) {
7796 ubc_upl_abort_range(pl
, pl_offset
, size
,
7797 UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
7801 nmrsize
= nmp
->nm_rsize
;
7803 plinfo
= ubc_upl_pageinfo(pl
);
7804 kret
= ubc_upl_map(pl
, &ioaddr
);
7805 if (kret
!= KERN_SUCCESS
) {
7806 panic("nfs_vnop_pagein: ubc_upl_map() failed with (%d)", kret
);
7808 ioaddr
+= pl_offset
;
7812 if (nmp
->nm_vers
>= NFS_VER4
) {
7813 stategenid
= nmp
->nm_stategenid
;
7816 txsize
= rxsize
= size
;
7817 txoffset
= f_offset
;
7820 bzero(req
, sizeof(req
));
7821 nextsend
= nextwait
= 0;
7823 if (np
->n_flag
& NREVOKE
) {
7827 /* send requests while we need to and have available slots */
7828 while ((txsize
> 0) && (req
[nextsend
] == NULL
)) {
7829 iosize
= MIN(nmrsize
, txsize
);
7830 if ((error
= nmp
->nm_funcs
->nf_read_rpc_async(np
, txoffset
, iosize
, thd
, cred
, NULL
, &req
[nextsend
]))) {
7831 req
[nextsend
] = NULL
;
7836 nextsend
= (nextsend
+ 1) % MAXPAGINGREQS
;
7838 /* wait while we need to and break out if more requests to send */
7839 while ((rxsize
> 0) && req
[nextwait
]) {
7840 iosize
= retsize
= MIN(nmrsize
, rxsize
);
7841 uio_reset(uio
, uio_offset(uio
), UIO_SYSSPACE
, UIO_READ
);
7842 uio_addiov(uio
, CAST_USER_ADDR_T(rxaddr
), iosize
);
7843 FSDBG(322, uio_offset(uio
), uio_resid(uio
), rxaddr
, rxsize
);
7845 upl_ubc_alias_set(pl
, (uintptr_t) current_thread(), (uintptr_t) 2);
7846 #endif /* UPL_DEBUG */
7847 OSAddAtomic64(1, &nfsstats
.pageins
);
7848 error
= nmp
->nm_funcs
->nf_read_rpc_async_finish(np
, req
[nextwait
], uio
, &retsize
, NULL
);
7849 req
[nextwait
] = NULL
;
7850 nextwait
= (nextwait
+ 1) % MAXPAGINGREQS
;
7852 if ((nmp
->nm_vers
>= NFS_VER4
) && nfs_mount_state_error_should_restart(error
)) {
7853 lck_mtx_lock(&nmp
->nm_lock
);
7854 if ((error
!= NFSERR_GRACE
) && (stategenid
== nmp
->nm_stategenid
)) {
7855 NP(np
, "nfs_vnop_pagein: error %d, initiating recovery", error
);
7856 nfs_need_recover(nmp
, error
);
7858 lck_mtx_unlock(&nmp
->nm_lock
);
7864 FSDBG(322, uio_offset(uio
), uio_resid(uio
), error
, -1);
7867 if (retsize
< iosize
) {
7868 /* Just zero fill the rest of the valid area. */
7869 int zcnt
= iosize
- retsize
;
7870 bzero((char *)rxaddr
+ retsize
, zcnt
);
7871 FSDBG(324, uio_offset(uio
), retsize
, zcnt
, rxaddr
);
7872 uio_update(uio
, zcnt
);
7880 } while (!error
&& (txsize
|| rxsize
));
7888 /* cancel any outstanding requests */
7889 while (req
[nextwait
]) {
7890 nfs_request_async_cancel(req
[nextwait
]);
7891 req
[nextwait
] = NULL
;
7892 nextwait
= (nextwait
+ 1) % MAXPAGINGREQS
;
7894 if (np
->n_flag
& NREVOKE
) {
7896 } else if (restart
) {
7897 if (restart
<= nfs_mount_state_max_restarts(nmp
)) { /* guard against no progress */
7898 if (error
== NFSERR_GRACE
) {
7899 tsleep(&nmp
->nm_state
, (PZERO
- 1), "nfsgrace", 2 * hz
);
7901 if (!(error
= nfs_mount_state_wait_for_recovery(nmp
))) {
7905 NP(np
, "nfs_pagein: too many restarts, aborting");
7914 ubc_upl_abort_range(pl
, pl_offset
, size
,
7916 UPL_ABORT_FREE_ON_EMPTY
);
7918 ubc_upl_commit_range(pl
, pl_offset
, size
,
7919 UPL_COMMIT_CLEAR_DIRTY
|
7920 UPL_COMMIT_FREE_ON_EMPTY
);
7928 * the following are needed only by nfs_pageout to know how to handle errors
7929 * see nfs_pageout comments on explanation of actions.
7930 * the errors here are copied from errno.h and errors returned by servers
7931 * are expected to match the same numbers here. If not, our actions maybe
7934 char nfs_pageouterrorhandler(int);
7935 enum actiontype
{NOACTION
, DUMP
, DUMPANDLOG
, RETRY
, SEVER
};
7936 #define NFS_ELAST 88
7937 static u_char errorcount
[NFS_ELAST
+ 1]; /* better be zeros when initialized */
7938 static const char errortooutcome
[NFS_ELAST
+ 1] = {
7940 DUMP
, /* EPERM 1 Operation not permitted */
7941 DUMP
, /* ENOENT 2 No such file or directory */
7942 DUMPANDLOG
, /* ESRCH 3 No such process */
7943 RETRY
, /* EINTR 4 Interrupted system call */
7944 DUMP
, /* EIO 5 Input/output error */
7945 DUMP
, /* ENXIO 6 Device not configured */
7946 DUMPANDLOG
, /* E2BIG 7 Argument list too long */
7947 DUMPANDLOG
, /* ENOEXEC 8 Exec format error */
7948 DUMPANDLOG
, /* EBADF 9 Bad file descriptor */
7949 DUMPANDLOG
, /* ECHILD 10 No child processes */
7950 DUMPANDLOG
, /* EDEADLK 11 Resource deadlock avoided - was EAGAIN */
7951 RETRY
, /* ENOMEM 12 Cannot allocate memory */
7952 DUMP
, /* EACCES 13 Permission denied */
7953 DUMPANDLOG
, /* EFAULT 14 Bad address */
7954 DUMPANDLOG
, /* ENOTBLK 15 POSIX - Block device required */
7955 RETRY
, /* EBUSY 16 Device busy */
7956 DUMP
, /* EEXIST 17 File exists */
7957 DUMP
, /* EXDEV 18 Cross-device link */
7958 DUMP
, /* ENODEV 19 Operation not supported by device */
7959 DUMP
, /* ENOTDIR 20 Not a directory */
7960 DUMP
, /* EISDIR 21 Is a directory */
7961 DUMP
, /* EINVAL 22 Invalid argument */
7962 DUMPANDLOG
, /* ENFILE 23 Too many open files in system */
7963 DUMPANDLOG
, /* EMFILE 24 Too many open files */
7964 DUMPANDLOG
, /* ENOTTY 25 Inappropriate ioctl for device */
7965 DUMPANDLOG
, /* ETXTBSY 26 Text file busy - POSIX */
7966 DUMP
, /* EFBIG 27 File too large */
7967 DUMP
, /* ENOSPC 28 No space left on device */
7968 DUMPANDLOG
, /* ESPIPE 29 Illegal seek */
7969 DUMP
, /* EROFS 30 Read-only file system */
7970 DUMP
, /* EMLINK 31 Too many links */
7971 RETRY
, /* EPIPE 32 Broken pipe */
7973 DUMPANDLOG
, /* EDOM 33 Numerical argument out of domain */
7974 DUMPANDLOG
, /* ERANGE 34 Result too large */
7975 RETRY
, /* EAGAIN/EWOULDBLOCK 35 Resource temporarily unavailable */
7976 DUMPANDLOG
, /* EINPROGRESS 36 Operation now in progress */
7977 DUMPANDLOG
, /* EALREADY 37 Operation already in progress */
7978 /* ipc/network software -- argument errors */
7979 DUMPANDLOG
, /* ENOTSOC 38 Socket operation on non-socket */
7980 DUMPANDLOG
, /* EDESTADDRREQ 39 Destination address required */
7981 DUMPANDLOG
, /* EMSGSIZE 40 Message too long */
7982 DUMPANDLOG
, /* EPROTOTYPE 41 Protocol wrong type for socket */
7983 DUMPANDLOG
, /* ENOPROTOOPT 42 Protocol not available */
7984 DUMPANDLOG
, /* EPROTONOSUPPORT 43 Protocol not supported */
7985 DUMPANDLOG
, /* ESOCKTNOSUPPORT 44 Socket type not supported */
7986 DUMPANDLOG
, /* ENOTSUP 45 Operation not supported */
7987 DUMPANDLOG
, /* EPFNOSUPPORT 46 Protocol family not supported */
7988 DUMPANDLOG
, /* EAFNOSUPPORT 47 Address family not supported by protocol family */
7989 DUMPANDLOG
, /* EADDRINUSE 48 Address already in use */
7990 DUMPANDLOG
, /* EADDRNOTAVAIL 49 Can't assign requested address */
7991 /* ipc/network software -- operational errors */
7992 RETRY
, /* ENETDOWN 50 Network is down */
7993 RETRY
, /* ENETUNREACH 51 Network is unreachable */
7994 RETRY
, /* ENETRESET 52 Network dropped connection on reset */
7995 RETRY
, /* ECONNABORTED 53 Software caused connection abort */
7996 RETRY
, /* ECONNRESET 54 Connection reset by peer */
7997 RETRY
, /* ENOBUFS 55 No buffer space available */
7998 RETRY
, /* EISCONN 56 Socket is already connected */
7999 RETRY
, /* ENOTCONN 57 Socket is not connected */
8000 RETRY
, /* ESHUTDOWN 58 Can't send after socket shutdown */
8001 RETRY
, /* ETOOMANYREFS 59 Too many references: can't splice */
8002 RETRY
, /* ETIMEDOUT 60 Operation timed out */
8003 RETRY
, /* ECONNREFUSED 61 Connection refused */
8005 DUMPANDLOG
, /* ELOOP 62 Too many levels of symbolic links */
8006 DUMP
, /* ENAMETOOLONG 63 File name too long */
8007 RETRY
, /* EHOSTDOWN 64 Host is down */
8008 RETRY
, /* EHOSTUNREACH 65 No route to host */
8009 DUMP
, /* ENOTEMPTY 66 Directory not empty */
8011 DUMPANDLOG
, /* PROCLIM 67 Too many processes */
8012 DUMPANDLOG
, /* EUSERS 68 Too many users */
8013 DUMPANDLOG
, /* EDQUOT 69 Disc quota exceeded */
8014 /* Network File System */
8015 DUMP
, /* ESTALE 70 Stale NFS file handle */
8016 DUMP
, /* EREMOTE 71 Too many levels of remote in path */
8017 DUMPANDLOG
, /* EBADRPC 72 RPC struct is bad */
8018 DUMPANDLOG
, /* ERPCMISMATCH 73 RPC version wrong */
8019 DUMPANDLOG
, /* EPROGUNAVAIL 74 RPC prog. not avail */
8020 DUMPANDLOG
, /* EPROGMISMATCH 75 Program version wrong */
8021 DUMPANDLOG
, /* EPROCUNAVAIL 76 Bad procedure for program */
8023 DUMPANDLOG
, /* ENOLCK 77 No locks available */
8024 DUMPANDLOG
, /* ENOSYS 78 Function not implemented */
8025 DUMPANDLOG
, /* EFTYPE 79 Inappropriate file type or format */
8026 DUMPANDLOG
, /* EAUTH 80 Authentication error */
8027 DUMPANDLOG
, /* ENEEDAUTH 81 Need authenticator */
8028 /* Intelligent device errors */
8029 DUMPANDLOG
, /* EPWROFF 82 Device power is off */
8030 DUMPANDLOG
, /* EDEVERR 83 Device error, e.g. paper out */
8031 DUMPANDLOG
, /* EOVERFLOW 84 Value too large to be stored in data type */
8032 /* Program loading errors */
8033 DUMPANDLOG
, /* EBADEXEC 85 Bad executable */
8034 DUMPANDLOG
, /* EBADARCH 86 Bad CPU type in executable */
8035 DUMPANDLOG
, /* ESHLIBVERS 87 Shared library version mismatch */
8036 DUMPANDLOG
, /* EBADMACHO 88 Malformed Macho file */
8040 nfs_pageouterrorhandler(int error
)
8042 if (error
> NFS_ELAST
) {
8045 return errortooutcome
[error
];
8051 * vnode OP for pageout using UPL
8053 * No buffer I/O, just RPCs straight from the mapped pages.
8054 * File size changes are not permitted in pageout.
8058 struct vnop_pageout_args
/* {
8059 * struct vnodeop_desc *a_desc;
8062 * vm_offset_t a_pl_offset;
8066 * vfs_context_t a_context;
8069 vnode_t vp
= ap
->a_vp
;
8070 upl_t pl
= ap
->a_pl
;
8071 size_t size
= ap
->a_size
;
8072 off_t f_offset
= ap
->a_f_offset
;
8073 vm_offset_t pl_offset
= ap
->a_pl_offset
;
8074 int flags
= ap
->a_flags
;
8075 nfsnode_t np
= VTONFS(vp
);
8079 struct nfsmount
*nmp
= VTONMP(vp
);
8081 int error
= 0, iomode
;
8082 off_t off
, txoffset
, rxoffset
;
8083 vm_offset_t ioaddr
, txaddr
, rxaddr
;
8085 char uio_buf
[UIO_SIZEOF(1)];
8086 int nofreeupl
= flags
& UPL_NOCOMMIT
;
8087 size_t nmwsize
, biosize
, iosize
, pgsize
, txsize
, rxsize
, xsize
, remsize
;
8088 struct nfsreq
*req
[MAXPAGINGREQS
];
8089 int nextsend
, nextwait
, wverfset
, commit
;
8090 uint64_t wverf
, wverf2
;
8092 uint32_t stategenid
= 0;
8094 uint32_t vrestart
= 0, restart
= 0, vrestarts
= 0, restarts
= 0;
8097 FSDBG(323, f_offset
, size
, pl
, pl_offset
);
8099 if (pl
== (upl_t
)NULL
) {
8100 panic("nfs_pageout: no upl");
8104 printf("nfs_pageout: invalid size %ld", size
);
8106 ubc_upl_abort_range(pl
, pl_offset
, size
, 0);
8113 ubc_upl_abort(pl
, UPL_ABORT_DUMP_PAGES
| UPL_ABORT_FREE_ON_EMPTY
);
8117 biosize
= nmp
->nm_biosize
;
8118 nmwsize
= nmp
->nm_wsize
;
8120 nfs_data_lock_noupdate(np
, NFS_DATA_LOCK_SHARED
);
8123 * Check to see whether the buffer is incore.
8124 * If incore and not busy, invalidate it from the cache.
8126 for (iosize
= 0; iosize
< size
; iosize
+= xsize
) {
8127 off
= f_offset
+ iosize
;
8128 /* need make sure we do things on block boundaries */
8129 xsize
= biosize
- (off
% biosize
);
8130 if (off
+ xsize
> f_offset
+ size
) {
8131 xsize
= f_offset
+ size
- off
;
8133 lbn
= (daddr64_t
)(off
/ biosize
);
8134 lck_mtx_lock(nfs_buf_mutex
);
8135 if ((bp
= nfs_buf_incore(np
, lbn
))) {
8136 FSDBG(323, off
, bp
, bp
->nb_lflags
, bp
->nb_flags
);
8137 if (nfs_buf_acquire(bp
, NBAC_NOWAIT
, 0, 0)) {
8138 lck_mtx_unlock(nfs_buf_mutex
);
8139 nfs_data_unlock_noupdate(np
);
8140 /* no panic. just tell vm we are busy */
8142 ubc_upl_abort_range(pl
, pl_offset
, size
, 0);
8146 if (bp
->nb_dirtyend
> 0) {
8148 * if there's a dirty range in the buffer, check
8149 * to see if it extends beyond the pageout region
8151 * if the dirty region lies completely within the
8152 * pageout region, we just invalidate the buffer
8153 * because it's all being written out now anyway.
8155 * if any of the dirty region lies outside the
8156 * pageout region, we'll try to clip the dirty
8157 * region to eliminate the portion that's being
8158 * paged out. If that's not possible, because
8159 * the dirty region extends before and after the
8160 * pageout region, then we'll just return EBUSY.
8162 off_t boff
, start
, end
;
8166 /* clip end to EOF */
8167 if (end
> (off_t
)np
->n_size
) {
8172 if ((bp
->nb_dirtyoff
< start
) &&
8173 (bp
->nb_dirtyend
> end
)) {
8175 * not gonna be able to clip the dirty region
8177 * But before returning the bad news, move the
8178 * buffer to the start of the delwri list and
8179 * give the list a push to try to flush the
8182 FSDBG(323, np
, bp
, 0xd00deebc, EBUSY
);
8183 nfs_buf_remfree(bp
);
8184 TAILQ_INSERT_HEAD(&nfsbufdelwri
, bp
, nb_free
);
8187 nfs_buf_delwri_push(1);
8188 lck_mtx_unlock(nfs_buf_mutex
);
8189 nfs_data_unlock_noupdate(np
);
8191 ubc_upl_abort_range(pl
, pl_offset
, size
, 0);
8195 if ((bp
->nb_dirtyoff
< start
) ||
8196 (bp
->nb_dirtyend
> end
)) {
8197 /* clip dirty region, if necessary */
8198 if (bp
->nb_dirtyoff
< start
) {
8199 bp
->nb_dirtyend
= min(bp
->nb_dirtyend
, start
);
8201 if (bp
->nb_dirtyend
> end
) {
8202 bp
->nb_dirtyoff
= max(bp
->nb_dirtyoff
, end
);
8204 FSDBG(323, bp
, bp
->nb_dirtyoff
, bp
->nb_dirtyend
, 0xd00dee00);
8205 /* we're leaving this block dirty */
8207 lck_mtx_unlock(nfs_buf_mutex
);
8211 nfs_buf_remfree(bp
);
8212 lck_mtx_unlock(nfs_buf_mutex
);
8213 SET(bp
->nb_flags
, NB_INVAL
);
8214 nfs_node_lock_force(np
);
8215 if (ISSET(bp
->nb_flags
, NB_NEEDCOMMIT
)) {
8216 CLR(bp
->nb_flags
, NB_NEEDCOMMIT
);
8217 np
->n_needcommitcnt
--;
8218 CHECK_NEEDCOMMITCNT(np
);
8220 nfs_node_unlock(np
);
8221 nfs_buf_release(bp
, 1);
8223 lck_mtx_unlock(nfs_buf_mutex
);
8227 thd
= vfs_context_thread(ap
->a_context
);
8228 cred
= ubc_getcred(vp
);
8229 if (!IS_VALID_CRED(cred
)) {
8230 cred
= vfs_context_ucred(ap
->a_context
);
8233 nfs_node_lock_force(np
);
8234 if (np
->n_flag
& NWRITEERR
) {
8235 error
= np
->n_error
;
8236 nfs_node_unlock(np
);
8237 nfs_data_unlock_noupdate(np
);
8239 ubc_upl_abort_range(pl
, pl_offset
, size
,
8240 UPL_ABORT_FREE_ON_EMPTY
);
8244 nfs_node_unlock(np
);
8246 if (f_offset
< 0 || f_offset
>= (off_t
)np
->n_size
||
8247 f_offset
& PAGE_MASK_64
|| size
& PAGE_MASK_64
) {
8248 nfs_data_unlock_noupdate(np
);
8250 ubc_upl_abort_range(pl
, pl_offset
, size
,
8251 UPL_ABORT_FREE_ON_EMPTY
);
8256 kret
= ubc_upl_map(pl
, &ioaddr
);
8257 if (kret
!= KERN_SUCCESS
) {
8258 panic("nfs_vnop_pageout: ubc_upl_map() failed with (%d)", kret
);
8260 ioaddr
+= pl_offset
;
8262 if ((u_quad_t
)f_offset
+ size
> np
->n_size
) {
8263 xsize
= np
->n_size
- f_offset
;
8268 pgsize
= round_page_64(xsize
);
8269 if ((size
> pgsize
) && !nofreeupl
) {
8270 ubc_upl_abort_range(pl
, pl_offset
+ pgsize
, size
- pgsize
,
8271 UPL_ABORT_FREE_ON_EMPTY
);
8275 * check for partial page and clear the
8276 * contents past end of the file before
8277 * releasing it in the VM page cache
8279 if ((u_quad_t
)f_offset
< np
->n_size
&& (u_quad_t
)f_offset
+ size
> np
->n_size
) {
8280 size_t io
= np
->n_size
- f_offset
;
8281 bzero((caddr_t
)(ioaddr
+ io
), size
- io
);
8282 FSDBG(321, np
->n_size
, f_offset
, f_offset
+ io
, size
- io
);
8284 nfs_data_unlock_noupdate(np
);
8286 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_WRITE
,
8287 &uio_buf
, sizeof(uio_buf
));
8291 if (nmp
->nm_vers
>= NFS_VER4
) {
8292 stategenid
= nmp
->nm_stategenid
;
8295 wverf
= wverf2
= wverfset
= 0;
8296 txsize
= rxsize
= xsize
;
8297 txoffset
= rxoffset
= f_offset
;
8298 txaddr
= rxaddr
= ioaddr
;
8299 commit
= NFS_WRITE_FILESYNC
;
8301 bzero(req
, sizeof(req
));
8302 nextsend
= nextwait
= 0;
8304 if (np
->n_flag
& NREVOKE
) {
8308 /* send requests while we need to and have available slots */
8309 while ((txsize
> 0) && (req
[nextsend
] == NULL
)) {
8310 iosize
= MIN(nmwsize
, txsize
);
8311 uio_reset(auio
, txoffset
, UIO_SYSSPACE
, UIO_WRITE
);
8312 uio_addiov(auio
, CAST_USER_ADDR_T(txaddr
), iosize
);
8313 FSDBG(323, uio_offset(auio
), iosize
, txaddr
, txsize
);
8314 OSAddAtomic64(1, &nfsstats
.pageouts
);
8315 nfs_node_lock_force(np
);
8317 nfs_node_unlock(np
);
8318 vnode_startwrite(vp
);
8319 iomode
= NFS_WRITE_UNSTABLE
;
8320 if ((error
= nmp
->nm_funcs
->nf_write_rpc_async(np
, auio
, iosize
, thd
, cred
, iomode
, NULL
, &req
[nextsend
]))) {
8321 req
[nextsend
] = NULL
;
8322 vnode_writedone(vp
);
8323 nfs_node_lock_force(np
);
8325 nfs_node_unlock(np
);
8331 nextsend
= (nextsend
+ 1) % MAXPAGINGREQS
;
8333 /* wait while we need to and break out if more requests to send */
8334 while ((rxsize
> 0) && req
[nextwait
]) {
8335 iosize
= remsize
= MIN(nmwsize
, rxsize
);
8336 error
= nmp
->nm_funcs
->nf_write_rpc_async_finish(np
, req
[nextwait
], &iomode
, &iosize
, &wverf2
);
8337 req
[nextwait
] = NULL
;
8338 nextwait
= (nextwait
+ 1) % MAXPAGINGREQS
;
8339 vnode_writedone(vp
);
8340 nfs_node_lock_force(np
);
8342 nfs_node_unlock(np
);
8344 if ((nmp
->nm_vers
>= NFS_VER4
) && nfs_mount_state_error_should_restart(error
)) {
8345 lck_mtx_lock(&nmp
->nm_lock
);
8346 if ((error
!= NFSERR_GRACE
) && (stategenid
== nmp
->nm_stategenid
)) {
8347 NP(np
, "nfs_vnop_pageout: error %d, initiating recovery", error
);
8348 nfs_need_recover(nmp
, error
);
8350 lck_mtx_unlock(&nmp
->nm_lock
);
8356 FSDBG(323, rxoffset
, rxsize
, error
, -1);
8362 } else if (wverf
!= wverf2
) {
8363 /* verifier changed, so we need to restart all the writes */
8367 /* Retain the lowest commitment level returned. */
8368 if (iomode
< commit
) {
8376 /* need to try sending the remainder */
8378 uio_reset(auio
, rxoffset
, UIO_SYSSPACE
, UIO_WRITE
);
8379 uio_addiov(auio
, CAST_USER_ADDR_T(rxaddr
), remsize
);
8380 iomode
= NFS_WRITE_UNSTABLE
;
8381 error
= nfs_write_rpc2(np
, auio
, thd
, cred
, &iomode
, &wverf2
);
8383 if ((nmp
->nm_vers
>= NFS_VER4
) && nfs_mount_state_error_should_restart(error
)) {
8384 NP(np
, "nfs_vnop_pageout: restart: error %d", error
);
8385 lck_mtx_lock(&nmp
->nm_lock
);
8386 if ((error
!= NFSERR_GRACE
) && (stategenid
== nmp
->nm_stategenid
)) {
8387 NP(np
, "nfs_vnop_pageout: error %d, initiating recovery", error
);
8388 nfs_need_recover(nmp
, error
);
8390 lck_mtx_unlock(&nmp
->nm_lock
);
8396 FSDBG(323, rxoffset
, rxsize
, error
, -1);
8399 if (wverf
!= wverf2
) {
8400 /* verifier changed, so we need to restart all the writes */
8404 if (iomode
< commit
) {
8415 } while (!error
&& (txsize
|| rxsize
));
8419 if (!error
&& (commit
!= NFS_WRITE_FILESYNC
)) {
8420 error
= nmp
->nm_funcs
->nf_commit_rpc(np
, f_offset
, xsize
, cred
, wverf
);
8421 if (error
== NFSERR_STALEWRITEVERF
) {
8429 /* cancel any outstanding requests */
8430 while (req
[nextwait
]) {
8431 nfs_request_async_cancel(req
[nextwait
]);
8432 req
[nextwait
] = NULL
;
8433 nextwait
= (nextwait
+ 1) % MAXPAGINGREQS
;
8434 vnode_writedone(vp
);
8435 nfs_node_lock_force(np
);
8437 nfs_node_unlock(np
);
8439 if (np
->n_flag
& NREVOKE
) {
8443 if (++vrestarts
<= 100) { /* guard against no progress */
8446 NP(np
, "nfs_pageout: too many restarts, aborting");
8447 FSDBG(323, f_offset
, xsize
, ERESTART
, -1);
8450 if (restarts
<= nfs_mount_state_max_restarts(nmp
)) { /* guard against no progress */
8451 if (error
== NFSERR_GRACE
) {
8452 tsleep(&nmp
->nm_state
, (PZERO
- 1), "nfsgrace", 2 * hz
);
8454 if (!(error
= nfs_mount_state_wait_for_recovery(nmp
))) {
8458 NP(np
, "nfs_pageout: too many restarts, aborting");
8459 FSDBG(323, f_offset
, xsize
, ERESTART
, -1);
8468 * We've had several different solutions on what to do when the pageout
8469 * gets an error. If we don't handle it, and return an error to the
8470 * caller, vm, it will retry . This can end in endless looping
8471 * between vm and here doing retries of the same page. Doing a dump
8472 * back to vm, will get it out of vm's knowledge and we lose whatever
8473 * data existed. This is risky, but in some cases necessary. For
8474 * example, the initial fix here was to do that for ESTALE. In that case
8475 * the server is telling us that the file is no longer the same. We
8476 * would not want to keep paging out to that. We also saw some 151
8477 * errors from Auspex server and NFSv3 can return errors higher than
8478 * ELAST. Those along with NFS known server errors we will "dump" from
8479 * vm. Errors we don't expect to occur, we dump and log for further
8480 * analysis. Errors that could be transient, networking ones,
8481 * we let vm "retry". Lastly, errors that we retry, but may have potential
8482 * to storm the network, we "retrywithsleep". "sever" will be used in
8483 * in the future to dump all pages of object for cases like ESTALE.
8484 * All this is the basis for the states returned and first guesses on
8485 * error handling. Tweaking expected as more statistics are gathered.
8486 * Note, in the long run we may need another more robust solution to
8487 * have some kind of persistant store when the vm cannot dump nor keep
8488 * retrying as a solution, but this would be a file architectural change
8490 if (!nofreeupl
) { /* otherwise stacked file system has to handle this */
8493 char action
= nfs_pageouterrorhandler(error
);
8497 abortflags
= UPL_ABORT_DUMP_PAGES
| UPL_ABORT_FREE_ON_EMPTY
;
8500 abortflags
= UPL_ABORT_DUMP_PAGES
| UPL_ABORT_FREE_ON_EMPTY
;
8501 if (error
<= NFS_ELAST
) {
8502 if ((errorcount
[error
] % 100) == 0) {
8503 NP(np
, "nfs_pageout: unexpected error %d. dumping vm page", error
);
8505 errorcount
[error
]++;
8509 abortflags
= UPL_ABORT_FREE_ON_EMPTY
;
8511 case SEVER
: /* not implemented */
8513 NP(np
, "nfs_pageout: action %d not expected", action
);
8517 ubc_upl_abort_range(pl
, pl_offset
, pgsize
, abortflags
);
8518 /* return error in all cases above */
8520 ubc_upl_commit_range(pl
, pl_offset
, pgsize
,
8521 UPL_COMMIT_CLEAR_DIRTY
|
8522 UPL_COMMIT_FREE_ON_EMPTY
);
8528 /* Blktooff derives file offset given a logical block number */
8531 struct vnop_blktooff_args
/* {
8532 * struct vnodeop_desc *a_desc;
8534 * daddr64_t a_lblkno;
8539 vnode_t vp
= ap
->a_vp
;
8540 struct nfsmount
*nmp
= VTONMP(vp
);
8542 if (nfs_mount_gone(nmp
)) {
8545 biosize
= nmp
->nm_biosize
;
8547 *ap
->a_offset
= (off_t
)(ap
->a_lblkno
* biosize
);
8554 struct vnop_offtoblk_args
/* {
8555 * struct vnodeop_desc *a_desc;
8558 * daddr64_t *a_lblkno;
8562 vnode_t vp
= ap
->a_vp
;
8563 struct nfsmount
*nmp
= VTONMP(vp
);
8565 if (nfs_mount_gone(nmp
)) {
8568 biosize
= nmp
->nm_biosize
;
8570 *ap
->a_lblkno
= (daddr64_t
)(ap
->a_offset
/ biosize
);
8576 * vnode change monitoring
8580 struct vnop_monitor_args
/* {
8581 * struct vnodeop_desc *a_desc;
8583 * uint32_t a_events;
8586 * vfs_context_t a_context;
8589 nfsnode_t np
= VTONFS(ap
->a_vp
);
8590 struct nfsmount
*nmp
= VTONMP(ap
->a_vp
);
8593 if (nfs_mount_gone(nmp
)) {
8597 /* make sure that the vnode's monitoring status is up to date */
8598 lck_mtx_lock(&nmp
->nm_lock
);
8599 if (vnode_ismonitored(ap
->a_vp
)) {
8600 /* This vnode is currently being monitored, make sure we're tracking it. */
8601 if (np
->n_monlink
.le_next
== NFSNOLIST
) {
8602 LIST_INSERT_HEAD(&nmp
->nm_monlist
, np
, n_monlink
);
8603 nfs_mount_sock_thread_wake(nmp
);
8606 /* This vnode is no longer being monitored, make sure we're not tracking it. */
8607 /* Wait for any in-progress getattr to complete first. */
8608 while (np
->n_mflag
& NMMONSCANINPROG
) {
8609 struct timespec ts
= { .tv_sec
= 1, .tv_nsec
= 0 };
8610 np
->n_mflag
|= NMMONSCANWANT
;
8611 msleep(&np
->n_mflag
, &nmp
->nm_lock
, PZERO
- 1, "nfswaitmonscan", &ts
);
8613 if (np
->n_monlink
.le_next
!= NFSNOLIST
) {
8614 LIST_REMOVE(np
, n_monlink
);
8615 np
->n_monlink
.le_next
= NFSNOLIST
;
8618 lck_mtx_unlock(&nmp
->nm_lock
);
8624 * Send a vnode notification for the given events.
8627 nfs_vnode_notify(nfsnode_t np
, uint32_t events
)
8629 struct nfsmount
*nmp
= NFSTONMP(np
);
8630 struct nfs_vattr nvattr
;
8631 struct vnode_attr vattr
, *vap
= NULL
;
8635 if ((np
->n_evtstamp
== now
.tv_sec
) || !nmp
) {
8636 /* delay sending this notify */
8637 np
->n_events
|= events
;
8640 events
|= np
->n_events
;
8642 np
->n_evtstamp
= now
.tv_sec
;
8644 vfs_get_notify_attributes(&vattr
);
8645 if (!nfs_getattrcache(np
, &nvattr
, 0)) {
8649 VATTR_RETURN(vap
, va_fsid
, vfs_statfs(nmp
->nm_mountp
)->f_fsid
.val
[0]);
8650 VATTR_RETURN(vap
, va_fileid
, nvattr
.nva_fileid
);
8651 VATTR_RETURN(vap
, va_mode
, nvattr
.nva_mode
);
8652 VATTR_RETURN(vap
, va_uid
, nvattr
.nva_uid
);
8653 VATTR_RETURN(vap
, va_gid
, nvattr
.nva_gid
);
8654 VATTR_RETURN(vap
, va_nlink
, nvattr
.nva_nlink
);
8656 vnode_notify(NFSTOV(np
), events
, vap
);
8659 #endif /* CONFIG_NFS_CLIENT */