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