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