| @@ -1,3661 +0,0 @@ | | | @@ -1,3661 +0,0 @@ |
1 | /* $NetBSD: nfs_vnops.c,v 1.268 2008/11/19 18:36:10 ad Exp $ */ | | | |
2 | | | | |
3 | /* | | | |
4 | * Copyright (c) 1989, 1993 | | | |
5 | * The Regents of the University of California. All rights reserved. | | | |
6 | * | | | |
7 | * This code is derived from software contributed to Berkeley by | | | |
8 | * Rick Macklem at The University of Guelph. | | | |
9 | * | | | |
10 | * Redistribution and use in source and binary forms, with or without | | | |
11 | * modification, are permitted provided that the following conditions | | | |
12 | * are met: | | | |
13 | * 1. Redistributions of source code must retain the above copyright | | | |
14 | * notice, this list of conditions and the following disclaimer. | | | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | | |
16 | * notice, this list of conditions and the following disclaimer in the | | | |
17 | * documentation and/or other materials provided with the distribution. | | | |
18 | * 3. Neither the name of the University nor the names of its contributors | | | |
19 | * may be used to endorse or promote products derived from this software | | | |
20 | * without specific prior written permission. | | | |
21 | * | | | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | | |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | | |
32 | * SUCH DAMAGE. | | | |
33 | * | | | |
34 | * @(#)nfs_vnops.c 8.19 (Berkeley) 7/31/95 | | | |
35 | */ | | | |
36 | | | | |
37 | /* | | | |
38 | * vnode op calls for Sun NFS version 2 and 3 | | | |
39 | */ | | | |
40 | | | | |
41 | #include <sys/cdefs.h> | | | |
42 | __KERNEL_RCSID(0, "$NetBSD: nfs_vnops.c,v 1.268 2008/11/19 18:36:10 ad Exp $"); | | | |
43 | | | | |
44 | #ifdef _KERNEL_OPT | | | |
45 | #include "opt_nfs.h" | | | |
46 | #include "opt_uvmhist.h" | | | |
47 | #endif | | | |
48 | | | | |
49 | #include <sys/param.h> | | | |
50 | #include <sys/proc.h> | | | |
51 | #include <sys/kernel.h> | | | |
52 | #include <sys/systm.h> | | | |
53 | #include <sys/resourcevar.h> | | | |
54 | #include <sys/mount.h> | | | |
55 | #include <sys/buf.h> | | | |
56 | #include <sys/condvar.h> | | | |
57 | #include <sys/disk.h> | | | |
58 | #include <sys/malloc.h> | | | |
59 | #include <sys/kmem.h> | | | |
60 | #include <sys/mbuf.h> | | | |
61 | #include <sys/mutex.h> | | | |
62 | #include <sys/namei.h> | | | |
63 | #include <sys/vnode.h> | | | |
64 | #include <sys/dirent.h> | | | |
65 | #include <sys/fcntl.h> | | | |
66 | #include <sys/hash.h> | | | |
67 | #include <sys/lockf.h> | | | |
68 | #include <sys/stat.h> | | | |
69 | #include <sys/unistd.h> | | | |
70 | #include <sys/kauth.h> | | | |
71 | | | | |
72 | #include <uvm/uvm_extern.h> | | | |
73 | #include <uvm/uvm.h> | | | |
74 | | | | |
75 | #include <miscfs/fifofs/fifo.h> | | | |
76 | #include <miscfs/genfs/genfs.h> | | | |
77 | #include <miscfs/genfs/genfs_node.h> | | | |
78 | #include <miscfs/specfs/specdev.h> | | | |
79 | | | | |
80 | #include <nfs/rpcv2.h> | | | |
81 | #include <nfs/nfsproto.h> | | | |
82 | #include <nfs/nfs.h> | | | |
83 | #include <nfs/nfsnode.h> | | | |
84 | #include <nfs/nfsmount.h> | | | |
85 | #include <nfs/xdr_subs.h> | | | |
86 | #include <nfs/nfsm_subs.h> | | | |
87 | #include <nfs/nfs_var.h> | | | |
88 | | | | |
89 | #include <net/if.h> | | | |
90 | #include <netinet/in.h> | | | |
91 | #include <netinet/in_var.h> | | | |
92 | | | | |
93 | /* | | | |
94 | * Global vfs data structures for nfs | | | |
95 | */ | | | |
96 | int (**nfsv2_vnodeop_p) __P((void *)); | | | |
97 | const struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { | | | |
98 | { &vop_default_desc, vn_default_error }, | | | |
99 | { &vop_lookup_desc, nfs_lookup }, /* lookup */ | | | |
100 | { &vop_create_desc, nfs_create }, /* create */ | | | |
101 | { &vop_mknod_desc, nfs_mknod }, /* mknod */ | | | |
102 | { &vop_open_desc, nfs_open }, /* open */ | | | |
103 | { &vop_close_desc, nfs_close }, /* close */ | | | |
104 | { &vop_access_desc, nfs_access }, /* access */ | | | |
105 | { &vop_getattr_desc, nfs_getattr }, /* getattr */ | | | |
106 | { &vop_setattr_desc, nfs_setattr }, /* setattr */ | | | |
107 | { &vop_read_desc, nfs_read }, /* read */ | | | |
108 | { &vop_write_desc, nfs_write }, /* write */ | | | |
109 | { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ | | | |
110 | { &vop_ioctl_desc, nfs_ioctl }, /* ioctl */ | | | |
111 | { &vop_poll_desc, nfs_poll }, /* poll */ | | | |
112 | { &vop_kqfilter_desc, nfs_kqfilter }, /* kqfilter */ | | | |
113 | { &vop_revoke_desc, nfs_revoke }, /* revoke */ | | | |
114 | { &vop_mmap_desc, nfs_mmap }, /* mmap */ | | | |
115 | { &vop_fsync_desc, nfs_fsync }, /* fsync */ | | | |
116 | { &vop_seek_desc, nfs_seek }, /* seek */ | | | |
117 | { &vop_remove_desc, nfs_remove }, /* remove */ | | | |
118 | { &vop_link_desc, nfs_link }, /* link */ | | | |
119 | { &vop_rename_desc, nfs_rename }, /* rename */ | | | |
120 | { &vop_mkdir_desc, nfs_mkdir }, /* mkdir */ | | | |
121 | { &vop_rmdir_desc, nfs_rmdir }, /* rmdir */ | | | |
122 | { &vop_symlink_desc, nfs_symlink }, /* symlink */ | | | |
123 | { &vop_readdir_desc, nfs_readdir }, /* readdir */ | | | |
124 | { &vop_readlink_desc, nfs_readlink }, /* readlink */ | | | |
125 | { &vop_abortop_desc, nfs_abortop }, /* abortop */ | | | |
126 | { &vop_inactive_desc, nfs_inactive }, /* inactive */ | | | |
127 | { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ | | | |
128 | { &vop_lock_desc, nfs_lock }, /* lock */ | | | |
129 | { &vop_unlock_desc, nfs_unlock }, /* unlock */ | | | |
130 | { &vop_bmap_desc, nfs_bmap }, /* bmap */ | | | |
131 | { &vop_strategy_desc, nfs_strategy }, /* strategy */ | | | |
132 | { &vop_print_desc, nfs_print }, /* print */ | | | |
133 | { &vop_islocked_desc, nfs_islocked }, /* islocked */ | | | |
134 | { &vop_pathconf_desc, nfs_pathconf }, /* pathconf */ | | | |
135 | { &vop_advlock_desc, nfs_advlock }, /* advlock */ | | | |
136 | { &vop_bwrite_desc, genfs_badop }, /* bwrite */ | | | |
137 | { &vop_getpages_desc, nfs_getpages }, /* getpages */ | | | |
138 | { &vop_putpages_desc, genfs_putpages }, /* putpages */ | | | |
139 | { NULL, NULL } | | | |
140 | }; | | | |
141 | const struct vnodeopv_desc nfsv2_vnodeop_opv_desc = | | | |
142 | { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries }; | | | |
143 | | | | |
144 | /* | | | |
145 | * Special device vnode ops | | | |
146 | */ | | | |
147 | int (**spec_nfsv2nodeop_p) __P((void *)); | | | |
148 | const struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { | | | |
149 | { &vop_default_desc, vn_default_error }, | | | |
150 | { &vop_lookup_desc, spec_lookup }, /* lookup */ | | | |
151 | { &vop_create_desc, spec_create }, /* create */ | | | |
152 | { &vop_mknod_desc, spec_mknod }, /* mknod */ | | | |
153 | { &vop_open_desc, spec_open }, /* open */ | | | |
154 | { &vop_close_desc, nfsspec_close }, /* close */ | | | |
155 | { &vop_access_desc, nfsspec_access }, /* access */ | | | |
156 | { &vop_getattr_desc, nfs_getattr }, /* getattr */ | | | |
157 | { &vop_setattr_desc, nfs_setattr }, /* setattr */ | | | |
158 | { &vop_read_desc, nfsspec_read }, /* read */ | | | |
159 | { &vop_write_desc, nfsspec_write }, /* write */ | | | |
160 | { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ | | | |
161 | { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ | | | |
162 | { &vop_poll_desc, spec_poll }, /* poll */ | | | |
163 | { &vop_kqfilter_desc, spec_kqfilter }, /* kqfilter */ | | | |
164 | { &vop_revoke_desc, spec_revoke }, /* revoke */ | | | |
165 | { &vop_mmap_desc, spec_mmap }, /* mmap */ | | | |
166 | { &vop_fsync_desc, spec_fsync }, /* fsync */ | | | |
167 | { &vop_seek_desc, spec_seek }, /* seek */ | | | |
168 | { &vop_remove_desc, spec_remove }, /* remove */ | | | |
169 | { &vop_link_desc, spec_link }, /* link */ | | | |
170 | { &vop_rename_desc, spec_rename }, /* rename */ | | | |
171 | { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ | | | |
172 | { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ | | | |
173 | { &vop_symlink_desc, spec_symlink }, /* symlink */ | | | |
174 | { &vop_readdir_desc, spec_readdir }, /* readdir */ | | | |
175 | { &vop_readlink_desc, spec_readlink }, /* readlink */ | | | |
176 | { &vop_abortop_desc, spec_abortop }, /* abortop */ | | | |
177 | { &vop_inactive_desc, nfs_inactive }, /* inactive */ | | | |
178 | { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ | | | |
179 | { &vop_lock_desc, nfs_lock }, /* lock */ | | | |
180 | { &vop_unlock_desc, nfs_unlock }, /* unlock */ | | | |
181 | { &vop_bmap_desc, spec_bmap }, /* bmap */ | | | |
182 | { &vop_strategy_desc, spec_strategy }, /* strategy */ | | | |
183 | { &vop_print_desc, nfs_print }, /* print */ | | | |
184 | { &vop_islocked_desc, nfs_islocked }, /* islocked */ | | | |
185 | { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ | | | |
186 | { &vop_advlock_desc, spec_advlock }, /* advlock */ | | | |
187 | { &vop_bwrite_desc, spec_bwrite }, /* bwrite */ | | | |
188 | { &vop_getpages_desc, spec_getpages }, /* getpages */ | | | |
189 | { &vop_putpages_desc, spec_putpages }, /* putpages */ | | | |
190 | { NULL, NULL } | | | |
191 | }; | | | |
192 | const struct vnodeopv_desc spec_nfsv2nodeop_opv_desc = | | | |
193 | { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries }; | | | |
194 | | | | |
195 | int (**fifo_nfsv2nodeop_p) __P((void *)); | | | |
196 | const struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { | | | |
197 | { &vop_default_desc, vn_default_error }, | | | |
198 | { &vop_lookup_desc, fifo_lookup }, /* lookup */ | | | |
199 | { &vop_create_desc, fifo_create }, /* create */ | | | |
200 | { &vop_mknod_desc, fifo_mknod }, /* mknod */ | | | |
201 | { &vop_open_desc, fifo_open }, /* open */ | | | |
202 | { &vop_close_desc, nfsfifo_close }, /* close */ | | | |
203 | { &vop_access_desc, nfsspec_access }, /* access */ | | | |
204 | { &vop_getattr_desc, nfs_getattr }, /* getattr */ | | | |
205 | { &vop_setattr_desc, nfs_setattr }, /* setattr */ | | | |
206 | { &vop_read_desc, nfsfifo_read }, /* read */ | | | |
207 | { &vop_write_desc, nfsfifo_write }, /* write */ | | | |
208 | { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ | | | |
209 | { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ | | | |
210 | { &vop_poll_desc, fifo_poll }, /* poll */ | | | |
211 | { &vop_kqfilter_desc, fifo_kqfilter }, /* kqfilter */ | | | |
212 | { &vop_revoke_desc, fifo_revoke }, /* revoke */ | | | |
213 | { &vop_mmap_desc, fifo_mmap }, /* mmap */ | | | |
214 | { &vop_fsync_desc, nfs_fsync }, /* fsync */ | | | |
215 | { &vop_seek_desc, fifo_seek }, /* seek */ | | | |
216 | { &vop_remove_desc, fifo_remove }, /* remove */ | | | |
217 | { &vop_link_desc, fifo_link }, /* link */ | | | |
218 | { &vop_rename_desc, fifo_rename }, /* rename */ | | | |
219 | { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ | | | |
220 | { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ | | | |
221 | { &vop_symlink_desc, fifo_symlink }, /* symlink */ | | | |
222 | { &vop_readdir_desc, fifo_readdir }, /* readdir */ | | | |
223 | { &vop_readlink_desc, fifo_readlink }, /* readlink */ | | | |
224 | { &vop_abortop_desc, fifo_abortop }, /* abortop */ | | | |
225 | { &vop_inactive_desc, nfs_inactive }, /* inactive */ | | | |
226 | { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ | | | |
227 | { &vop_lock_desc, nfs_lock }, /* lock */ | | | |
228 | { &vop_unlock_desc, nfs_unlock }, /* unlock */ | | | |
229 | { &vop_bmap_desc, fifo_bmap }, /* bmap */ | | | |
230 | { &vop_strategy_desc, genfs_badop }, /* strategy */ | | | |
231 | { &vop_print_desc, nfs_print }, /* print */ | | | |
232 | { &vop_islocked_desc, nfs_islocked }, /* islocked */ | | | |
233 | { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ | | | |
234 | { &vop_advlock_desc, fifo_advlock }, /* advlock */ | | | |
235 | { &vop_bwrite_desc, genfs_badop }, /* bwrite */ | | | |
236 | { &vop_putpages_desc, fifo_putpages }, /* putpages */ | | | |
237 | { NULL, NULL } | | | |
238 | }; | | | |
239 | const struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc = | | | |
240 | { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries }; | | | |
241 | | | | |
242 | static int nfs_linkrpc(struct vnode *, struct vnode *, const char *, | | | |
243 | size_t, kauth_cred_t, struct lwp *); | | | |
244 | static void nfs_writerpc_extfree(struct mbuf *, void *, size_t, void *); | | | |
245 | | | | |
246 | /* | | | |
247 | * Global variables | | | |
248 | */ | | | |
249 | extern u_int32_t nfs_true, nfs_false; | | | |
250 | extern u_int32_t nfs_xdrneg1; | | | |
251 | extern const nfstype nfsv3_type[9]; | | | |
252 | | | | |
253 | int nfs_numasync = 0; | | | |
254 | #define DIRHDSIZ _DIRENT_NAMEOFF(dp) | | | |
255 | #define UIO_ADVANCE(uio, siz) \ | | | |
256 | (void)((uio)->uio_resid -= (siz), \ | | | |
257 | (uio)->uio_iov->iov_base = (char *)(uio)->uio_iov->iov_base + (siz), \ | | | |
258 | (uio)->uio_iov->iov_len -= (siz)) | | | |
259 | | | | |
260 | static void nfs_cache_enter(struct vnode *, struct vnode *, | | | |
261 | struct componentname *); | | | |
262 | | | | |
263 | static void | | | |
264 | nfs_cache_enter(struct vnode *dvp, struct vnode *vp, | | | |
265 | struct componentname *cnp) | | | |
266 | { | | | |
267 | struct nfsnode *dnp = VTONFS(dvp); | | | |
268 | | | | |
269 | if (vp != NULL) { | | | |
270 | struct nfsnode *np = VTONFS(vp); | | | |
271 | | | | |
272 | np->n_ctime = np->n_vattr->va_ctime.tv_sec; | | | |
273 | } | | | |
274 | | | | |
275 | if (!timespecisset(&dnp->n_nctime)) | | | |
276 | dnp->n_nctime = dnp->n_vattr->va_mtime; | | | |
277 | | | | |
278 | cache_enter(dvp, vp, cnp); | | | |
279 | } | | | |
280 | | | | |
281 | /* | | | |
282 | * nfs null call from vfs. | | | |
283 | */ | | | |
284 | int | | | |
285 | nfs_null(vp, cred, l) | | | |
286 | struct vnode *vp; | | | |
287 | kauth_cred_t cred; | | | |
288 | struct lwp *l; | | | |
289 | { | | | |
290 | char *bpos, *dpos; | | | |
291 | int error = 0; | | | |
292 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
293 | struct nfsnode *np = VTONFS(vp); | | | |
294 | | | | |
295 | nfsm_reqhead(np, NFSPROC_NULL, 0); | | | |
296 | nfsm_request(np, NFSPROC_NULL, l, cred); | | | |
297 | nfsm_reqdone; | | | |
298 | return (error); | | | |
299 | } | | | |
300 | | | | |
301 | /* | | | |
302 | * nfs access vnode op. | | | |
303 | * For nfs version 2, just return ok. File accesses may fail later. | | | |
304 | * For nfs version 3, use the access rpc to check accessibility. If file modes | | | |
305 | * are changed on the server, accesses might still fail later. | | | |
306 | */ | | | |
307 | int | | | |
308 | nfs_access(v) | | | |
309 | void *v; | | | |
310 | { | | | |
311 | struct vop_access_args /* { | | | |
312 | struct vnode *a_vp; | | | |
313 | int a_mode; | | | |
314 | kauth_cred_t a_cred; | | | |
315 | } */ *ap = v; | | | |
316 | struct vnode *vp = ap->a_vp; | | | |
317 | #ifndef NFS_V2_ONLY | | | |
318 | u_int32_t *tl; | | | |
319 | char *cp; | | | |
320 | int32_t t1, t2; | | | |
321 | char *bpos, *dpos, *cp2; | | | |
322 | int error = 0, attrflag; | | | |
323 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
324 | u_int32_t mode, rmode; | | | |
325 | const int v3 = NFS_ISV3(vp); | | | |
326 | #endif | | | |
327 | int cachevalid; | | | |
328 | struct nfsnode *np = VTONFS(vp); | | | |
329 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); | | | |
330 | | | | |
331 | cachevalid = (np->n_accstamp != -1 && | | | |
332 | (time_uptime - np->n_accstamp) < nfs_attrtimeo(nmp, np) && | | | |
333 | np->n_accuid == kauth_cred_geteuid(ap->a_cred)); | | | |
334 | | | | |
335 | /* | | | |
336 | * Check access cache first. If this request has been made for this | | | |
337 | * uid shortly before, use the cached result. | | | |
338 | */ | | | |
339 | if (cachevalid) { | | | |
340 | if (!np->n_accerror) { | | | |
341 | if ((np->n_accmode & ap->a_mode) == ap->a_mode) | | | |
342 | return np->n_accerror; | | | |
343 | } else if ((np->n_accmode & ap->a_mode) == np->n_accmode) | | | |
344 | return np->n_accerror; | | | |
345 | } | | | |
346 | | | | |
347 | #ifndef NFS_V2_ONLY | | | |
348 | /* | | | |
349 | * For nfs v3, do an access rpc, otherwise you are stuck emulating | | | |
350 | * ufs_access() locally using the vattr. This may not be correct, | | | |
351 | * since the server may apply other access criteria such as | | | |
352 | * client uid-->server uid mapping that we do not know about, but | | | |
353 | * this is better than just returning anything that is lying about | | | |
354 | * in the cache. | | | |
355 | */ | | | |
356 | if (v3) { | | | |
357 | nfsstats.rpccnt[NFSPROC_ACCESS]++; | | | |
358 | nfsm_reqhead(np, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED); | | | |
359 | nfsm_fhtom(np, v3); | | | |
360 | nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
361 | if (ap->a_mode & VREAD) | | | |
362 | mode = NFSV3ACCESS_READ; | | | |
363 | else | | | |
364 | mode = 0; | | | |
365 | if (vp->v_type != VDIR) { | | | |
366 | if (ap->a_mode & VWRITE) | | | |
367 | mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); | | | |
368 | if (ap->a_mode & VEXEC) | | | |
369 | mode |= NFSV3ACCESS_EXECUTE; | | | |
370 | } else { | | | |
371 | if (ap->a_mode & VWRITE) | | | |
372 | mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | | | | |
373 | NFSV3ACCESS_DELETE); | | | |
374 | if (ap->a_mode & VEXEC) | | | |
375 | mode |= NFSV3ACCESS_LOOKUP; | | | |
376 | } | | | |
377 | *tl = txdr_unsigned(mode); | | | |
378 | nfsm_request(np, NFSPROC_ACCESS, curlwp, ap->a_cred); | | | |
379 | nfsm_postop_attr(vp, attrflag, 0); | | | |
380 | if (!error) { | | | |
381 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
382 | rmode = fxdr_unsigned(u_int32_t, *tl); | | | |
383 | /* | | | |
384 | * The NFS V3 spec does not clarify whether or not | | | |
385 | * the returned access bits can be a superset of | | | |
386 | * the ones requested, so... | | | |
387 | */ | | | |
388 | if ((rmode & mode) != mode) | | | |
389 | error = EACCES; | | | |
390 | } | | | |
391 | nfsm_reqdone; | | | |
392 | } else | | | |
393 | #endif | | | |
394 | return (nfsspec_access(ap)); | | | |
395 | #ifndef NFS_V2_ONLY | | | |
396 | /* | | | |
397 | * Disallow write attempts on filesystems mounted read-only; | | | |
398 | * unless the file is a socket, fifo, or a block or character | | | |
399 | * device resident on the filesystem. | | | |
400 | */ | | | |
401 | if (!error && (ap->a_mode & VWRITE) && | | | |
402 | (vp->v_mount->mnt_flag & MNT_RDONLY)) { | | | |
403 | switch (vp->v_type) { | | | |
404 | case VREG: | | | |
405 | case VDIR: | | | |
406 | case VLNK: | | | |
407 | error = EROFS; | | | |
408 | default: | | | |
409 | break; | | | |
410 | } | | | |
411 | } | | | |
412 | | | | |
413 | if (!error || error == EACCES) { | | | |
414 | /* | | | |
415 | * If we got the same result as for a previous, | | | |
416 | * different request, OR it in. Don't update | | | |
417 | * the timestamp in that case. | | | |
418 | */ | | | |
419 | if (cachevalid && np->n_accstamp != -1 && | | | |
420 | error == np->n_accerror) { | | | |
421 | if (!error) | | | |
422 | np->n_accmode |= ap->a_mode; | | | |
423 | else if ((np->n_accmode & ap->a_mode) == ap->a_mode) | | | |
424 | np->n_accmode = ap->a_mode; | | | |
425 | } else { | | | |
426 | np->n_accstamp = time_uptime; | | | |
427 | np->n_accuid = kauth_cred_geteuid(ap->a_cred); | | | |
428 | np->n_accmode = ap->a_mode; | | | |
429 | np->n_accerror = error; | | | |
430 | } | | | |
431 | } | | | |
432 | | | | |
433 | return (error); | | | |
434 | #endif | | | |
435 | } | | | |
436 | | | | |
437 | /* | | | |
438 | * nfs open vnode op | | | |
439 | * Check to see if the type is ok | | | |
440 | * and that deletion is not in progress. | | | |
441 | * For paged in text files, you will need to flush the page cache | | | |
442 | * if consistency is lost. | | | |
443 | */ | | | |
444 | /* ARGSUSED */ | | | |
445 | int | | | |
446 | nfs_open(v) | | | |
447 | void *v; | | | |
448 | { | | | |
449 | struct vop_open_args /* { | | | |
450 | struct vnode *a_vp; | | | |
451 | int a_mode; | | | |
452 | kauth_cred_t a_cred; | | | |
453 | } */ *ap = v; | | | |
454 | struct vnode *vp = ap->a_vp; | | | |
455 | struct nfsnode *np = VTONFS(vp); | | | |
456 | int error; | | | |
457 | | | | |
458 | if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) { | | | |
459 | return (EACCES); | | | |
460 | } | | | |
461 | | | | |
462 | if (ap->a_mode & FREAD) { | | | |
463 | if (np->n_rcred != NULL) | | | |
464 | kauth_cred_free(np->n_rcred); | | | |
465 | np->n_rcred = ap->a_cred; | | | |
466 | kauth_cred_hold(np->n_rcred); | | | |
467 | } | | | |
468 | if (ap->a_mode & FWRITE) { | | | |
469 | if (np->n_wcred != NULL) | | | |
470 | kauth_cred_free(np->n_wcred); | | | |
471 | np->n_wcred = ap->a_cred; | | | |
472 | kauth_cred_hold(np->n_wcred); | | | |
473 | } | | | |
474 | | | | |
475 | error = nfs_flushstalebuf(vp, ap->a_cred, curlwp, 0); | | | |
476 | if (error) | | | |
477 | return error; | | | |
478 | | | | |
479 | NFS_INVALIDATE_ATTRCACHE(np); /* For Open/Close consistency */ | | | |
480 | | | | |
481 | return (0); | | | |
482 | } | | | |
483 | | | | |
484 | /* | | | |
485 | * nfs close vnode op | | | |
486 | * What an NFS client should do upon close after writing is a debatable issue. | | | |
487 | * Most NFS clients push delayed writes to the server upon close, basically for | | | |
488 | * two reasons: | | | |
489 | * 1 - So that any write errors may be reported back to the client process | | | |
490 | * doing the close system call. By far the two most likely errors are | | | |
491 | * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure. | | | |
492 | * 2 - To put a worst case upper bound on cache inconsistency between | | | |
493 | * multiple clients for the file. | | | |
494 | * There is also a consistency problem for Version 2 of the protocol w.r.t. | | | |
495 | * not being able to tell if other clients are writing a file concurrently, | | | |
496 | * since there is no way of knowing if the changed modify time in the reply | | | |
497 | * is only due to the write for this client. | | | |
498 | * (NFS Version 3 provides weak cache consistency data in the reply that | | | |
499 | * should be sufficient to detect and handle this case.) | | | |
500 | * | | | |
501 | * The current code does the following: | | | |
502 | * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers | | | |
503 | * for NFS Version 3 - flush dirty buffers to the server but don't invalidate | | | |
504 | * or commit them (this satisfies 1 and 2 except for the | | | |
505 | * case where the server crashes after this close but | | | |
506 | * before the commit RPC, which is felt to be "good | | | |
507 | * enough". Changing the last argument to nfs_flush() to | | | |
508 | * a 1 would force a commit operation, if it is felt a | | | |
509 | * commit is necessary now. | | | |
510 | */ | | | |
511 | /* ARGSUSED */ | | | |
512 | int | | | |
513 | nfs_close(v) | | | |
514 | void *v; | | | |
515 | { | | | |
516 | struct vop_close_args /* { | | | |
517 | struct vnodeop_desc *a_desc; | | | |
518 | struct vnode *a_vp; | | | |
519 | int a_fflag; | | | |
520 | kauth_cred_t a_cred; | | | |
521 | } */ *ap = v; | | | |
522 | struct vnode *vp = ap->a_vp; | | | |
523 | struct nfsnode *np = VTONFS(vp); | | | |
524 | int error = 0; | | | |
525 | UVMHIST_FUNC("nfs_close"); UVMHIST_CALLED(ubchist); | | | |
526 | | | | |
527 | if (vp->v_type == VREG) { | | | |
528 | if (np->n_flag & NMODIFIED) { | | | |
529 | #ifndef NFS_V2_ONLY | | | |
530 | if (NFS_ISV3(vp)) { | | | |
531 | error = nfs_flush(vp, ap->a_cred, MNT_WAIT, curlwp, 0); | | | |
532 | np->n_flag &= ~NMODIFIED; | | | |
533 | } else | | | |
534 | #endif | | | |
535 | error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, curlwp, 1); | | | |
536 | NFS_INVALIDATE_ATTRCACHE(np); | | | |
537 | } | | | |
538 | if (np->n_flag & NWRITEERR) { | | | |
539 | np->n_flag &= ~NWRITEERR; | | | |
540 | error = np->n_error; | | | |
541 | } | | | |
542 | } | | | |
543 | UVMHIST_LOG(ubchist, "returning %d", error,0,0,0); | | | |
544 | return (error); | | | |
545 | } | | | |
546 | | | | |
547 | /* | | | |
548 | * nfs getattr call from vfs. | | | |
549 | */ | | | |
550 | int | | | |
551 | nfs_getattr(v) | | | |
552 | void *v; | | | |
553 | { | | | |
554 | struct vop_getattr_args /* { | | | |
555 | struct vnode *a_vp; | | | |
556 | struct vattr *a_vap; | | | |
557 | kauth_cred_t a_cred; | | | |
558 | } */ *ap = v; | | | |
559 | struct vnode *vp = ap->a_vp; | | | |
560 | struct nfsnode *np = VTONFS(vp); | | | |
561 | char *cp; | | | |
562 | u_int32_t *tl; | | | |
563 | int32_t t1, t2; | | | |
564 | char *bpos, *dpos; | | | |
565 | int error = 0; | | | |
566 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
567 | const int v3 = NFS_ISV3(vp); | | | |
568 | | | | |
569 | /* | | | |
570 | * Update local times for special files. | | | |
571 | */ | | | |
572 | if (np->n_flag & (NACC | NUPD)) | | | |
573 | np->n_flag |= NCHG; | | | |
574 | | | | |
575 | /* | | | |
576 | * if we have delayed truncation, do it now. | | | |
577 | */ | | | |
578 | nfs_delayedtruncate(vp); | | | |
579 | | | | |
580 | /* | | | |
581 | * First look in the cache. | | | |
582 | */ | | | |
583 | if (nfs_getattrcache(vp, ap->a_vap) == 0) | | | |
584 | return (0); | | | |
585 | nfsstats.rpccnt[NFSPROC_GETATTR]++; | | | |
586 | nfsm_reqhead(np, NFSPROC_GETATTR, NFSX_FH(v3)); | | | |
587 | nfsm_fhtom(np, v3); | | | |
588 | nfsm_request(np, NFSPROC_GETATTR, curlwp, ap->a_cred); | | | |
589 | if (!error) { | | | |
590 | nfsm_loadattr(vp, ap->a_vap, 0); | | | |
591 | if (vp->v_type == VDIR && | | | |
592 | ap->a_vap->va_blocksize < NFS_DIRFRAGSIZ) | | | |
593 | ap->a_vap->va_blocksize = NFS_DIRFRAGSIZ; | | | |
594 | } | | | |
595 | nfsm_reqdone; | | | |
596 | return (error); | | | |
597 | } | | | |
598 | | | | |
599 | /* | | | |
600 | * nfs setattr call. | | | |
601 | */ | | | |
602 | int | | | |
603 | nfs_setattr(v) | | | |
604 | void *v; | | | |
605 | { | | | |
606 | struct vop_setattr_args /* { | | | |
607 | struct vnodeop_desc *a_desc; | | | |
608 | struct vnode *a_vp; | | | |
609 | struct vattr *a_vap; | | | |
610 | kauth_cred_t a_cred; | | | |
611 | } */ *ap = v; | | | |
612 | struct vnode *vp = ap->a_vp; | | | |
613 | struct nfsnode *np = VTONFS(vp); | | | |
614 | struct vattr *vap = ap->a_vap; | | | |
615 | int error = 0; | | | |
616 | u_quad_t tsize = 0; | | | |
617 | | | | |
618 | /* | | | |
619 | * Setting of flags is not supported. | | | |
620 | */ | | | |
621 | if (vap->va_flags != VNOVAL) | | | |
622 | return (EOPNOTSUPP); | | | |
623 | | | | |
624 | /* | | | |
625 | * Disallow write attempts if the filesystem is mounted read-only. | | | |
626 | */ | | | |
627 | if ((vap->va_uid != (uid_t)VNOVAL || | | | |
628 | vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || | | | |
629 | vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) && | | | |
630 | (vp->v_mount->mnt_flag & MNT_RDONLY)) | | | |
631 | return (EROFS); | | | |
632 | if (vap->va_size != VNOVAL) { | | | |
633 | if (vap->va_size > VFSTONFS(vp->v_mount)->nm_maxfilesize) { | | | |
634 | return EFBIG; | | | |
635 | } | | | |
636 | switch (vp->v_type) { | | | |
637 | case VDIR: | | | |
638 | return (EISDIR); | | | |
639 | case VCHR: | | | |
640 | case VBLK: | | | |
641 | case VSOCK: | | | |
642 | case VFIFO: | | | |
643 | if (vap->va_mtime.tv_sec == VNOVAL && | | | |
644 | vap->va_atime.tv_sec == VNOVAL && | | | |
645 | vap->va_mode == (mode_t)VNOVAL && | | | |
646 | vap->va_uid == (uid_t)VNOVAL && | | | |
647 | vap->va_gid == (gid_t)VNOVAL) | | | |
648 | return (0); | | | |
649 | vap->va_size = VNOVAL; | | | |
650 | break; | | | |
651 | default: | | | |
652 | /* | | | |
653 | * Disallow write attempts if the filesystem is | | | |
654 | * mounted read-only. | | | |
655 | */ | | | |
656 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | | | |
657 | return (EROFS); | | | |
658 | genfs_node_wrlock(vp); | | | |
659 | uvm_vnp_setsize(vp, vap->va_size); | | | |
660 | tsize = np->n_size; | | | |
661 | np->n_size = vap->va_size; | | | |
662 | if (vap->va_size == 0) | | | |
663 | error = nfs_vinvalbuf(vp, 0, | | | |
664 | ap->a_cred, curlwp, 1); | | | |
665 | else | | | |
666 | error = nfs_vinvalbuf(vp, V_SAVE, | | | |
667 | ap->a_cred, curlwp, 1); | | | |
668 | if (error) { | | | |
669 | uvm_vnp_setsize(vp, tsize); | | | |
670 | genfs_node_unlock(vp); | | | |
671 | return (error); | | | |
672 | } | | | |
673 | np->n_vattr->va_size = vap->va_size; | | | |
674 | } | | | |
675 | } else { | | | |
676 | /* | | | |
677 | * flush files before setattr because a later write of | | | |
678 | * cached data might change timestamps or reset sugid bits | | | |
679 | */ | | | |
680 | if ((vap->va_mtime.tv_sec != VNOVAL || | | | |
681 | vap->va_atime.tv_sec != VNOVAL || | | | |
682 | vap->va_mode != VNOVAL) && | | | |
683 | vp->v_type == VREG && | | | |
684 | (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, | | | |
685 | curlwp, 1)) == EINTR) | | | |
686 | return (error); | | | |
687 | } | | | |
688 | error = nfs_setattrrpc(vp, vap, ap->a_cred, curlwp); | | | |
689 | if (vap->va_size != VNOVAL) { | | | |
690 | if (error) { | | | |
691 | np->n_size = np->n_vattr->va_size = tsize; | | | |
692 | uvm_vnp_setsize(vp, np->n_size); | | | |
693 | } | | | |
694 | genfs_node_unlock(vp); | | | |
695 | } | | | |
696 | VN_KNOTE(vp, NOTE_ATTRIB); | | | |
697 | return (error); | | | |
698 | } | | | |
699 | | | | |
700 | /* | | | |
701 | * Do an nfs setattr rpc. | | | |
702 | */ | | | |
703 | int | | | |
704 | nfs_setattrrpc(vp, vap, cred, l) | | | |
705 | struct vnode *vp; | | | |
706 | struct vattr *vap; | | | |
707 | kauth_cred_t cred; | | | |
708 | struct lwp *l; | | | |
709 | { | | | |
710 | struct nfsv2_sattr *sp; | | | |
711 | char *cp; | | | |
712 | int32_t t1, t2; | | | |
713 | char *bpos, *dpos; | | | |
714 | u_int32_t *tl; | | | |
715 | int error = 0; | | | |
716 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
717 | const int v3 = NFS_ISV3(vp); | | | |
718 | struct nfsnode *np = VTONFS(vp); | | | |
719 | #ifndef NFS_V2_ONLY | | | |
720 | int wccflag = NFSV3_WCCRATTR; | | | |
721 | char *cp2; | | | |
722 | #endif | | | |
723 | | | | |
724 | nfsstats.rpccnt[NFSPROC_SETATTR]++; | | | |
725 | nfsm_reqhead(np, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3)); | | | |
726 | nfsm_fhtom(np, v3); | | | |
727 | #ifndef NFS_V2_ONLY | | | |
728 | if (v3) { | | | |
729 | nfsm_v3attrbuild(vap, true); | | | |
730 | nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
731 | *tl = nfs_false; | | | |
732 | } else { | | | |
733 | #endif | | | |
734 | nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); | | | |
735 | if (vap->va_mode == (mode_t)VNOVAL) | | | |
736 | sp->sa_mode = nfs_xdrneg1; | | | |
737 | else | | | |
738 | sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode); | | | |
739 | if (vap->va_uid == (uid_t)VNOVAL) | | | |
740 | sp->sa_uid = nfs_xdrneg1; | | | |
741 | else | | | |
742 | sp->sa_uid = txdr_unsigned(vap->va_uid); | | | |
743 | if (vap->va_gid == (gid_t)VNOVAL) | | | |
744 | sp->sa_gid = nfs_xdrneg1; | | | |
745 | else | | | |
746 | sp->sa_gid = txdr_unsigned(vap->va_gid); | | | |
747 | sp->sa_size = txdr_unsigned(vap->va_size); | | | |
748 | txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); | | | |
749 | txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); | | | |
750 | #ifndef NFS_V2_ONLY | | | |
751 | } | | | |
752 | #endif | | | |
753 | nfsm_request(np, NFSPROC_SETATTR, l, cred); | | | |
754 | #ifndef NFS_V2_ONLY | | | |
755 | if (v3) { | | | |
756 | nfsm_wcc_data(vp, wccflag, NAC_NOTRUNC, false); | | | |
757 | } else | | | |
758 | #endif | | | |
759 | nfsm_loadattr(vp, (struct vattr *)0, NAC_NOTRUNC); | | | |
760 | nfsm_reqdone; | | | |
761 | return (error); | | | |
762 | } | | | |
763 | | | | |
764 | /* | | | |
765 | * nfs lookup call, one step at a time... | | | |
766 | * First look in cache | | | |
767 | * If not found, unlock the directory nfsnode and do the rpc | | | |
768 | * | | | |
769 | * This code is full of lock/unlock statements and checks, because | | | |
770 | * we continue after cache_lookup has finished (we need to check | | | |
771 | * with the attr cache and do an rpc if it has timed out). This means | | | |
772 | * that the locking effects of cache_lookup have to be taken into | | | |
773 | * account. | | | |
774 | */ | | | |
775 | int | | | |
776 | nfs_lookup(v) | | | |
777 | void *v; | | | |
778 | { | | | |
779 | struct vop_lookup_args /* { | | | |
780 | struct vnodeop_desc *a_desc; | | | |
781 | struct vnode *a_dvp; | | | |
782 | struct vnode **a_vpp; | | | |
783 | struct componentname *a_cnp; | | | |
784 | } */ *ap = v; | | | |
785 | struct componentname *cnp = ap->a_cnp; | | | |
786 | struct vnode *dvp = ap->a_dvp; | | | |
787 | struct vnode **vpp = ap->a_vpp; | | | |
788 | int flags; | | | |
789 | struct vnode *newvp; | | | |
790 | u_int32_t *tl; | | | |
791 | char *cp; | | | |
792 | int32_t t1, t2; | | | |
793 | char *bpos, *dpos, *cp2; | | | |
794 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
795 | long len; | | | |
796 | nfsfh_t *fhp; | | | |
797 | struct nfsnode *np; | | | |
798 | int error = 0, attrflag, fhsize; | | | |
799 | const int v3 = NFS_ISV3(dvp); | | | |
800 | | | | |
801 | flags = cnp->cn_flags; | | | |
802 | | | | |
803 | *vpp = NULLVP; | | | |
804 | newvp = NULLVP; | | | |
805 | if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && | | | |
806 | (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) | | | |
807 | return (EROFS); | | | |
808 | if (dvp->v_type != VDIR) | | | |
809 | return (ENOTDIR); | | | |
810 | | | | |
811 | /* | | | |
812 | * RFC1813(nfsv3) 3.2 says clients should handle "." by themselves. | | | |
813 | */ | | | |
814 | if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { | | | |
815 | error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred); | | | |
816 | if (error) | | | |
817 | return error; | | | |
818 | if (cnp->cn_nameiop == RENAME && (flags & ISLASTCN)) | | | |
819 | return EISDIR; | | | |
820 | VREF(dvp); | | | |
821 | *vpp = dvp; | | | |
822 | if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) | | | |
823 | cnp->cn_flags |= SAVENAME; | | | |
824 | return 0; | | | |
825 | } | | | |
826 | | | | |
827 | np = VTONFS(dvp); | | | |
828 | | | | |
829 | /* | | | |
830 | * Before tediously performing a linear scan of the directory, | | | |
831 | * check the name cache to see if the directory/name pair | | | |
832 | * we are looking for is known already. | | | |
833 | * If the directory/name pair is found in the name cache, | | | |
834 | * we have to ensure the directory has not changed from | | | |
835 | * the time the cache entry has been created. If it has, | | | |
836 | * the cache entry has to be ignored. | | | |
837 | */ | | | |
838 | error = cache_lookup_raw(dvp, vpp, cnp); | | | |
839 | KASSERT(dvp != *vpp); | | | |
840 | if (error >= 0) { | | | |
841 | struct vattr vattr; | | | |
842 | int err2; | | | |
843 | | | | |
844 | if (error && error != ENOENT) { | | | |
845 | *vpp = NULLVP; | | | |
846 | return error; | | | |
847 | } | | | |
848 | | | | |
849 | err2 = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred); | | | |
850 | if (err2 != 0) { | | | |
851 | if (error == 0) | | | |
852 | vrele(*vpp); | | | |
853 | *vpp = NULLVP; | | | |
854 | return err2; | | | |
855 | } | | | |
856 | | | | |
857 | if (VOP_GETATTR(dvp, &vattr, cnp->cn_cred) | | | |
858 | || timespeccmp(&vattr.va_mtime, | | | |
859 | &VTONFS(dvp)->n_nctime, !=)) { | | | |
860 | if (error == 0) { | | | |
861 | vrele(*vpp); | | | |
862 | *vpp = NULLVP; | | | |
863 | } | | | |
864 | cache_purge1(dvp, NULL, PURGE_CHILDREN); | | | |
865 | timespecclear(&np->n_nctime); | | | |
866 | goto dorpc; | | | |
867 | } | | | |
868 | | | | |
869 | if (error == ENOENT) { | | | |
870 | goto noentry; | | | |
871 | } | | | |
872 | | | | |
873 | newvp = *vpp; | | | |
874 | if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred) | | | |
875 | && vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime) { | | | |
876 | nfsstats.lookupcache_hits++; | | | |
877 | if ((flags & ISDOTDOT) != 0) { | | | |
878 | VOP_UNLOCK(dvp, 0); | | | |
879 | } | | | |
880 | error = vn_lock(newvp, LK_EXCLUSIVE); | | | |
881 | if ((flags & ISDOTDOT) != 0) { | | | |
882 | vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); | | | |
883 | } | | | |
884 | if (error) { | | | |
885 | /* newvp has been revoked. */ | | | |
886 | vrele(newvp); | | | |
887 | *vpp = NULL; | | | |
888 | return error; | | | |
889 | } | | | |
890 | if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) | | | |
891 | cnp->cn_flags |= SAVENAME; | | | |
892 | KASSERT(newvp->v_type != VNON); | | | |
893 | return (0); | | | |
894 | } | | | |
895 | cache_purge1(newvp, NULL, PURGE_PARENTS); | | | |
896 | vrele(newvp); | | | |
897 | *vpp = NULLVP; | | | |
898 | } | | | |
899 | dorpc: | | | |
900 | #if 0 | | | |
901 | /* | | | |
902 | * because nfsv3 has the same CREATE semantics as ours, | | | |
903 | * we don't have to perform LOOKUPs beforehand. | | | |
904 | * | | | |
905 | * XXX ideally we can do the same for nfsv2 in the case of !O_EXCL. | | | |
906 | * XXX although we have no way to know if O_EXCL is requested or not. | | | |
907 | */ | | | |
908 | | | | |
909 | if (v3 && cnp->cn_nameiop == CREATE && | | | |
910 | (flags & (ISLASTCN|ISDOTDOT)) == ISLASTCN && | | | |
911 | (dvp->v_mount->mnt_flag & MNT_RDONLY) == 0) { | | | |
912 | cnp->cn_flags |= SAVENAME; | | | |
913 | return (EJUSTRETURN); | | | |
914 | } | | | |
915 | #endif /* 0 */ | | | |
916 | | | | |
917 | error = 0; | | | |
918 | newvp = NULLVP; | | | |
919 | nfsstats.lookupcache_misses++; | | | |
920 | nfsstats.rpccnt[NFSPROC_LOOKUP]++; | | | |
921 | len = cnp->cn_namelen; | | | |
922 | nfsm_reqhead(np, NFSPROC_LOOKUP, | | | |
923 | NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); | | | |
924 | nfsm_fhtom(np, v3); | | | |
925 | nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); | | | |
926 | nfsm_request(np, NFSPROC_LOOKUP, curlwp, cnp->cn_cred); | | | |
927 | if (error) { | | | |
928 | nfsm_postop_attr(dvp, attrflag, 0); | | | |
929 | m_freem(mrep); | | | |
930 | goto nfsmout; | | | |
931 | } | | | |
932 | nfsm_getfh(fhp, fhsize, v3); | | | |
933 | | | | |
934 | /* | | | |
935 | * Handle RENAME case... | | | |
936 | */ | | | |
937 | if (cnp->cn_nameiop == RENAME && (flags & ISLASTCN)) { | | | |
938 | if (NFS_CMPFH(np, fhp, fhsize)) { | | | |
939 | m_freem(mrep); | | | |
940 | return (EISDIR); | | | |
941 | } | | | |
942 | error = nfs_nget(dvp->v_mount, fhp, fhsize, &np); | | | |
943 | if (error) { | | | |
944 | m_freem(mrep); | | | |
945 | return error; | | | |
946 | } | | | |
947 | newvp = NFSTOV(np); | | | |
948 | #ifndef NFS_V2_ONLY | | | |
949 | if (v3) { | | | |
950 | nfsm_postop_attr(newvp, attrflag, 0); | | | |
951 | nfsm_postop_attr(dvp, attrflag, 0); | | | |
952 | } else | | | |
953 | #endif | | | |
954 | nfsm_loadattr(newvp, (struct vattr *)0, 0); | | | |
955 | *vpp = newvp; | | | |
956 | m_freem(mrep); | | | |
957 | cnp->cn_flags |= SAVENAME; | | | |
958 | goto validate; | | | |
959 | } | | | |
960 | | | | |
961 | /* | | | |
962 | * The postop attr handling is duplicated for each if case, | | | |
963 | * because it should be done while dvp is locked (unlocking | | | |
964 | * dvp is different for each case). | | | |
965 | */ | | | |
966 | | | | |
967 | if (NFS_CMPFH(np, fhp, fhsize)) { | | | |
968 | /* | | | |
969 | * "." lookup | | | |
970 | */ | | | |
971 | VREF(dvp); | | | |
972 | newvp = dvp; | | | |
973 | #ifndef NFS_V2_ONLY | | | |
974 | if (v3) { | | | |
975 | nfsm_postop_attr(newvp, attrflag, 0); | | | |
976 | nfsm_postop_attr(dvp, attrflag, 0); | | | |
977 | } else | | | |
978 | #endif | | | |
979 | nfsm_loadattr(newvp, (struct vattr *)0, 0); | | | |
980 | } else if (flags & ISDOTDOT) { | | | |
981 | /* | | | |
982 | * ".." lookup | | | |
983 | */ | | | |
984 | VOP_UNLOCK(dvp, 0); | | | |
985 | error = nfs_nget(dvp->v_mount, fhp, fhsize, &np); | | | |
986 | vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); | | | |
987 | if (error) { | | | |
988 | m_freem(mrep); | | | |
989 | return error; | | | |
990 | } | | | |
991 | newvp = NFSTOV(np); | | | |
992 | | | | |
993 | #ifndef NFS_V2_ONLY | | | |
994 | if (v3) { | | | |
995 | nfsm_postop_attr(newvp, attrflag, 0); | | | |
996 | nfsm_postop_attr(dvp, attrflag, 0); | | | |
997 | } else | | | |
998 | #endif | | | |
999 | nfsm_loadattr(newvp, (struct vattr *)0, 0); | | | |
1000 | } else { | | | |
1001 | /* | | | |
1002 | * Other lookups. | | | |
1003 | */ | | | |
1004 | error = nfs_nget(dvp->v_mount, fhp, fhsize, &np); | | | |
1005 | if (error) { | | | |
1006 | m_freem(mrep); | | | |
1007 | return error; | | | |
1008 | } | | | |
1009 | newvp = NFSTOV(np); | | | |
1010 | #ifndef NFS_V2_ONLY | | | |
1011 | if (v3) { | | | |
1012 | nfsm_postop_attr(newvp, attrflag, 0); | | | |
1013 | nfsm_postop_attr(dvp, attrflag, 0); | | | |
1014 | } else | | | |
1015 | #endif | | | |
1016 | nfsm_loadattr(newvp, (struct vattr *)0, 0); | | | |
1017 | } | | | |
1018 | if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) | | | |
1019 | cnp->cn_flags |= SAVENAME; | | | |
1020 | if ((cnp->cn_flags & MAKEENTRY) && | | | |
1021 | (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { | | | |
1022 | nfs_cache_enter(dvp, newvp, cnp); | | | |
1023 | } | | | |
1024 | *vpp = newvp; | | | |
1025 | nfsm_reqdone; | | | |
1026 | if (error) { | | | |
1027 | /* | | | |
1028 | * We get here only because of errors returned by | | | |
1029 | * the RPC. Otherwise we'll have returned above | | | |
1030 | * (the nfsm_* macros will jump to nfsm_reqdone | | | |
1031 | * on error). | | | |
1032 | */ | | | |
1033 | if (error == ENOENT && (cnp->cn_flags & MAKEENTRY) && | | | |
1034 | cnp->cn_nameiop != CREATE) { | | | |
1035 | nfs_cache_enter(dvp, NULL, cnp); | | | |
1036 | } | | | |
1037 | if (newvp != NULLVP) { | | | |
1038 | if (newvp == dvp) { | | | |
1039 | vrele(newvp); | | | |
1040 | } else { | | | |
1041 | vput(newvp); | | | |
1042 | } | | | |
1043 | } | | | |
1044 | noentry: | | | |
1045 | if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && | | | |
1046 | (flags & ISLASTCN) && error == ENOENT) { | | | |
1047 | if (dvp->v_mount->mnt_flag & MNT_RDONLY) { | | | |
1048 | error = EROFS; | | | |
1049 | } else { | | | |
1050 | error = EJUSTRETURN; | | | |
1051 | cnp->cn_flags |= SAVENAME; | | | |
1052 | } | | | |
1053 | } | | | |
1054 | *vpp = NULL; | | | |
1055 | return error; | | | |
1056 | } | | | |
1057 | | | | |
1058 | validate: | | | |
1059 | /* | | | |
1060 | * make sure we have valid type and size. | | | |
1061 | */ | | | |
1062 | | | | |
1063 | newvp = *vpp; | | | |
1064 | if (newvp->v_type == VNON) { | | | |
1065 | struct vattr vattr; /* dummy */ | | | |
1066 | | | | |
1067 | KASSERT(VTONFS(newvp)->n_attrstamp == 0); | | | |
1068 | error = VOP_GETATTR(newvp, &vattr, cnp->cn_cred); | | | |
1069 | if (error) { | | | |
1070 | vput(newvp); | | | |
1071 | *vpp = NULL; | | | |
1072 | } | | | |
1073 | } | | | |
1074 | | | | |
1075 | return error; | | | |
1076 | } | | | |
1077 | | | | |
1078 | /* | | | |
1079 | * nfs read call. | | | |
1080 | * Just call nfs_bioread() to do the work. | | | |
1081 | */ | | | |
1082 | int | | | |
1083 | nfs_read(v) | | | |
1084 | void *v; | | | |
1085 | { | | | |
1086 | struct vop_read_args /* { | | | |
1087 | struct vnode *a_vp; | | | |
1088 | struct uio *a_uio; | | | |
1089 | int a_ioflag; | | | |
1090 | kauth_cred_t a_cred; | | | |
1091 | } */ *ap = v; | | | |
1092 | struct vnode *vp = ap->a_vp; | | | |
1093 | | | | |
1094 | if (vp->v_type != VREG) | | | |
1095 | return EISDIR; | | | |
1096 | return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred, 0)); | | | |
1097 | } | | | |
1098 | | | | |
1099 | /* | | | |
1100 | * nfs readlink call | | | |
1101 | */ | | | |
1102 | int | | | |
1103 | nfs_readlink(v) | | | |
1104 | void *v; | | | |
1105 | { | | | |
1106 | struct vop_readlink_args /* { | | | |
1107 | struct vnode *a_vp; | | | |
1108 | struct uio *a_uio; | | | |
1109 | kauth_cred_t a_cred; | | | |
1110 | } */ *ap = v; | | | |
1111 | struct vnode *vp = ap->a_vp; | | | |
1112 | struct nfsnode *np = VTONFS(vp); | | | |
1113 | | | | |
1114 | if (vp->v_type != VLNK) | | | |
1115 | return (EPERM); | | | |
1116 | | | | |
1117 | if (np->n_rcred != NULL) { | | | |
1118 | kauth_cred_free(np->n_rcred); | | | |
1119 | } | | | |
1120 | np->n_rcred = ap->a_cred; | | | |
1121 | kauth_cred_hold(np->n_rcred); | | | |
1122 | | | | |
1123 | return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred, 0)); | | | |
1124 | } | | | |
1125 | | | | |
1126 | /* | | | |
1127 | * Do a readlink rpc. | | | |
1128 | * Called by nfs_doio() from below the buffer cache. | | | |
1129 | */ | | | |
1130 | int | | | |
1131 | nfs_readlinkrpc(vp, uiop, cred) | | | |
1132 | struct vnode *vp; | | | |
1133 | struct uio *uiop; | | | |
1134 | kauth_cred_t cred; | | | |
1135 | { | | | |
1136 | u_int32_t *tl; | | | |
1137 | char *cp; | | | |
1138 | int32_t t1, t2; | | | |
1139 | char *bpos, *dpos, *cp2; | | | |
1140 | int error = 0; | | | |
1141 | uint32_t len; | | | |
1142 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
1143 | const int v3 = NFS_ISV3(vp); | | | |
1144 | struct nfsnode *np = VTONFS(vp); | | | |
1145 | #ifndef NFS_V2_ONLY | | | |
1146 | int attrflag; | | | |
1147 | #endif | | | |
1148 | | | | |
1149 | nfsstats.rpccnt[NFSPROC_READLINK]++; | | | |
1150 | nfsm_reqhead(np, NFSPROC_READLINK, NFSX_FH(v3)); | | | |
1151 | nfsm_fhtom(np, v3); | | | |
1152 | nfsm_request(np, NFSPROC_READLINK, curlwp, cred); | | | |
1153 | #ifndef NFS_V2_ONLY | | | |
1154 | if (v3) | | | |
1155 | nfsm_postop_attr(vp, attrflag, 0); | | | |
1156 | #endif | | | |
1157 | if (!error) { | | | |
1158 | #ifndef NFS_V2_ONLY | | | |
1159 | if (v3) { | | | |
1160 | nfsm_dissect(tl, uint32_t *, NFSX_UNSIGNED); | | | |
1161 | len = fxdr_unsigned(uint32_t, *tl); | | | |
1162 | if (len > MAXPATHLEN) { | | | |
1163 | /* | | | |
1164 | * this pathname is too long for us. | | | |
1165 | */ | | | |
1166 | m_freem(mrep); | | | |
1167 | /* Solaris returns EINVAL. should we follow? */ | | | |
1168 | error = ENAMETOOLONG; | | | |
1169 | goto nfsmout; | | | |
1170 | } | | | |
1171 | } else | | | |
1172 | #endif | | | |
1173 | { | | | |
1174 | nfsm_strsiz(len, NFS_MAXPATHLEN); | | | |
1175 | } | | | |
1176 | nfsm_mtouio(uiop, len); | | | |
1177 | } | | | |
1178 | nfsm_reqdone; | | | |
1179 | return (error); | | | |
1180 | } | | | |
1181 | | | | |
1182 | /* | | | |
1183 | * nfs read rpc call | | | |
1184 | * Ditto above | | | |
1185 | */ | | | |
1186 | int | | | |
1187 | nfs_readrpc(vp, uiop) | | | |
1188 | struct vnode *vp; | | | |
1189 | struct uio *uiop; | | | |
1190 | { | | | |
1191 | u_int32_t *tl; | | | |
1192 | char *cp; | | | |
1193 | int32_t t1, t2; | | | |
1194 | char *bpos, *dpos, *cp2; | | | |
1195 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
1196 | struct nfsmount *nmp; | | | |
1197 | int error = 0, len, retlen, tsiz, eof, byte_count; | | | |
1198 | const int v3 = NFS_ISV3(vp); | | | |
1199 | struct nfsnode *np = VTONFS(vp); | | | |
1200 | #ifndef NFS_V2_ONLY | | | |
1201 | int attrflag; | | | |
1202 | #endif | | | |
1203 | | | | |
1204 | #ifndef nolint | | | |
1205 | eof = 0; | | | |
1206 | #endif | | | |
1207 | nmp = VFSTONFS(vp->v_mount); | | | |
1208 | tsiz = uiop->uio_resid; | | | |
1209 | if (uiop->uio_offset + tsiz > nmp->nm_maxfilesize) | | | |
1210 | return (EFBIG); | | | |
1211 | iostat_busy(nmp->nm_stats); | | | |
1212 | byte_count = 0; /* count bytes actually transferred */ | | | |
1213 | while (tsiz > 0) { | | | |
1214 | nfsstats.rpccnt[NFSPROC_READ]++; | | | |
1215 | len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; | | | |
1216 | nfsm_reqhead(np, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3); | | | |
1217 | nfsm_fhtom(np, v3); | | | |
1218 | nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED * 3); | | | |
1219 | #ifndef NFS_V2_ONLY | | | |
1220 | if (v3) { | | | |
1221 | txdr_hyper(uiop->uio_offset, tl); | | | |
1222 | *(tl + 2) = txdr_unsigned(len); | | | |
1223 | } else | | | |
1224 | #endif | | | |
1225 | { | | | |
1226 | *tl++ = txdr_unsigned(uiop->uio_offset); | | | |
1227 | *tl++ = txdr_unsigned(len); | | | |
1228 | *tl = 0; | | | |
1229 | } | | | |
1230 | nfsm_request(np, NFSPROC_READ, curlwp, np->n_rcred); | | | |
1231 | #ifndef NFS_V2_ONLY | | | |
1232 | if (v3) { | | | |
1233 | nfsm_postop_attr(vp, attrflag, NAC_NOTRUNC); | | | |
1234 | if (error) { | | | |
1235 | m_freem(mrep); | | | |
1236 | goto nfsmout; | | | |
1237 | } | | | |
1238 | nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED); | | | |
1239 | eof = fxdr_unsigned(int, *(tl + 1)); | | | |
1240 | } else | | | |
1241 | #endif | | | |
1242 | nfsm_loadattr(vp, (struct vattr *)0, NAC_NOTRUNC); | | | |
1243 | nfsm_strsiz(retlen, nmp->nm_rsize); | | | |
1244 | nfsm_mtouio(uiop, retlen); | | | |
1245 | m_freem(mrep); | | | |
1246 | tsiz -= retlen; | | | |
1247 | byte_count += retlen; | | | |
1248 | #ifndef NFS_V2_ONLY | | | |
1249 | if (v3) { | | | |
1250 | if (eof || retlen == 0) | | | |
1251 | tsiz = 0; | | | |
1252 | } else | | | |
1253 | #endif | | | |
1254 | if (retlen < len) | | | |
1255 | tsiz = 0; | | | |
1256 | } | | | |
1257 | nfsmout: | | | |
1258 | iostat_unbusy(nmp->nm_stats, byte_count, 1); | | | |
1259 | return (error); | | | |
1260 | } | | | |
1261 | | | | |
1262 | struct nfs_writerpc_context { | | | |
1263 | kmutex_t nwc_lock; | | | |
1264 | kcondvar_t nwc_cv; | | | |
1265 | int nwc_mbufcount; | | | |
1266 | }; | | | |
1267 | | | | |
1268 | /* | | | |
1269 | * free mbuf used to refer protected pages while write rpc call. | | | |
1270 | * called at splvm. | | | |
1271 | */ | | | |
1272 | static void | | | |
1273 | nfs_writerpc_extfree(struct mbuf *m, void *tbuf, size_t size, void *arg) | | | |
1274 | { | | | |
1275 | struct nfs_writerpc_context *ctx = arg; | | | |
1276 | | | | |
1277 | KASSERT(m != NULL); | | | |
1278 | KASSERT(ctx != NULL); | | | |
1279 | pool_cache_put(mb_cache, m); | | | |
1280 | mutex_enter(&ctx->nwc_lock); | | | |
1281 | if (--ctx->nwc_mbufcount == 0) { | | | |
1282 | cv_signal(&ctx->nwc_cv); | | | |
1283 | } | | | |
1284 | mutex_exit(&ctx->nwc_lock); | | | |
1285 | } | | | |
1286 | | | | |
1287 | /* | | | |
1288 | * nfs write call | | | |
1289 | */ | | | |
1290 | int | | | |
1291 | nfs_writerpc(vp, uiop, iomode, pageprotected, stalewriteverfp) | | | |
1292 | struct vnode *vp; | | | |
1293 | struct uio *uiop; | | | |
1294 | int *iomode; | | | |
1295 | bool pageprotected; | | | |
1296 | bool *stalewriteverfp; | | | |
1297 | { | | | |
1298 | u_int32_t *tl; | | | |
1299 | char *cp; | | | |
1300 | int32_t t1, t2; | | | |
1301 | char *bpos, *dpos; | | | |
1302 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
1303 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); | | | |
1304 | int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR; | | | |
1305 | const int v3 = NFS_ISV3(vp); | | | |
1306 | int committed = NFSV3WRITE_FILESYNC; | | | |
1307 | struct nfsnode *np = VTONFS(vp); | | | |
1308 | struct nfs_writerpc_context ctx; | | | |
1309 | int byte_count; | | | |
1310 | struct lwp *l = NULL; | | | |
1311 | size_t origresid; | | | |
1312 | #ifndef NFS_V2_ONLY | | | |
1313 | char *cp2; | | | |
1314 | int rlen, commit; | | | |
1315 | #endif | | | |
1316 | | | | |
1317 | mutex_init(&ctx.nwc_lock, MUTEX_DRIVER, IPL_VM); | | | |
1318 | cv_init(&ctx.nwc_cv, "nfsmblk"); | | | |
1319 | ctx.nwc_mbufcount = 1; | | | |
1320 | | | | |
1321 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { | | | |
1322 | panic("writerpc readonly vp %p", vp); | | | |
1323 | } | | | |
1324 | | | | |
1325 | #ifdef DIAGNOSTIC | | | |
1326 | if (uiop->uio_iovcnt != 1) | | | |
1327 | panic("nfs: writerpc iovcnt > 1"); | | | |
1328 | #endif | | | |
1329 | tsiz = uiop->uio_resid; | | | |
1330 | if (uiop->uio_offset + tsiz > nmp->nm_maxfilesize) | | | |
1331 | return (EFBIG); | | | |
1332 | if (pageprotected) { | | | |
1333 | l = curlwp; | | | |
1334 | uvm_lwp_hold(l); | | | |
1335 | } | | | |
1336 | retry: | | | |
1337 | origresid = uiop->uio_resid; | | | |
1338 | KASSERT(origresid == uiop->uio_iov->iov_len); | | | |
1339 | iostat_busy(nmp->nm_stats); | | | |
1340 | byte_count = 0; /* count of bytes actually written */ | | | |
1341 | while (tsiz > 0) { | | | |
1342 | uint32_t datalen; /* data bytes need to be allocated in mbuf */ | | | |
1343 | uint32_t backup; | | | |
1344 | bool stalewriteverf = false; | | | |
1345 | | | | |
1346 | nfsstats.rpccnt[NFSPROC_WRITE]++; | | | |
1347 | len = min(tsiz, nmp->nm_wsize); | | | |
1348 | datalen = pageprotected ? 0 : nfsm_rndup(len); | | | |
1349 | nfsm_reqhead(np, NFSPROC_WRITE, | | | |
1350 | NFSX_FH(v3) + 5 * NFSX_UNSIGNED + datalen); | | | |
1351 | nfsm_fhtom(np, v3); | | | |
1352 | #ifndef NFS_V2_ONLY | | | |
1353 | if (v3) { | | | |
1354 | nfsm_build(tl, u_int32_t *, 5 * NFSX_UNSIGNED); | | | |
1355 | txdr_hyper(uiop->uio_offset, tl); | | | |
1356 | tl += 2; | | | |
1357 | *tl++ = txdr_unsigned(len); | | | |
1358 | *tl++ = txdr_unsigned(*iomode); | | | |
1359 | *tl = txdr_unsigned(len); | | | |
1360 | } else | | | |
1361 | #endif | | | |
1362 | { | | | |
1363 | u_int32_t x; | | | |
1364 | | | | |
1365 | nfsm_build(tl, u_int32_t *, 4 * NFSX_UNSIGNED); | | | |
1366 | /* Set both "begin" and "current" to non-garbage. */ | | | |
1367 | x = txdr_unsigned((u_int32_t)uiop->uio_offset); | | | |
1368 | *tl++ = x; /* "begin offset" */ | | | |
1369 | *tl++ = x; /* "current offset" */ | | | |
1370 | x = txdr_unsigned(len); | | | |
1371 | *tl++ = x; /* total to this offset */ | | | |
1372 | *tl = x; /* size of this write */ | | | |
1373 | | | | |
1374 | } | | | |
1375 | if (pageprotected) { | | | |
1376 | /* | | | |
1377 | * since we know pages can't be modified during i/o, | | | |
1378 | * no need to copy them for us. | | | |
1379 | */ | | | |
1380 | struct mbuf *m; | | | |
1381 | struct iovec *iovp = uiop->uio_iov; | | | |
1382 | | | | |
1383 | m = m_get(M_WAIT, MT_DATA); | | | |
1384 | MCLAIM(m, &nfs_mowner); | | | |
1385 | MEXTADD(m, iovp->iov_base, len, M_MBUF, | | | |
1386 | nfs_writerpc_extfree, &ctx); | | | |
1387 | m->m_flags |= M_EXT_ROMAP; | | | |
1388 | m->m_len = len; | | | |
1389 | mb->m_next = m; | | | |
1390 | /* | | | |
1391 | * no need to maintain mb and bpos here | | | |
1392 | * because no one care them later. | | | |
1393 | */ | | | |
1394 | #if 0 | | | |
1395 | mb = m; | | | |
1396 | bpos = mtod(void *, mb) + mb->m_len; | | | |
1397 | #endif | | | |
1398 | UIO_ADVANCE(uiop, len); | | | |
1399 | uiop->uio_offset += len; | | | |
1400 | mutex_enter(&ctx.nwc_lock); | | | |
1401 | ctx.nwc_mbufcount++; | | | |
1402 | mutex_exit(&ctx.nwc_lock); | | | |
1403 | nfs_zeropad(mb, 0, nfsm_padlen(len)); | | | |
1404 | } else { | | | |
1405 | nfsm_uiotom(uiop, len); | | | |
1406 | } | | | |
1407 | nfsm_request(np, NFSPROC_WRITE, curlwp, np->n_wcred); | | | |
1408 | #ifndef NFS_V2_ONLY | | | |
1409 | if (v3) { | | | |
1410 | wccflag = NFSV3_WCCCHK; | | | |
1411 | nfsm_wcc_data(vp, wccflag, NAC_NOTRUNC, !error); | | | |
1412 | if (!error) { | | | |
1413 | nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED | | | |
1414 | + NFSX_V3WRITEVERF); | | | |
1415 | rlen = fxdr_unsigned(int, *tl++); | | | |
1416 | if (rlen == 0) { | | | |
1417 | error = NFSERR_IO; | | | |
1418 | m_freem(mrep); | | | |
1419 | break; | | | |
1420 | } else if (rlen < len) { | | | |
1421 | backup = len - rlen; | | | |
1422 | UIO_ADVANCE(uiop, -backup); | | | |
1423 | uiop->uio_offset -= backup; | | | |
1424 | len = rlen; | | | |
1425 | } | | | |
1426 | commit = fxdr_unsigned(int, *tl++); | | | |
1427 | | | | |
1428 | /* | | | |
1429 | * Return the lowest committment level | | | |
1430 | * obtained by any of the RPCs. | | | |
1431 | */ | | | |
1432 | if (committed == NFSV3WRITE_FILESYNC) | | | |
1433 | committed = commit; | | | |
1434 | else if (committed == NFSV3WRITE_DATASYNC && | | | |
1435 | commit == NFSV3WRITE_UNSTABLE) | | | |
1436 | committed = commit; | | | |
1437 | mutex_enter(&nmp->nm_lock); | | | |
1438 | if ((nmp->nm_iflag & NFSMNT_HASWRITEVERF) == 0){ | | | |
1439 | memcpy(nmp->nm_writeverf, tl, | | | |
1440 | NFSX_V3WRITEVERF); | | | |
1441 | nmp->nm_iflag |= NFSMNT_HASWRITEVERF; | | | |
1442 | } else if ((nmp->nm_iflag & | | | |
1443 | NFSMNT_STALEWRITEVERF) || | | | |
1444 | memcmp(tl, nmp->nm_writeverf, | | | |
1445 | NFSX_V3WRITEVERF)) { | | | |
1446 | memcpy(nmp->nm_writeverf, tl, | | | |
1447 | NFSX_V3WRITEVERF); | | | |
1448 | /* | | | |
1449 | * note NFSMNT_STALEWRITEVERF | | | |
1450 | * if we're the first thread to | | | |
1451 | * notice it. | | | |
1452 | */ | | | |
1453 | if ((nmp->nm_iflag & | | | |
1454 | NFSMNT_STALEWRITEVERF) == 0) { | | | |
1455 | stalewriteverf = true; | | | |
1456 | nmp->nm_iflag |= | | | |
1457 | NFSMNT_STALEWRITEVERF; | | | |
1458 | } | | | |
1459 | } | | | |
1460 | mutex_exit(&nmp->nm_lock); | | | |
1461 | } | | | |
1462 | } else | | | |
1463 | #endif | | | |
1464 | nfsm_loadattr(vp, (struct vattr *)0, NAC_NOTRUNC); | | | |
1465 | if (wccflag) | | | |
1466 | VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr->va_mtime; | | | |
1467 | m_freem(mrep); | | | |
1468 | if (error) | | | |
1469 | break; | | | |
1470 | tsiz -= len; | | | |
1471 | byte_count += len; | | | |
1472 | if (stalewriteverf) { | | | |
1473 | *stalewriteverfp = true; | | | |
1474 | stalewriteverf = false; | | | |
1475 | if (committed == NFSV3WRITE_UNSTABLE && | | | |
1476 | len != origresid) { | | | |
1477 | /* | | | |
1478 | * if our write requests weren't atomic but | | | |
1479 | * unstable, datas in previous iterations | | | |
1480 | * might have already been lost now. | | | |
1481 | * then, we should resend them to nfsd. | | | |
1482 | */ | | | |
1483 | backup = origresid - tsiz; | | | |
1484 | UIO_ADVANCE(uiop, -backup); | | | |
1485 | uiop->uio_offset -= backup; | | | |
1486 | tsiz = origresid; | | | |
1487 | goto retry; | | | |
1488 | } | | | |
1489 | } | | | |
1490 | } | | | |
1491 | nfsmout: | | | |
1492 | iostat_unbusy(nmp->nm_stats, byte_count, 0); | | | |
1493 | if (pageprotected) { | | | |
1494 | /* | | | |
1495 | * wait until mbufs go away. | | | |
1496 | * retransmitted mbufs can survive longer than rpc requests | | | |
1497 | * themselves. | | | |
1498 | */ | | | |
1499 | mutex_enter(&ctx.nwc_lock); | | | |
1500 | ctx.nwc_mbufcount--; | | | |
1501 | while (ctx.nwc_mbufcount > 0) { | | | |
1502 | cv_wait(&ctx.nwc_cv, &ctx.nwc_lock); | | | |
1503 | } | | | |
1504 | mutex_exit(&ctx.nwc_lock); | | | |
1505 | uvm_lwp_rele(l); | | | |
1506 | } | | | |
1507 | mutex_destroy(&ctx.nwc_lock); | | | |
1508 | cv_destroy(&ctx.nwc_cv); | | | |
1509 | *iomode = committed; | | | |
1510 | if (error) | | | |
1511 | uiop->uio_resid = tsiz; | | | |
1512 | return (error); | | | |
1513 | } | | | |
1514 | | | | |
1515 | /* | | | |
1516 | * nfs mknod rpc | | | |
1517 | * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the | | | |
1518 | * mode set to specify the file type and the size field for rdev. | | | |
1519 | */ | | | |
1520 | int | | | |
1521 | nfs_mknodrpc(dvp, vpp, cnp, vap) | | | |
1522 | struct vnode *dvp; | | | |
1523 | struct vnode **vpp; | | | |
1524 | struct componentname *cnp; | | | |
1525 | struct vattr *vap; | | | |
1526 | { | | | |
1527 | struct nfsv2_sattr *sp; | | | |
1528 | u_int32_t *tl; | | | |
1529 | char *cp; | | | |
1530 | int32_t t1, t2; | | | |
1531 | struct vnode *newvp = (struct vnode *)0; | | | |
1532 | struct nfsnode *dnp, *np; | | | |
1533 | char *cp2; | | | |
1534 | char *bpos, *dpos; | | | |
1535 | int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0; | | | |
1536 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
1537 | u_int32_t rdev; | | | |
1538 | const int v3 = NFS_ISV3(dvp); | | | |
1539 | | | | |
1540 | if (vap->va_type == VCHR || vap->va_type == VBLK) | | | |
1541 | rdev = txdr_unsigned(vap->va_rdev); | | | |
1542 | else if (vap->va_type == VFIFO || vap->va_type == VSOCK) | | | |
1543 | rdev = nfs_xdrneg1; | | | |
1544 | else { | | | |
1545 | VOP_ABORTOP(dvp, cnp); | | | |
1546 | vput(dvp); | | | |
1547 | return (EOPNOTSUPP); | | | |
1548 | } | | | |
1549 | nfsstats.rpccnt[NFSPROC_MKNOD]++; | | | |
1550 | dnp = VTONFS(dvp); | | | |
1551 | nfsm_reqhead(dnp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED + | | | |
1552 | + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); | | | |
1553 | nfsm_fhtom(dnp, v3); | | | |
1554 | nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); | | | |
1555 | #ifndef NFS_V2_ONLY | | | |
1556 | if (v3) { | | | |
1557 | nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
1558 | *tl++ = vtonfsv3_type(vap->va_type); | | | |
1559 | nfsm_v3attrbuild(vap, false); | | | |
1560 | if (vap->va_type == VCHR || vap->va_type == VBLK) { | | | |
1561 | nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED); | | | |
1562 | *tl++ = txdr_unsigned(major(vap->va_rdev)); | | | |
1563 | *tl = txdr_unsigned(minor(vap->va_rdev)); | | | |
1564 | } | | | |
1565 | } else | | | |
1566 | #endif | | | |
1567 | { | | | |
1568 | nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); | | | |
1569 | sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); | | | |
1570 | sp->sa_uid = nfs_xdrneg1; | | | |
1571 | sp->sa_gid = nfs_xdrneg1; | | | |
1572 | sp->sa_size = rdev; | | | |
1573 | txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); | | | |
1574 | txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); | | | |
1575 | } | | | |
1576 | nfsm_request(dnp, NFSPROC_MKNOD, curlwp, cnp->cn_cred); | | | |
1577 | if (!error) { | | | |
1578 | nfsm_mtofh(dvp, newvp, v3, gotvp); | | | |
1579 | if (!gotvp) { | | | |
1580 | error = nfs_lookitup(dvp, cnp->cn_nameptr, | | | |
1581 | cnp->cn_namelen, cnp->cn_cred, curlwp, &np); | | | |
1582 | if (!error) | | | |
1583 | newvp = NFSTOV(np); | | | |
1584 | } | | | |
1585 | } | | | |
1586 | #ifndef NFS_V2_ONLY | | | |
1587 | if (v3) | | | |
1588 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
1589 | #endif | | | |
1590 | nfsm_reqdone; | | | |
1591 | if (error) { | | | |
1592 | if (newvp) | | | |
1593 | vput(newvp); | | | |
1594 | } else { | | | |
1595 | if (cnp->cn_flags & MAKEENTRY) | | | |
1596 | nfs_cache_enter(dvp, newvp, cnp); | | | |
1597 | *vpp = newvp; | | | |
1598 | } | | | |
1599 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
1600 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
1601 | if (!wccflag) | | | |
1602 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
1603 | vput(dvp); | | | |
1604 | return (error); | | | |
1605 | } | | | |
1606 | | | | |
1607 | /* | | | |
1608 | * nfs mknod vop | | | |
1609 | * just call nfs_mknodrpc() to do the work. | | | |
1610 | */ | | | |
1611 | /* ARGSUSED */ | | | |
1612 | int | | | |
1613 | nfs_mknod(v) | | | |
1614 | void *v; | | | |
1615 | { | | | |
1616 | struct vop_mknod_args /* { | | | |
1617 | struct vnode *a_dvp; | | | |
1618 | struct vnode **a_vpp; | | | |
1619 | struct componentname *a_cnp; | | | |
1620 | struct vattr *a_vap; | | | |
1621 | } */ *ap = v; | | | |
1622 | struct vnode *dvp = ap->a_dvp; | | | |
1623 | struct componentname *cnp = ap->a_cnp; | | | |
1624 | int error; | | | |
1625 | | | | |
1626 | error = nfs_mknodrpc(dvp, ap->a_vpp, cnp, ap->a_vap); | | | |
1627 | VN_KNOTE(dvp, NOTE_WRITE); | | | |
1628 | if (error == 0 || error == EEXIST) | | | |
1629 | cache_purge1(dvp, cnp, 0); | | | |
1630 | return (error); | | | |
1631 | } | | | |
1632 | | | | |
1633 | /* | | | |
1634 | * nfs file create call | | | |
1635 | */ | | | |
1636 | int | | | |
1637 | nfs_create(v) | | | |
1638 | void *v; | | | |
1639 | { | | | |
1640 | struct vop_create_args /* { | | | |
1641 | struct vnode *a_dvp; | | | |
1642 | struct vnode **a_vpp; | | | |
1643 | struct componentname *a_cnp; | | | |
1644 | struct vattr *a_vap; | | | |
1645 | } */ *ap = v; | | | |
1646 | struct vnode *dvp = ap->a_dvp; | | | |
1647 | struct vattr *vap = ap->a_vap; | | | |
1648 | struct componentname *cnp = ap->a_cnp; | | | |
1649 | struct nfsv2_sattr *sp; | | | |
1650 | u_int32_t *tl; | | | |
1651 | char *cp; | | | |
1652 | int32_t t1, t2; | | | |
1653 | struct nfsnode *dnp, *np = (struct nfsnode *)0; | | | |
1654 | struct vnode *newvp = (struct vnode *)0; | | | |
1655 | char *bpos, *dpos, *cp2; | | | |
1656 | int error, wccflag = NFSV3_WCCRATTR, gotvp = 0; | | | |
1657 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
1658 | const int v3 = NFS_ISV3(dvp); | | | |
1659 | u_int32_t excl_mode = NFSV3CREATE_UNCHECKED; | | | |
1660 | | | | |
1661 | /* | | | |
1662 | * Oops, not for me.. | | | |
1663 | */ | | | |
1664 | if (vap->va_type == VSOCK) | | | |
1665 | return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap)); | | | |
1666 | | | | |
1667 | KASSERT(vap->va_type == VREG); | | | |
1668 | | | | |
1669 | #ifdef VA_EXCLUSIVE | | | |
1670 | if (vap->va_vaflags & VA_EXCLUSIVE) { | | | |
1671 | excl_mode = NFSV3CREATE_EXCLUSIVE; | | | |
1672 | } | | | |
1673 | #endif | | | |
1674 | again: | | | |
1675 | error = 0; | | | |
1676 | nfsstats.rpccnt[NFSPROC_CREATE]++; | | | |
1677 | dnp = VTONFS(dvp); | | | |
1678 | nfsm_reqhead(dnp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED + | | | |
1679 | nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); | | | |
1680 | nfsm_fhtom(dnp, v3); | | | |
1681 | nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); | | | |
1682 | #ifndef NFS_V2_ONLY | | | |
1683 | if (v3) { | | | |
1684 | nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
1685 | if (excl_mode == NFSV3CREATE_EXCLUSIVE) { | | | |
1686 | *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE); | | | |
1687 | nfsm_build(tl, u_int32_t *, NFSX_V3CREATEVERF); | | | |
1688 | *tl++ = arc4random(); | | | |
1689 | *tl = arc4random(); | | | |
1690 | } else { | | | |
1691 | *tl = txdr_unsigned(excl_mode); | | | |
1692 | nfsm_v3attrbuild(vap, false); | | | |
1693 | } | | | |
1694 | } else | | | |
1695 | #endif | | | |
1696 | { | | | |
1697 | nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); | | | |
1698 | sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); | | | |
1699 | sp->sa_uid = nfs_xdrneg1; | | | |
1700 | sp->sa_gid = nfs_xdrneg1; | | | |
1701 | sp->sa_size = 0; | | | |
1702 | txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); | | | |
1703 | txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); | | | |
1704 | } | | | |
1705 | nfsm_request(dnp, NFSPROC_CREATE, curlwp, cnp->cn_cred); | | | |
1706 | if (!error) { | | | |
1707 | nfsm_mtofh(dvp, newvp, v3, gotvp); | | | |
1708 | if (!gotvp) { | | | |
1709 | error = nfs_lookitup(dvp, cnp->cn_nameptr, | | | |
1710 | cnp->cn_namelen, cnp->cn_cred, curlwp, &np); | | | |
1711 | if (!error) | | | |
1712 | newvp = NFSTOV(np); | | | |
1713 | } | | | |
1714 | } | | | |
1715 | #ifndef NFS_V2_ONLY | | | |
1716 | if (v3) | | | |
1717 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
1718 | #endif | | | |
1719 | nfsm_reqdone; | | | |
1720 | if (error) { | | | |
1721 | /* | | | |
1722 | * nfs_request maps NFSERR_NOTSUPP to ENOTSUP. | | | |
1723 | */ | | | |
1724 | if (v3 && error == ENOTSUP) { | | | |
1725 | if (excl_mode == NFSV3CREATE_EXCLUSIVE) { | | | |
1726 | excl_mode = NFSV3CREATE_GUARDED; | | | |
1727 | goto again; | | | |
1728 | } else if (excl_mode == NFSV3CREATE_GUARDED) { | | | |
1729 | excl_mode = NFSV3CREATE_UNCHECKED; | | | |
1730 | goto again; | | | |
1731 | } | | | |
1732 | } | | | |
1733 | } else if (v3 && (excl_mode == NFSV3CREATE_EXCLUSIVE)) { | | | |
1734 | struct timespec ts; | | | |
1735 | | | | |
1736 | getnanotime(&ts); | | | |
1737 | | | | |
1738 | /* | | | |
1739 | * make sure that we'll update timestamps as | | | |
1740 | * most server implementations use them to store | | | |
1741 | * the create verifier. | | | |
1742 | * | | | |
1743 | * XXX it's better to use TOSERVER always. | | | |
1744 | */ | | | |
1745 | | | | |
1746 | if (vap->va_atime.tv_sec == VNOVAL) | | | |
1747 | vap->va_atime = ts; | | | |
1748 | if (vap->va_mtime.tv_sec == VNOVAL) | | | |
1749 | vap->va_mtime = ts; | | | |
1750 | | | | |
1751 | error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, curlwp); | | | |
1752 | } | | | |
1753 | if (error == 0) { | | | |
1754 | if (cnp->cn_flags & MAKEENTRY) | | | |
1755 | nfs_cache_enter(dvp, newvp, cnp); | | | |
1756 | else | | | |
1757 | cache_purge1(dvp, cnp, 0); | | | |
1758 | *ap->a_vpp = newvp; | | | |
1759 | } else { | | | |
1760 | if (newvp) | | | |
1761 | vput(newvp); | | | |
1762 | if (error == EEXIST) | | | |
1763 | cache_purge1(dvp, cnp, 0); | | | |
1764 | } | | | |
1765 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
1766 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
1767 | if (!wccflag) | | | |
1768 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
1769 | VN_KNOTE(ap->a_dvp, NOTE_WRITE); | | | |
1770 | vput(dvp); | | | |
1771 | return (error); | | | |
1772 | } | | | |
1773 | | | | |
1774 | /* | | | |
1775 | * nfs file remove call | | | |
1776 | * To try and make nfs semantics closer to ufs semantics, a file that has | | | |
1777 | * other processes using the vnode is renamed instead of removed and then | | | |
1778 | * removed later on the last close. | | | |
1779 | * - If v_usecount > 1 | | | |
1780 | * If a rename is not already in the works | | | |
1781 | * call nfs_sillyrename() to set it up | | | |
1782 | * else | | | |
1783 | * do the remove rpc | | | |
1784 | */ | | | |
1785 | int | | | |
1786 | nfs_remove(v) | | | |
1787 | void *v; | | | |
1788 | { | | | |
1789 | struct vop_remove_args /* { | | | |
1790 | struct vnodeop_desc *a_desc; | | | |
1791 | struct vnode * a_dvp; | | | |
1792 | struct vnode * a_vp; | | | |
1793 | struct componentname * a_cnp; | | | |
1794 | } */ *ap = v; | | | |
1795 | struct vnode *vp = ap->a_vp; | | | |
1796 | struct vnode *dvp = ap->a_dvp; | | | |
1797 | struct componentname *cnp = ap->a_cnp; | | | |
1798 | struct nfsnode *np = VTONFS(vp); | | | |
1799 | int error = 0; | | | |
1800 | struct vattr vattr; | | | |
1801 | | | | |
1802 | #ifndef DIAGNOSTIC | | | |
1803 | if ((cnp->cn_flags & HASBUF) == 0) | | | |
1804 | panic("nfs_remove: no name"); | | | |
1805 | if (vp->v_usecount < 1) | | | |
1806 | panic("nfs_remove: bad v_usecount"); | | | |
1807 | #endif | | | |
1808 | if (vp->v_type == VDIR) | | | |
1809 | error = EPERM; | | | |
1810 | else if (vp->v_usecount == 1 || (np->n_sillyrename && | | | |
1811 | VOP_GETATTR(vp, &vattr, cnp->cn_cred) == 0 && | | | |
1812 | vattr.va_nlink > 1)) { | | | |
1813 | /* | | | |
1814 | * Purge the name cache so that the chance of a lookup for | | | |
1815 | * the name succeeding while the remove is in progress is | | | |
1816 | * minimized. Without node locking it can still happen, such | | | |
1817 | * that an I/O op returns ESTALE, but since you get this if | | | |
1818 | * another host removes the file.. | | | |
1819 | */ | | | |
1820 | cache_purge(vp); | | | |
1821 | /* | | | |
1822 | * throw away biocache buffers, mainly to avoid | | | |
1823 | * unnecessary delayed writes later. | | | |
1824 | */ | | | |
1825 | error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, curlwp, 1); | | | |
1826 | /* Do the rpc */ | | | |
1827 | if (error != EINTR) | | | |
1828 | error = nfs_removerpc(dvp, cnp->cn_nameptr, | | | |
1829 | cnp->cn_namelen, cnp->cn_cred, curlwp); | | | |
1830 | } else if (!np->n_sillyrename) | | | |
1831 | error = nfs_sillyrename(dvp, vp, cnp, false); | | | |
1832 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
1833 | if (!error && nfs_getattrcache(vp, &vattr) == 0 && | | | |
1834 | vattr.va_nlink == 1) { | | | |
1835 | np->n_flag |= NREMOVED; | | | |
1836 | } | | | |
1837 | NFS_INVALIDATE_ATTRCACHE(np); | | | |
1838 | VN_KNOTE(vp, NOTE_DELETE); | | | |
1839 | VN_KNOTE(dvp, NOTE_WRITE); | | | |
1840 | if (dvp == vp) | | | |
1841 | vrele(vp); | | | |
1842 | else | | | |
1843 | vput(vp); | | | |
1844 | vput(dvp); | | | |
1845 | return (error); | | | |
1846 | } | | | |
1847 | | | | |
1848 | /* | | | |
1849 | * nfs file remove rpc called from nfs_inactive | | | |
1850 | */ | | | |
1851 | int | | | |
1852 | nfs_removeit(sp) | | | |
1853 | struct sillyrename *sp; | | | |
1854 | { | | | |
1855 | | | | |
1856 | return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, | | | |
1857 | (struct lwp *)0)); | | | |
1858 | } | | | |
1859 | | | | |
1860 | /* | | | |
1861 | * Nfs remove rpc, called from nfs_remove() and nfs_removeit(). | | | |
1862 | */ | | | |
1863 | int | | | |
1864 | nfs_removerpc(dvp, name, namelen, cred, l) | | | |
1865 | struct vnode *dvp; | | | |
1866 | const char *name; | | | |
1867 | int namelen; | | | |
1868 | kauth_cred_t cred; | | | |
1869 | struct lwp *l; | | | |
1870 | { | | | |
1871 | u_int32_t *tl; | | | |
1872 | char *cp; | | | |
1873 | #ifndef NFS_V2_ONLY | | | |
1874 | int32_t t1; | | | |
1875 | char *cp2; | | | |
1876 | #endif | | | |
1877 | int32_t t2; | | | |
1878 | char *bpos, *dpos; | | | |
1879 | int error = 0, wccflag = NFSV3_WCCRATTR; | | | |
1880 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
1881 | const int v3 = NFS_ISV3(dvp); | | | |
1882 | int rexmit = 0; | | | |
1883 | struct nfsnode *dnp = VTONFS(dvp); | | | |
1884 | | | | |
1885 | nfsstats.rpccnt[NFSPROC_REMOVE]++; | | | |
1886 | nfsm_reqhead(dnp, NFSPROC_REMOVE, | | | |
1887 | NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen)); | | | |
1888 | nfsm_fhtom(dnp, v3); | | | |
1889 | nfsm_strtom(name, namelen, NFS_MAXNAMLEN); | | | |
1890 | nfsm_request1(dnp, NFSPROC_REMOVE, l, cred, &rexmit); | | | |
1891 | #ifndef NFS_V2_ONLY | | | |
1892 | if (v3) | | | |
1893 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
1894 | #endif | | | |
1895 | nfsm_reqdone; | | | |
1896 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
1897 | if (!wccflag) | | | |
1898 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
1899 | /* | | | |
1900 | * Kludge City: If the first reply to the remove rpc is lost.. | | | |
1901 | * the reply to the retransmitted request will be ENOENT | | | |
1902 | * since the file was in fact removed | | | |
1903 | * Therefore, we cheat and return success. | | | |
1904 | */ | | | |
1905 | if (rexmit && error == ENOENT) | | | |
1906 | error = 0; | | | |
1907 | return (error); | | | |
1908 | } | | | |
1909 | | | | |
1910 | /* | | | |
1911 | * nfs file rename call | | | |
1912 | */ | | | |
1913 | int | | | |
1914 | nfs_rename(v) | | | |
1915 | void *v; | | | |
1916 | { | | | |
1917 | struct vop_rename_args /* { | | | |
1918 | struct vnode *a_fdvp; | | | |
1919 | struct vnode *a_fvp; | | | |
1920 | struct componentname *a_fcnp; | | | |
1921 | struct vnode *a_tdvp; | | | |
1922 | struct vnode *a_tvp; | | | |
1923 | struct componentname *a_tcnp; | | | |
1924 | } */ *ap = v; | | | |
1925 | struct vnode *fvp = ap->a_fvp; | | | |
1926 | struct vnode *tvp = ap->a_tvp; | | | |
1927 | struct vnode *fdvp = ap->a_fdvp; | | | |
1928 | struct vnode *tdvp = ap->a_tdvp; | | | |
1929 | struct componentname *tcnp = ap->a_tcnp; | | | |
1930 | struct componentname *fcnp = ap->a_fcnp; | | | |
1931 | int error; | | | |
1932 | | | | |
1933 | #ifndef DIAGNOSTIC | | | |
1934 | if ((tcnp->cn_flags & HASBUF) == 0 || | | | |
1935 | (fcnp->cn_flags & HASBUF) == 0) | | | |
1936 | panic("nfs_rename: no name"); | | | |
1937 | #endif | | | |
1938 | /* Check for cross-device rename */ | | | |
1939 | if ((fvp->v_mount != tdvp->v_mount) || | | | |
1940 | (tvp && (fvp->v_mount != tvp->v_mount))) { | | | |
1941 | error = EXDEV; | | | |
1942 | goto out; | | | |
1943 | } | | | |
1944 | | | | |
1945 | /* | | | |
1946 | * If the tvp exists and is in use, sillyrename it before doing the | | | |
1947 | * rename of the new file over it. | | | |
1948 | * | | | |
1949 | * Have sillyrename use link instead of rename if possible, | | | |
1950 | * so that we don't lose the file if the rename fails, and so | | | |
1951 | * that there's no window when the "to" file doesn't exist. | | | |
1952 | */ | | | |
1953 | if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename && | | | |
1954 | tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp, true)) { | | | |
1955 | VN_KNOTE(tvp, NOTE_DELETE); | | | |
1956 | vput(tvp); | | | |
1957 | tvp = NULL; | | | |
1958 | } | | | |
1959 | | | | |
1960 | error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen, | | | |
1961 | tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred, | | | |
1962 | curlwp); | | | |
1963 | | | | |
1964 | VN_KNOTE(fdvp, NOTE_WRITE); | | | |
1965 | VN_KNOTE(tdvp, NOTE_WRITE); | | | |
1966 | if (error == 0 || error == EEXIST) { | | | |
1967 | if (fvp->v_type == VDIR) | | | |
1968 | cache_purge(fvp); | | | |
1969 | else | | | |
1970 | cache_purge1(fdvp, fcnp, 0); | | | |
1971 | if (tvp != NULL && tvp->v_type == VDIR) | | | |
1972 | cache_purge(tvp); | | | |
1973 | else | | | |
1974 | cache_purge1(tdvp, tcnp, 0); | | | |
1975 | } | | | |
1976 | out: | | | |
1977 | if (tdvp == tvp) | | | |
1978 | vrele(tdvp); | | | |
1979 | else | | | |
1980 | vput(tdvp); | | | |
1981 | if (tvp) | | | |
1982 | vput(tvp); | | | |
1983 | vrele(fdvp); | | | |
1984 | vrele(fvp); | | | |
1985 | return (error); | | | |
1986 | } | | | |
1987 | | | | |
1988 | /* | | | |
1989 | * nfs file rename rpc called from nfs_remove() above | | | |
1990 | */ | | | |
1991 | int | | | |
1992 | nfs_renameit(sdvp, scnp, sp) | | | |
1993 | struct vnode *sdvp; | | | |
1994 | struct componentname *scnp; | | | |
1995 | struct sillyrename *sp; | | | |
1996 | { | | | |
1997 | return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen, | | | |
1998 | sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, curlwp)); | | | |
1999 | } | | | |
2000 | | | | |
2001 | /* | | | |
2002 | * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit(). | | | |
2003 | */ | | | |
2004 | int | | | |
2005 | nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, l) | | | |
2006 | struct vnode *fdvp; | | | |
2007 | const char *fnameptr; | | | |
2008 | int fnamelen; | | | |
2009 | struct vnode *tdvp; | | | |
2010 | const char *tnameptr; | | | |
2011 | int tnamelen; | | | |
2012 | kauth_cred_t cred; | | | |
2013 | struct lwp *l; | | | |
2014 | { | | | |
2015 | u_int32_t *tl; | | | |
2016 | char *cp; | | | |
2017 | #ifndef NFS_V2_ONLY | | | |
2018 | int32_t t1; | | | |
2019 | char *cp2; | | | |
2020 | #endif | | | |
2021 | int32_t t2; | | | |
2022 | char *bpos, *dpos; | | | |
2023 | int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR; | | | |
2024 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2025 | const int v3 = NFS_ISV3(fdvp); | | | |
2026 | int rexmit = 0; | | | |
2027 | struct nfsnode *fdnp = VTONFS(fdvp); | | | |
2028 | | | | |
2029 | nfsstats.rpccnt[NFSPROC_RENAME]++; | | | |
2030 | nfsm_reqhead(fdnp, NFSPROC_RENAME, | | | |
2031 | (NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) + | | | |
2032 | nfsm_rndup(tnamelen)); | | | |
2033 | nfsm_fhtom(fdnp, v3); | | | |
2034 | nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN); | | | |
2035 | nfsm_fhtom(VTONFS(tdvp), v3); | | | |
2036 | nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN); | | | |
2037 | nfsm_request1(fdnp, NFSPROC_RENAME, l, cred, &rexmit); | | | |
2038 | #ifndef NFS_V2_ONLY | | | |
2039 | if (v3) { | | | |
2040 | nfsm_wcc_data(fdvp, fwccflag, 0, !error); | | | |
2041 | nfsm_wcc_data(tdvp, twccflag, 0, !error); | | | |
2042 | } | | | |
2043 | #endif | | | |
2044 | nfsm_reqdone; | | | |
2045 | VTONFS(fdvp)->n_flag |= NMODIFIED; | | | |
2046 | VTONFS(tdvp)->n_flag |= NMODIFIED; | | | |
2047 | if (!fwccflag) | | | |
2048 | NFS_INVALIDATE_ATTRCACHE(VTONFS(fdvp)); | | | |
2049 | if (!twccflag) | | | |
2050 | NFS_INVALIDATE_ATTRCACHE(VTONFS(tdvp)); | | | |
2051 | /* | | | |
2052 | * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. | | | |
2053 | */ | | | |
2054 | if (rexmit && error == ENOENT) | | | |
2055 | error = 0; | | | |
2056 | return (error); | | | |
2057 | } | | | |
2058 | | | | |
2059 | /* | | | |
2060 | * NFS link RPC, called from nfs_link. | | | |
2061 | * Assumes dvp and vp locked, and leaves them that way. | | | |
2062 | */ | | | |
2063 | | | | |
2064 | static int | | | |
2065 | nfs_linkrpc(struct vnode *dvp, struct vnode *vp, const char *name, | | | |
2066 | size_t namelen, kauth_cred_t cred, struct lwp *l) | | | |
2067 | { | | | |
2068 | u_int32_t *tl; | | | |
2069 | char *cp; | | | |
2070 | #ifndef NFS_V2_ONLY | | | |
2071 | int32_t t1; | | | |
2072 | char *cp2; | | | |
2073 | #endif | | | |
2074 | int32_t t2; | | | |
2075 | char *bpos, *dpos; | | | |
2076 | int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0; | | | |
2077 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2078 | const int v3 = NFS_ISV3(dvp); | | | |
2079 | int rexmit = 0; | | | |
2080 | struct nfsnode *np = VTONFS(vp); | | | |
2081 | | | | |
2082 | nfsstats.rpccnt[NFSPROC_LINK]++; | | | |
2083 | nfsm_reqhead(np, NFSPROC_LINK, | | | |
2084 | NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(namelen)); | | | |
2085 | nfsm_fhtom(np, v3); | | | |
2086 | nfsm_fhtom(VTONFS(dvp), v3); | | | |
2087 | nfsm_strtom(name, namelen, NFS_MAXNAMLEN); | | | |
2088 | nfsm_request1(np, NFSPROC_LINK, l, cred, &rexmit); | | | |
2089 | #ifndef NFS_V2_ONLY | | | |
2090 | if (v3) { | | | |
2091 | nfsm_postop_attr(vp, attrflag, 0); | | | |
2092 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
2093 | } | | | |
2094 | #endif | | | |
2095 | nfsm_reqdone; | | | |
2096 | | | | |
2097 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
2098 | if (!attrflag) | | | |
2099 | NFS_INVALIDATE_ATTRCACHE(VTONFS(vp)); | | | |
2100 | if (!wccflag) | | | |
2101 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
2102 | | | | |
2103 | /* | | | |
2104 | * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. | | | |
2105 | */ | | | |
2106 | if (rexmit && error == EEXIST) | | | |
2107 | error = 0; | | | |
2108 | | | | |
2109 | return error; | | | |
2110 | } | | | |
2111 | | | | |
2112 | /* | | | |
2113 | * nfs hard link create call | | | |
2114 | */ | | | |
2115 | int | | | |
2116 | nfs_link(v) | | | |
2117 | void *v; | | | |
2118 | { | | | |
2119 | struct vop_link_args /* { | | | |
2120 | struct vnode *a_dvp; | | | |
2121 | struct vnode *a_vp; | | | |
2122 | struct componentname *a_cnp; | | | |
2123 | } */ *ap = v; | | | |
2124 | struct vnode *vp = ap->a_vp; | | | |
2125 | struct vnode *dvp = ap->a_dvp; | | | |
2126 | struct componentname *cnp = ap->a_cnp; | | | |
2127 | int error = 0; | | | |
2128 | | | | |
2129 | if (dvp->v_mount != vp->v_mount) { | | | |
2130 | VOP_ABORTOP(dvp, cnp); | | | |
2131 | vput(dvp); | | | |
2132 | return (EXDEV); | | | |
2133 | } | | | |
2134 | if (dvp != vp) { | | | |
2135 | error = vn_lock(vp, LK_EXCLUSIVE); | | | |
2136 | if (error != 0) { | | | |
2137 | VOP_ABORTOP(dvp, cnp); | | | |
2138 | vput(dvp); | | | |
2139 | return error; | | | |
2140 | } | | | |
2141 | } | | | |
2142 | | | | |
2143 | /* | | | |
2144 | * Push all writes to the server, so that the attribute cache | | | |
2145 | * doesn't get "out of sync" with the server. | | | |
2146 | * XXX There should be a better way! | | | |
2147 | */ | | | |
2148 | VOP_FSYNC(vp, cnp->cn_cred, FSYNC_WAIT, 0, 0); | | | |
2149 | | | | |
2150 | error = nfs_linkrpc(dvp, vp, cnp->cn_nameptr, cnp->cn_namelen, | | | |
2151 | cnp->cn_cred, curlwp); | | | |
2152 | | | | |
2153 | if (error == 0) | | | |
2154 | cache_purge1(dvp, cnp, 0); | | | |
2155 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
2156 | if (dvp != vp) | | | |
2157 | VOP_UNLOCK(vp, 0); | | | |
2158 | VN_KNOTE(vp, NOTE_LINK); | | | |
2159 | VN_KNOTE(dvp, NOTE_WRITE); | | | |
2160 | vput(dvp); | | | |
2161 | return (error); | | | |
2162 | } | | | |
2163 | | | | |
2164 | /* | | | |
2165 | * nfs symbolic link create call | | | |
2166 | */ | | | |
2167 | int | | | |
2168 | nfs_symlink(v) | | | |
2169 | void *v; | | | |
2170 | { | | | |
2171 | struct vop_symlink_args /* { | | | |
2172 | struct vnode *a_dvp; | | | |
2173 | struct vnode **a_vpp; | | | |
2174 | struct componentname *a_cnp; | | | |
2175 | struct vattr *a_vap; | | | |
2176 | char *a_target; | | | |
2177 | } */ *ap = v; | | | |
2178 | struct vnode *dvp = ap->a_dvp; | | | |
2179 | struct vattr *vap = ap->a_vap; | | | |
2180 | struct componentname *cnp = ap->a_cnp; | | | |
2181 | struct nfsv2_sattr *sp; | | | |
2182 | u_int32_t *tl; | | | |
2183 | char *cp; | | | |
2184 | int32_t t1, t2; | | | |
2185 | char *bpos, *dpos, *cp2; | | | |
2186 | int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp; | | | |
2187 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2188 | struct vnode *newvp = (struct vnode *)0; | | | |
2189 | const int v3 = NFS_ISV3(dvp); | | | |
2190 | int rexmit = 0; | | | |
2191 | struct nfsnode *dnp = VTONFS(dvp); | | | |
2192 | | | | |
2193 | *ap->a_vpp = NULL; | | | |
2194 | nfsstats.rpccnt[NFSPROC_SYMLINK]++; | | | |
2195 | slen = strlen(ap->a_target); | | | |
2196 | nfsm_reqhead(dnp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED + | | | |
2197 | nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3)); | | | |
2198 | nfsm_fhtom(dnp, v3); | | | |
2199 | nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); | | | |
2200 | #ifndef NFS_V2_ONlY | | | |
2201 | if (v3) | | | |
2202 | nfsm_v3attrbuild(vap, false); | | | |
2203 | #endif | | | |
2204 | nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN); | | | |
2205 | #ifndef NFS_V2_ONlY | | | |
2206 | if (!v3) { | | | |
2207 | nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); | | | |
2208 | sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode); | | | |
2209 | sp->sa_uid = nfs_xdrneg1; | | | |
2210 | sp->sa_gid = nfs_xdrneg1; | | | |
2211 | sp->sa_size = nfs_xdrneg1; | | | |
2212 | txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); | | | |
2213 | txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); | | | |
2214 | } | | | |
2215 | #endif | | | |
2216 | nfsm_request1(dnp, NFSPROC_SYMLINK, curlwp, cnp->cn_cred, | | | |
2217 | &rexmit); | | | |
2218 | #ifndef NFS_V2_ONlY | | | |
2219 | if (v3) { | | | |
2220 | if (!error) | | | |
2221 | nfsm_mtofh(dvp, newvp, v3, gotvp); | | | |
2222 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
2223 | } | | | |
2224 | #endif | | | |
2225 | nfsm_reqdone; | | | |
2226 | /* | | | |
2227 | * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. | | | |
2228 | */ | | | |
2229 | if (rexmit && error == EEXIST) | | | |
2230 | error = 0; | | | |
2231 | if (error == 0 || error == EEXIST) | | | |
2232 | cache_purge1(dvp, cnp, 0); | | | |
2233 | if (error == 0 && newvp == NULL) { | | | |
2234 | struct nfsnode *np = NULL; | | | |
2235 | | | | |
2236 | error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, | | | |
2237 | cnp->cn_cred, curlwp, &np); | | | |
2238 | if (error == 0) | | | |
2239 | newvp = NFSTOV(np); | | | |
2240 | } | | | |
2241 | if (error) { | | | |
2242 | if (newvp != NULL) | | | |
2243 | vput(newvp); | | | |
2244 | } else { | | | |
2245 | *ap->a_vpp = newvp; | | | |
2246 | } | | | |
2247 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
2248 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
2249 | if (!wccflag) | | | |
2250 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
2251 | VN_KNOTE(dvp, NOTE_WRITE); | | | |
2252 | vput(dvp); | | | |
2253 | return (error); | | | |
2254 | } | | | |
2255 | | | | |
2256 | /* | | | |
2257 | * nfs make dir call | | | |
2258 | */ | | | |
2259 | int | | | |
2260 | nfs_mkdir(v) | | | |
2261 | void *v; | | | |
2262 | { | | | |
2263 | struct vop_mkdir_args /* { | | | |
2264 | struct vnode *a_dvp; | | | |
2265 | struct vnode **a_vpp; | | | |
2266 | struct componentname *a_cnp; | | | |
2267 | struct vattr *a_vap; | | | |
2268 | } */ *ap = v; | | | |
2269 | struct vnode *dvp = ap->a_dvp; | | | |
2270 | struct vattr *vap = ap->a_vap; | | | |
2271 | struct componentname *cnp = ap->a_cnp; | | | |
2272 | struct nfsv2_sattr *sp; | | | |
2273 | u_int32_t *tl; | | | |
2274 | char *cp; | | | |
2275 | int32_t t1, t2; | | | |
2276 | int len; | | | |
2277 | struct nfsnode *dnp = VTONFS(dvp), *np = (struct nfsnode *)0; | | | |
2278 | struct vnode *newvp = (struct vnode *)0; | | | |
2279 | char *bpos, *dpos, *cp2; | | | |
2280 | int error = 0, wccflag = NFSV3_WCCRATTR; | | | |
2281 | int gotvp = 0; | | | |
2282 | int rexmit = 0; | | | |
2283 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2284 | const int v3 = NFS_ISV3(dvp); | | | |
2285 | | | | |
2286 | len = cnp->cn_namelen; | | | |
2287 | nfsstats.rpccnt[NFSPROC_MKDIR]++; | | | |
2288 | nfsm_reqhead(dnp, NFSPROC_MKDIR, | | | |
2289 | NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3)); | | | |
2290 | nfsm_fhtom(dnp, v3); | | | |
2291 | nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); | | | |
2292 | #ifndef NFS_V2_ONLY | | | |
2293 | if (v3) { | | | |
2294 | nfsm_v3attrbuild(vap, false); | | | |
2295 | } else | | | |
2296 | #endif | | | |
2297 | { | | | |
2298 | nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); | | | |
2299 | sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode); | | | |
2300 | sp->sa_uid = nfs_xdrneg1; | | | |
2301 | sp->sa_gid = nfs_xdrneg1; | | | |
2302 | sp->sa_size = nfs_xdrneg1; | | | |
2303 | txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); | | | |
2304 | txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); | | | |
2305 | } | | | |
2306 | nfsm_request1(dnp, NFSPROC_MKDIR, curlwp, cnp->cn_cred, &rexmit); | | | |
2307 | if (!error) | | | |
2308 | nfsm_mtofh(dvp, newvp, v3, gotvp); | | | |
2309 | if (v3) | | | |
2310 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
2311 | nfsm_reqdone; | | | |
2312 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
2313 | if (!wccflag) | | | |
2314 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
2315 | /* | | | |
2316 | * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry | | | |
2317 | * if we can succeed in looking up the directory. | | | |
2318 | */ | | | |
2319 | if ((rexmit && error == EEXIST) || (!error && !gotvp)) { | | | |
2320 | if (newvp) { | | | |
2321 | vput(newvp); | | | |
2322 | newvp = (struct vnode *)0; | | | |
2323 | } | | | |
2324 | error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred, | | | |
2325 | curlwp, &np); | | | |
2326 | if (!error) { | | | |
2327 | newvp = NFSTOV(np); | | | |
2328 | if (newvp->v_type != VDIR || newvp == dvp) | | | |
2329 | error = EEXIST; | | | |
2330 | } | | | |
2331 | } | | | |
2332 | if (error) { | | | |
2333 | if (newvp) { | | | |
2334 | if (dvp != newvp) | | | |
2335 | vput(newvp); | | | |
2336 | else | | | |
2337 | vrele(newvp); | | | |
2338 | } | | | |
2339 | } else { | | | |
2340 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); | | | |
2341 | if (cnp->cn_flags & MAKEENTRY) | | | |
2342 | nfs_cache_enter(dvp, newvp, cnp); | | | |
2343 | *ap->a_vpp = newvp; | | | |
2344 | } | | | |
2345 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
2346 | vput(dvp); | | | |
2347 | return (error); | | | |
2348 | } | | | |
2349 | | | | |
2350 | /* | | | |
2351 | * nfs remove directory call | | | |
2352 | */ | | | |
2353 | int | | | |
2354 | nfs_rmdir(v) | | | |
2355 | void *v; | | | |
2356 | { | | | |
2357 | struct vop_rmdir_args /* { | | | |
2358 | struct vnode *a_dvp; | | | |
2359 | struct vnode *a_vp; | | | |
2360 | struct componentname *a_cnp; | | | |
2361 | } */ *ap = v; | | | |
2362 | struct vnode *vp = ap->a_vp; | | | |
2363 | struct vnode *dvp = ap->a_dvp; | | | |
2364 | struct componentname *cnp = ap->a_cnp; | | | |
2365 | u_int32_t *tl; | | | |
2366 | char *cp; | | | |
2367 | #ifndef NFS_V2_ONLY | | | |
2368 | int32_t t1; | | | |
2369 | char *cp2; | | | |
2370 | #endif | | | |
2371 | int32_t t2; | | | |
2372 | char *bpos, *dpos; | | | |
2373 | int error = 0, wccflag = NFSV3_WCCRATTR; | | | |
2374 | int rexmit = 0; | | | |
2375 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2376 | const int v3 = NFS_ISV3(dvp); | | | |
2377 | struct nfsnode *dnp; | | | |
2378 | | | | |
2379 | if (dvp == vp) { | | | |
2380 | vrele(dvp); | | | |
2381 | vput(dvp); | | | |
2382 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
2383 | return (EINVAL); | | | |
2384 | } | | | |
2385 | nfsstats.rpccnt[NFSPROC_RMDIR]++; | | | |
2386 | dnp = VTONFS(dvp); | | | |
2387 | nfsm_reqhead(dnp, NFSPROC_RMDIR, | | | |
2388 | NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); | | | |
2389 | nfsm_fhtom(dnp, v3); | | | |
2390 | nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); | | | |
2391 | nfsm_request1(dnp, NFSPROC_RMDIR, curlwp, cnp->cn_cred, &rexmit); | | | |
2392 | #ifndef NFS_V2_ONLY | | | |
2393 | if (v3) | | | |
2394 | nfsm_wcc_data(dvp, wccflag, 0, !error); | | | |
2395 | #endif | | | |
2396 | nfsm_reqdone; | | | |
2397 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
2398 | VTONFS(dvp)->n_flag |= NMODIFIED; | | | |
2399 | if (!wccflag) | | | |
2400 | NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); | | | |
2401 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); | | | |
2402 | VN_KNOTE(vp, NOTE_DELETE); | | | |
2403 | cache_purge(vp); | | | |
2404 | vput(vp); | | | |
2405 | vput(dvp); | | | |
2406 | /* | | | |
2407 | * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. | | | |
2408 | */ | | | |
2409 | if (rexmit && error == ENOENT) | | | |
2410 | error = 0; | | | |
2411 | return (error); | | | |
2412 | } | | | |
2413 | | | | |
2414 | /* | | | |
2415 | * nfs readdir call | | | |
2416 | */ | | | |
2417 | int | | | |
2418 | nfs_readdir(v) | | | |
2419 | void *v; | | | |
2420 | { | | | |
2421 | struct vop_readdir_args /* { | | | |
2422 | struct vnode *a_vp; | | | |
2423 | struct uio *a_uio; | | | |
2424 | kauth_cred_t a_cred; | | | |
2425 | int *a_eofflag; | | | |
2426 | off_t **a_cookies; | | | |
2427 | int *a_ncookies; | | | |
2428 | } */ *ap = v; | | | |
2429 | struct vnode *vp = ap->a_vp; | | | |
2430 | struct uio *uio = ap->a_uio; | | | |
2431 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); | | | |
2432 | char *base = uio->uio_iov->iov_base; | | | |
2433 | int tresid, error; | | | |
2434 | size_t count, lost; | | | |
2435 | struct dirent *dp; | | | |
2436 | off_t *cookies = NULL; | | | |
2437 | int ncookies = 0, nc; | | | |
2438 | | | | |
2439 | if (vp->v_type != VDIR) | | | |
2440 | return (EPERM); | | | |
2441 | | | | |
2442 | lost = uio->uio_resid & (NFS_DIRFRAGSIZ - 1); | | | |
2443 | count = uio->uio_resid - lost; | | | |
2444 | if (count <= 0) | | | |
2445 | return (EINVAL); | | | |
2446 | | | | |
2447 | /* | | | |
2448 | * Call nfs_bioread() to do the real work. | | | |
2449 | */ | | | |
2450 | tresid = uio->uio_resid = count; | | | |
2451 | error = nfs_bioread(vp, uio, 0, ap->a_cred, | | | |
2452 | ap->a_cookies ? NFSBIO_CACHECOOKIES : 0); | | | |
2453 | | | | |
2454 | if (!error && ap->a_cookies) { | | | |
2455 | ncookies = count / 16; | | | |
2456 | cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK); | | | |
2457 | *ap->a_cookies = cookies; | | | |
2458 | } | | | |
2459 | | | | |
2460 | if (!error && uio->uio_resid == tresid) { | | | |
2461 | uio->uio_resid += lost; | | | |
2462 | nfsstats.direofcache_misses++; | | | |
2463 | if (ap->a_cookies) | | | |
2464 | *ap->a_ncookies = 0; | | | |
2465 | *ap->a_eofflag = 1; | | | |
2466 | return (0); | | | |
2467 | } | | | |
2468 | | | | |
2469 | if (!error && ap->a_cookies) { | | | |
2470 | /* | | | |
2471 | * Only the NFS server and emulations use cookies, and they | | | |
2472 | * load the directory block into system space, so we can | | | |
2473 | * just look at it directly. | | | |
2474 | */ | | | |
2475 | if (!VMSPACE_IS_KERNEL_P(uio->uio_vmspace) || | | | |
2476 | uio->uio_iovcnt != 1) | | | |
2477 | panic("nfs_readdir: lost in space"); | | | |
2478 | for (nc = 0; ncookies-- && | | | |
2479 | base < (char *)uio->uio_iov->iov_base; nc++){ | | | |
2480 | dp = (struct dirent *) base; | | | |
2481 | if (dp->d_reclen == 0) | | | |
2482 | break; | | | |
2483 | if (nmp->nm_flag & NFSMNT_XLATECOOKIE) | | | |
2484 | *(cookies++) = (off_t)NFS_GETCOOKIE32(dp); | | | |
2485 | else | | | |
2486 | *(cookies++) = NFS_GETCOOKIE(dp); | | | |
2487 | base += dp->d_reclen; | | | |
2488 | } | | | |
2489 | uio->uio_resid += | | | |
2490 | ((char *)uio->uio_iov->iov_base - base); | | | |
2491 | uio->uio_iov->iov_len += | | | |
2492 | ((char *)uio->uio_iov->iov_base - base); | | | |
2493 | uio->uio_iov->iov_base = base; | | | |
2494 | *ap->a_ncookies = nc; | | | |
2495 | } | | | |
2496 | | | | |
2497 | uio->uio_resid += lost; | | | |
2498 | *ap->a_eofflag = 0; | | | |
2499 | return (error); | | | |
2500 | } | | | |
2501 | | | | |
2502 | /* | | | |
2503 | * Readdir rpc call. | | | |
2504 | * Called from below the buffer cache by nfs_doio(). | | | |
2505 | */ | | | |
2506 | int | | | |
2507 | nfs_readdirrpc(vp, uiop, cred) | | | |
2508 | struct vnode *vp; | | | |
2509 | struct uio *uiop; | | | |
2510 | kauth_cred_t cred; | | | |
2511 | { | | | |
2512 | int len, left; | | | |
2513 | struct dirent *dp = NULL; | | | |
2514 | u_int32_t *tl; | | | |
2515 | char *cp; | | | |
2516 | int32_t t1, t2; | | | |
2517 | char *bpos, *dpos, *cp2; | | | |
2518 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2519 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); | | | |
2520 | struct nfsnode *dnp = VTONFS(vp); | | | |
2521 | u_quad_t fileno; | | | |
2522 | int error = 0, more_dirs = 1, blksiz = 0, bigenough = 1; | | | |
2523 | #ifndef NFS_V2_ONLY | | | |
2524 | int attrflag; | | | |
2525 | #endif | | | |
2526 | int nrpcs = 0, reclen; | | | |
2527 | const int v3 = NFS_ISV3(vp); | | | |
2528 | | | | |
2529 | #ifdef DIAGNOSTIC | | | |
2530 | /* | | | |
2531 | * Should be called from buffer cache, so only amount of | | | |
2532 | * NFS_DIRBLKSIZ will be requested. | | | |
2533 | */ | | | |
2534 | if (uiop->uio_iovcnt != 1 || uiop->uio_resid != NFS_DIRBLKSIZ) | | | |
2535 | panic("nfs readdirrpc bad uio"); | | | |
2536 | #endif | | | |
2537 | | | | |
2538 | /* | | | |
2539 | * Loop around doing readdir rpc's of size nm_readdirsize | | | |
2540 | * truncated to a multiple of NFS_DIRFRAGSIZ. | | | |
2541 | * The stopping criteria is EOF or buffer full. | | | |
2542 | */ | | | |
2543 | while (more_dirs && bigenough) { | | | |
2544 | /* | | | |
2545 | * Heuristic: don't bother to do another RPC to further | | | |
2546 | * fill up this block if there is not much room left. (< 50% | | | |
2547 | * of the readdir RPC size). This wastes some buffer space | | | |
2548 | * but can save up to 50% in RPC calls. | | | |
2549 | */ | | | |
2550 | if (nrpcs > 0 && uiop->uio_resid < (nmp->nm_readdirsize / 2)) { | | | |
2551 | bigenough = 0; | | | |
2552 | break; | | | |
2553 | } | | | |
2554 | nfsstats.rpccnt[NFSPROC_READDIR]++; | | | |
2555 | nfsm_reqhead(dnp, NFSPROC_READDIR, NFSX_FH(v3) + | | | |
2556 | NFSX_READDIR(v3)); | | | |
2557 | nfsm_fhtom(dnp, v3); | | | |
2558 | #ifndef NFS_V2_ONLY | | | |
2559 | if (v3) { | | | |
2560 | nfsm_build(tl, u_int32_t *, 5 * NFSX_UNSIGNED); | | | |
2561 | if (nmp->nm_iflag & NFSMNT_SWAPCOOKIE) { | | | |
2562 | txdr_swapcookie3(uiop->uio_offset, tl); | | | |
2563 | } else { | | | |
2564 | txdr_cookie3(uiop->uio_offset, tl); | | | |
2565 | } | | | |
2566 | tl += 2; | | | |
2567 | *tl++ = dnp->n_cookieverf.nfsuquad[0]; | | | |
2568 | *tl++ = dnp->n_cookieverf.nfsuquad[1]; | | | |
2569 | } else | | | |
2570 | #endif | | | |
2571 | { | | | |
2572 | nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED); | | | |
2573 | *tl++ = txdr_unsigned(uiop->uio_offset); | | | |
2574 | } | | | |
2575 | *tl = txdr_unsigned(nmp->nm_readdirsize); | | | |
2576 | nfsm_request(dnp, NFSPROC_READDIR, curlwp, cred); | | | |
2577 | nrpcs++; | | | |
2578 | #ifndef NFS_V2_ONLY | | | |
2579 | if (v3) { | | | |
2580 | nfsm_postop_attr(vp, attrflag, 0); | | | |
2581 | if (!error) { | | | |
2582 | nfsm_dissect(tl, u_int32_t *, | | | |
2583 | 2 * NFSX_UNSIGNED); | | | |
2584 | dnp->n_cookieverf.nfsuquad[0] = *tl++; | | | |
2585 | dnp->n_cookieverf.nfsuquad[1] = *tl; | | | |
2586 | } else { | | | |
2587 | m_freem(mrep); | | | |
2588 | goto nfsmout; | | | |
2589 | } | | | |
2590 | } | | | |
2591 | #endif | | | |
2592 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
2593 | more_dirs = fxdr_unsigned(int, *tl); | | | |
2594 | | | | |
2595 | /* loop thru the dir entries, doctoring them to 4bsd form */ | | | |
2596 | while (more_dirs && bigenough) { | | | |
2597 | #ifndef NFS_V2_ONLY | | | |
2598 | if (v3) { | | | |
2599 | nfsm_dissect(tl, u_int32_t *, | | | |
2600 | 3 * NFSX_UNSIGNED); | | | |
2601 | fileno = fxdr_hyper(tl); | | | |
2602 | len = fxdr_unsigned(int, *(tl + 2)); | | | |
2603 | } else | | | |
2604 | #endif | | | |
2605 | { | | | |
2606 | nfsm_dissect(tl, u_int32_t *, | | | |
2607 | 2 * NFSX_UNSIGNED); | | | |
2608 | fileno = fxdr_unsigned(u_quad_t, *tl++); | | | |
2609 | len = fxdr_unsigned(int, *tl); | | | |
2610 | } | | | |
2611 | if (len <= 0 || len > NFS_MAXNAMLEN) { | | | |
2612 | error = EBADRPC; | | | |
2613 | m_freem(mrep); | | | |
2614 | goto nfsmout; | | | |
2615 | } | | | |
2616 | /* for cookie stashing */ | | | |
2617 | reclen = _DIRENT_RECLEN(dp, len) + 2 * sizeof(off_t); | | | |
2618 | left = NFS_DIRFRAGSIZ - blksiz; | | | |
2619 | if (reclen > left) { | | | |
2620 | memset(uiop->uio_iov->iov_base, 0, left); | | | |
2621 | dp->d_reclen += left; | | | |
2622 | UIO_ADVANCE(uiop, left); | | | |
2623 | blksiz = 0; | | | |
2624 | NFS_STASHCOOKIE(dp, uiop->uio_offset); | | | |
2625 | } | | | |
2626 | if (reclen > uiop->uio_resid) | | | |
2627 | bigenough = 0; | | | |
2628 | if (bigenough) { | | | |
2629 | int tlen; | | | |
2630 | | | | |
2631 | dp = (struct dirent *)uiop->uio_iov->iov_base; | | | |
2632 | dp->d_fileno = fileno; | | | |
2633 | dp->d_namlen = len; | | | |
2634 | dp->d_reclen = reclen; | | | |
2635 | dp->d_type = DT_UNKNOWN; | | | |
2636 | blksiz += reclen; | | | |
2637 | if (blksiz == NFS_DIRFRAGSIZ) | | | |
2638 | blksiz = 0; | | | |
2639 | UIO_ADVANCE(uiop, DIRHDSIZ); | | | |
2640 | nfsm_mtouio(uiop, len); | | | |
2641 | tlen = reclen - (DIRHDSIZ + len); | | | |
2642 | (void)memset(uiop->uio_iov->iov_base, 0, tlen); | | | |
2643 | UIO_ADVANCE(uiop, tlen); | | | |
2644 | } else | | | |
2645 | nfsm_adv(nfsm_rndup(len)); | | | |
2646 | #ifndef NFS_V2_ONLY | | | |
2647 | if (v3) { | | | |
2648 | nfsm_dissect(tl, u_int32_t *, | | | |
2649 | 3 * NFSX_UNSIGNED); | | | |
2650 | } else | | | |
2651 | #endif | | | |
2652 | { | | | |
2653 | nfsm_dissect(tl, u_int32_t *, | | | |
2654 | 2 * NFSX_UNSIGNED); | | | |
2655 | } | | | |
2656 | if (bigenough) { | | | |
2657 | #ifndef NFS_V2_ONLY | | | |
2658 | if (v3) { | | | |
2659 | if (nmp->nm_iflag & NFSMNT_SWAPCOOKIE) | | | |
2660 | uiop->uio_offset = | | | |
2661 | fxdr_swapcookie3(tl); | | | |
2662 | else | | | |
2663 | uiop->uio_offset = | | | |
2664 | fxdr_cookie3(tl); | | | |
2665 | } | | | |
2666 | else | | | |
2667 | #endif | | | |
2668 | { | | | |
2669 | uiop->uio_offset = | | | |
2670 | fxdr_unsigned(off_t, *tl); | | | |
2671 | } | | | |
2672 | NFS_STASHCOOKIE(dp, uiop->uio_offset); | | | |
2673 | } | | | |
2674 | if (v3) | | | |
2675 | tl += 2; | | | |
2676 | else | | | |
2677 | tl++; | | | |
2678 | more_dirs = fxdr_unsigned(int, *tl); | | | |
2679 | } | | | |
2680 | /* | | | |
2681 | * If at end of rpc data, get the eof boolean | | | |
2682 | */ | | | |
2683 | if (!more_dirs) { | | | |
2684 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
2685 | more_dirs = (fxdr_unsigned(int, *tl) == 0); | | | |
2686 | | | | |
2687 | /* | | | |
2688 | * kludge: if we got no entries, treat it as EOF. | | | |
2689 | * some server sometimes send a reply without any | | | |
2690 | * entries or EOF. | | | |
2691 | * although it might mean the server has very long name, | | | |
2692 | * we can't handle such entries anyway. | | | |
2693 | */ | | | |
2694 | | | | |
2695 | if (uiop->uio_resid >= NFS_DIRBLKSIZ) | | | |
2696 | more_dirs = 0; | | | |
2697 | } | | | |
2698 | m_freem(mrep); | | | |
2699 | } | | | |
2700 | /* | | | |
2701 | * Fill last record, iff any, out to a multiple of NFS_DIRFRAGSIZ | | | |
2702 | * by increasing d_reclen for the last record. | | | |
2703 | */ | | | |
2704 | if (blksiz > 0) { | | | |
2705 | left = NFS_DIRFRAGSIZ - blksiz; | | | |
2706 | memset(uiop->uio_iov->iov_base, 0, left); | | | |
2707 | dp->d_reclen += left; | | | |
2708 | NFS_STASHCOOKIE(dp, uiop->uio_offset); | | | |
2709 | UIO_ADVANCE(uiop, left); | | | |
2710 | } | | | |
2711 | | | | |
2712 | /* | | | |
2713 | * We are now either at the end of the directory or have filled the | | | |
2714 | * block. | | | |
2715 | */ | | | |
2716 | if (bigenough) { | | | |
2717 | dnp->n_direofoffset = uiop->uio_offset; | | | |
2718 | dnp->n_flag |= NEOFVALID; | | | |
2719 | } | | | |
2720 | nfsmout: | | | |
2721 | return (error); | | | |
2722 | } | | | |
2723 | | | | |
2724 | #ifndef NFS_V2_ONLY | | | |
2725 | /* | | | |
2726 | * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc(). | | | |
2727 | */ | | | |
2728 | int | | | |
2729 | nfs_readdirplusrpc(vp, uiop, cred) | | | |
2730 | struct vnode *vp; | | | |
2731 | struct uio *uiop; | | | |
2732 | kauth_cred_t cred; | | | |
2733 | { | | | |
2734 | int len, left; | | | |
2735 | struct dirent *dp = NULL; | | | |
2736 | u_int32_t *tl; | | | |
2737 | char *cp; | | | |
2738 | int32_t t1, t2; | | | |
2739 | struct vnode *newvp; | | | |
2740 | char *bpos, *dpos, *cp2; | | | |
2741 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
2742 | struct nameidata nami, *ndp = &nami; | | | |
2743 | struct componentname *cnp = &ndp->ni_cnd; | | | |
2744 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); | | | |
2745 | struct nfsnode *dnp = VTONFS(vp), *np; | | | |
2746 | nfsfh_t *fhp; | | | |
2747 | u_quad_t fileno; | | | |
2748 | int error = 0, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; | | | |
2749 | int attrflag, fhsize, nrpcs = 0, reclen; | | | |
2750 | struct nfs_fattr fattr, *fp; | | | |
2751 | | | | |
2752 | #ifdef DIAGNOSTIC | | | |
2753 | if (uiop->uio_iovcnt != 1 || uiop->uio_resid != NFS_DIRBLKSIZ) | | | |
2754 | panic("nfs readdirplusrpc bad uio"); | | | |
2755 | #endif | | | |
2756 | ndp->ni_dvp = vp; | | | |
2757 | newvp = NULLVP; | | | |
2758 | | | | |
2759 | /* | | | |
2760 | * Loop around doing readdir rpc's of size nm_readdirsize | | | |
2761 | * truncated to a multiple of NFS_DIRFRAGSIZ. | | | |
2762 | * The stopping criteria is EOF or buffer full. | | | |
2763 | */ | | | |
2764 | while (more_dirs && bigenough) { | | | |
2765 | if (nrpcs > 0 && uiop->uio_resid < (nmp->nm_readdirsize / 2)) { | | | |
2766 | bigenough = 0; | | | |
2767 | break; | | | |
2768 | } | | | |
2769 | nfsstats.rpccnt[NFSPROC_READDIRPLUS]++; | | | |
2770 | nfsm_reqhead(dnp, NFSPROC_READDIRPLUS, | | | |
2771 | NFSX_FH(1) + 6 * NFSX_UNSIGNED); | | | |
2772 | nfsm_fhtom(dnp, 1); | | | |
2773 | nfsm_build(tl, u_int32_t *, 6 * NFSX_UNSIGNED); | | | |
2774 | if (nmp->nm_iflag & NFSMNT_SWAPCOOKIE) { | | | |
2775 | txdr_swapcookie3(uiop->uio_offset, tl); | | | |
2776 | } else { | | | |
2777 | txdr_cookie3(uiop->uio_offset, tl); | | | |
2778 | } | | | |
2779 | tl += 2; | | | |
2780 | *tl++ = dnp->n_cookieverf.nfsuquad[0]; | | | |
2781 | *tl++ = dnp->n_cookieverf.nfsuquad[1]; | | | |
2782 | *tl++ = txdr_unsigned(nmp->nm_readdirsize); | | | |
2783 | *tl = txdr_unsigned(nmp->nm_rsize); | | | |
2784 | nfsm_request(dnp, NFSPROC_READDIRPLUS, curlwp, cred); | | | |
2785 | nfsm_postop_attr(vp, attrflag, 0); | | | |
2786 | if (error) { | | | |
2787 | m_freem(mrep); | | | |
2788 | goto nfsmout; | | | |
2789 | } | | | |
2790 | nrpcs++; | | | |
2791 | nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED); | | | |
2792 | dnp->n_cookieverf.nfsuquad[0] = *tl++; | | | |
2793 | dnp->n_cookieverf.nfsuquad[1] = *tl++; | | | |
2794 | more_dirs = fxdr_unsigned(int, *tl); | | | |
2795 | | | | |
2796 | /* loop thru the dir entries, doctoring them to 4bsd form */ | | | |
2797 | while (more_dirs && bigenough) { | | | |
2798 | nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED); | | | |
2799 | fileno = fxdr_hyper(tl); | | | |
2800 | len = fxdr_unsigned(int, *(tl + 2)); | | | |
2801 | if (len <= 0 || len > NFS_MAXNAMLEN) { | | | |
2802 | error = EBADRPC; | | | |
2803 | m_freem(mrep); | | | |
2804 | goto nfsmout; | | | |
2805 | } | | | |
2806 | /* for cookie stashing */ | | | |
2807 | reclen = _DIRENT_RECLEN(dp, len) + 2 * sizeof(off_t); | | | |
2808 | left = NFS_DIRFRAGSIZ - blksiz; | | | |
2809 | if (reclen > left) { | | | |
2810 | /* | | | |
2811 | * DIRFRAGSIZ is aligned, no need to align | | | |
2812 | * again here. | | | |
2813 | */ | | | |
2814 | memset(uiop->uio_iov->iov_base, 0, left); | | | |
2815 | dp->d_reclen += left; | | | |
2816 | UIO_ADVANCE(uiop, left); | | | |
2817 | NFS_STASHCOOKIE(dp, uiop->uio_offset); | | | |
2818 | blksiz = 0; | | | |
2819 | } | | | |
2820 | if (reclen > uiop->uio_resid) | | | |
2821 | bigenough = 0; | | | |
2822 | if (bigenough) { | | | |
2823 | int tlen; | | | |
2824 | | | | |
2825 | dp = (struct dirent *)uiop->uio_iov->iov_base; | | | |
2826 | dp->d_fileno = fileno; | | | |
2827 | dp->d_namlen = len; | | | |
2828 | dp->d_reclen = reclen; | | | |
2829 | dp->d_type = DT_UNKNOWN; | | | |
2830 | blksiz += reclen; | | | |
2831 | if (blksiz == NFS_DIRFRAGSIZ) | | | |
2832 | blksiz = 0; | | | |
2833 | UIO_ADVANCE(uiop, DIRHDSIZ); | | | |
2834 | nfsm_mtouio(uiop, len); | | | |
2835 | tlen = reclen - (DIRHDSIZ + len); | | | |
2836 | (void)memset(uiop->uio_iov->iov_base, 0, tlen); | | | |
2837 | UIO_ADVANCE(uiop, tlen); | | | |
2838 | cnp->cn_nameptr = dp->d_name; | | | |
2839 | cnp->cn_namelen = dp->d_namlen; | | | |
2840 | } else | | | |
2841 | nfsm_adv(nfsm_rndup(len)); | | | |
2842 | nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED); | | | |
2843 | if (bigenough) { | | | |
2844 | if (nmp->nm_iflag & NFSMNT_SWAPCOOKIE) | | | |
2845 | uiop->uio_offset = | | | |
2846 | fxdr_swapcookie3(tl); | | | |
2847 | else | | | |
2848 | uiop->uio_offset = | | | |
2849 | fxdr_cookie3(tl); | | | |
2850 | NFS_STASHCOOKIE(dp, uiop->uio_offset); | | | |
2851 | } | | | |
2852 | tl += 2; | | | |
2853 | | | | |
2854 | /* | | | |
2855 | * Since the attributes are before the file handle | | | |
2856 | * (sigh), we must skip over the attributes and then | | | |
2857 | * come back and get them. | | | |
2858 | */ | | | |
2859 | attrflag = fxdr_unsigned(int, *tl); | | | |
2860 | if (attrflag) { | | | |
2861 | nfsm_dissect(fp, struct nfs_fattr *, NFSX_V3FATTR); | | | |
2862 | memcpy(&fattr, fp, NFSX_V3FATTR); | | | |
2863 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
2864 | doit = fxdr_unsigned(int, *tl); | | | |
2865 | if (doit) { | | | |
2866 | nfsm_getfh(fhp, fhsize, 1); | | | |
2867 | if (NFS_CMPFH(dnp, fhp, fhsize)) { | | | |
2868 | VREF(vp); | | | |
2869 | newvp = vp; | | | |
2870 | np = dnp; | | | |
2871 | } else { | | | |
2872 | error = nfs_nget1(vp->v_mount, fhp, | | | |
2873 | fhsize, &np, LK_NOWAIT); | | | |
2874 | if (!error) | | | |
2875 | newvp = NFSTOV(np); | | | |
2876 | } | | | |
2877 | if (!error) { | | | |
2878 | const char *xcp; | | | |
2879 | | | | |
2880 | nfs_loadattrcache(&newvp, &fattr, 0, 0); | | | |
2881 | if (bigenough) { | | | |
2882 | dp->d_type = | | | |
2883 | IFTODT(VTTOIF(np->n_vattr->va_type)); | | | |
2884 | if (cnp->cn_namelen <= NCHNAMLEN) { | | | |
2885 | ndp->ni_vp = newvp; | | | |
2886 | xcp = cnp->cn_nameptr + | | | |
2887 | cnp->cn_namelen; | | | |
2888 | cnp->cn_hash = | | | |
2889 | namei_hash(cnp->cn_nameptr, &xcp); | | | |
2890 | nfs_cache_enter(ndp->ni_dvp, | | | |
2891 | ndp->ni_vp, cnp); | | | |
2892 | } | | | |
2893 | } | | | |
2894 | } | | | |
2895 | error = 0; | | | |
2896 | } | | | |
2897 | } else { | | | |
2898 | /* Just skip over the file handle */ | | | |
2899 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
2900 | i = fxdr_unsigned(int, *tl); | | | |
2901 | nfsm_adv(nfsm_rndup(i)); | | | |
2902 | } | | | |
2903 | if (newvp != NULLVP) { | | | |
2904 | if (newvp == vp) | | | |
2905 | vrele(newvp); | | | |
2906 | else | | | |
2907 | vput(newvp); | | | |
2908 | newvp = NULLVP; | | | |
2909 | } | | | |
2910 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
2911 | more_dirs = fxdr_unsigned(int, *tl); | | | |
2912 | } | | | |
2913 | /* | | | |
2914 | * If at end of rpc data, get the eof boolean | | | |
2915 | */ | | | |
2916 | if (!more_dirs) { | | | |
2917 | nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); | | | |
2918 | more_dirs = (fxdr_unsigned(int, *tl) == 0); | | | |
2919 | | | | |
2920 | /* | | | |
2921 | * kludge: see a comment in nfs_readdirrpc. | | | |
2922 | */ | | | |
2923 | | | | |
2924 | if (uiop->uio_resid >= NFS_DIRBLKSIZ) | | | |
2925 | more_dirs = 0; | | | |
2926 | } | | | |
2927 | m_freem(mrep); | | | |
2928 | } | | | |
2929 | /* | | | |
2930 | * Fill last record, iff any, out to a multiple of NFS_DIRFRAGSIZ | | | |
2931 | * by increasing d_reclen for the last record. | | | |
2932 | */ | | | |
2933 | if (blksiz > 0) { | | | |
2934 | left = NFS_DIRFRAGSIZ - blksiz; | | | |
2935 | memset(uiop->uio_iov->iov_base, 0, left); | | | |
2936 | dp->d_reclen += left; | | | |
2937 | NFS_STASHCOOKIE(dp, uiop->uio_offset); | | | |
2938 | UIO_ADVANCE(uiop, left); | | | |
2939 | } | | | |
2940 | | | | |
2941 | /* | | | |
2942 | * We are now either at the end of the directory or have filled the | | | |
2943 | * block. | | | |
2944 | */ | | | |
2945 | if (bigenough) { | | | |
2946 | dnp->n_direofoffset = uiop->uio_offset; | | | |
2947 | dnp->n_flag |= NEOFVALID; | | | |
2948 | } | | | |
2949 | nfsmout: | | | |
2950 | if (newvp != NULLVP) { | | | |
2951 | if(newvp == vp) | | | |
2952 | vrele(newvp); | | | |
2953 | else | | | |
2954 | vput(newvp); | | | |
2955 | } | | | |
2956 | return (error); | | | |
2957 | } | | | |
2958 | #endif | | | |
2959 | | | | |
2960 | /* | | | |
2961 | * Silly rename. To make the NFS filesystem that is stateless look a little | | | |
2962 | * more like the "ufs" a remove of an active vnode is translated to a rename | | | |
2963 | * to a funny looking filename that is removed by nfs_inactive on the | | | |
2964 | * nfsnode. There is the potential for another process on a different client | | | |
2965 | * to create the same funny name between the nfs_lookitup() fails and the | | | |
2966 | * nfs_rename() completes, but... | | | |
2967 | */ | | | |
2968 | int | | | |
2969 | nfs_sillyrename(dvp, vp, cnp, dolink) | | | |
2970 | struct vnode *dvp, *vp; | | | |
2971 | struct componentname *cnp; | | | |
2972 | bool dolink; | | | |
2973 | { | | | |
2974 | struct sillyrename *sp; | | | |
2975 | struct nfsnode *np; | | | |
2976 | int error; | | | |
2977 | short pid; | | | |
2978 | | | | |
2979 | cache_purge(dvp); | | | |
2980 | np = VTONFS(vp); | | | |
2981 | #ifndef DIAGNOSTIC | | | |
2982 | if (vp->v_type == VDIR) | | | |
2983 | panic("nfs: sillyrename dir"); | | | |
2984 | #endif | | | |
2985 | sp = kmem_alloc(sizeof(*sp), KM_SLEEP); | | | |
2986 | sp->s_cred = kauth_cred_dup(cnp->cn_cred); | | | |
2987 | sp->s_dvp = dvp; | | | |
2988 | VREF(dvp); | | | |
2989 | | | | |
2990 | /* Fudge together a funny name */ | | | |
2991 | pid = curlwp->l_proc->p_pid; | | | |
2992 | memcpy(sp->s_name, ".nfsAxxxx4.4", 13); | | | |
2993 | sp->s_namlen = 12; | | | |
2994 | sp->s_name[8] = hexdigits[pid & 0xf]; | | | |
2995 | sp->s_name[7] = hexdigits[(pid >> 4) & 0xf]; | | | |
2996 | sp->s_name[6] = hexdigits[(pid >> 8) & 0xf]; | | | |
2997 | sp->s_name[5] = hexdigits[(pid >> 12) & 0xf]; | | | |
2998 | | | | |
2999 | /* Try lookitups until we get one that isn't there */ | | | |
3000 | while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, | | | |
3001 | curlwp, (struct nfsnode **)0) == 0) { | | | |
3002 | sp->s_name[4]++; | | | |
3003 | if (sp->s_name[4] > 'z') { | | | |
3004 | error = EINVAL; | | | |
3005 | goto bad; | | | |
3006 | } | | | |
3007 | } | | | |
3008 | if (dolink) { | | | |
3009 | error = nfs_linkrpc(dvp, vp, sp->s_name, sp->s_namlen, | | | |
3010 | sp->s_cred, curlwp); | | | |
3011 | /* | | | |
3012 | * nfs_request maps NFSERR_NOTSUPP to ENOTSUP. | | | |
3013 | */ | | | |
3014 | if (error == ENOTSUP) { | | | |
3015 | error = nfs_renameit(dvp, cnp, sp); | | | |
3016 | } | | | |
3017 | } else { | | | |
3018 | error = nfs_renameit(dvp, cnp, sp); | | | |
3019 | } | | | |
3020 | if (error) | | | |
3021 | goto bad; | | | |
3022 | error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, | | | |
3023 | curlwp, &np); | | | |
3024 | np->n_sillyrename = sp; | | | |
3025 | return (0); | | | |
3026 | bad: | | | |
3027 | vrele(sp->s_dvp); | | | |
3028 | kauth_cred_free(sp->s_cred); | | | |
3029 | kmem_free(sp, sizeof(*sp)); | | | |
3030 | return (error); | | | |
3031 | } | | | |
3032 | | | | |
3033 | /* | | | |
3034 | * Look up a file name and optionally either update the file handle or | | | |
3035 | * allocate an nfsnode, depending on the value of npp. | | | |
3036 | * npp == NULL --> just do the lookup | | | |
3037 | * *npp == NULL --> allocate a new nfsnode and make sure attributes are | | | |
3038 | * handled too | | | |
3039 | * *npp != NULL --> update the file handle in the vnode | | | |
3040 | */ | | | |
3041 | int | | | |
3042 | nfs_lookitup(dvp, name, len, cred, l, npp) | | | |
3043 | struct vnode *dvp; | | | |
3044 | const char *name; | | | |
3045 | int len; | | | |
3046 | kauth_cred_t cred; | | | |
3047 | struct lwp *l; | | | |
3048 | struct nfsnode **npp; | | | |
3049 | { | | | |
3050 | u_int32_t *tl; | | | |
3051 | char *cp; | | | |
3052 | int32_t t1, t2; | | | |
3053 | struct vnode *newvp = (struct vnode *)0; | | | |
3054 | struct nfsnode *np, *dnp = VTONFS(dvp); | | | |
3055 | char *bpos, *dpos, *cp2; | | | |
3056 | int error = 0, fhlen; | | | |
3057 | #ifndef NFS_V2_ONLY | | | |
3058 | int attrflag; | | | |
3059 | #endif | | | |
3060 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
3061 | nfsfh_t *nfhp; | | | |
3062 | const int v3 = NFS_ISV3(dvp); | | | |
3063 | | | | |
3064 | nfsstats.rpccnt[NFSPROC_LOOKUP]++; | | | |
3065 | nfsm_reqhead(dnp, NFSPROC_LOOKUP, | | | |
3066 | NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); | | | |
3067 | nfsm_fhtom(dnp, v3); | | | |
3068 | nfsm_strtom(name, len, NFS_MAXNAMLEN); | | | |
3069 | nfsm_request(dnp, NFSPROC_LOOKUP, l, cred); | | | |
3070 | if (npp && !error) { | | | |
3071 | nfsm_getfh(nfhp, fhlen, v3); | | | |
3072 | if (*npp) { | | | |
3073 | np = *npp; | | | |
3074 | if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) { | | | |
3075 | kmem_free(np->n_fhp, np->n_fhsize); | | | |
3076 | np->n_fhp = &np->n_fh; | | | |
3077 | } | | | |
3078 | #if NFS_SMALLFH < NFSX_V3FHMAX | | | |
3079 | else if (np->n_fhsize <= NFS_SMALLFH && fhlen > NFS_SMALLFH) | | | |
3080 | np->n_fhp = kmem_alloc(fhlen, KM_SLEEP); | | | |
3081 | #endif | | | |
3082 | memcpy(np->n_fhp, nfhp, fhlen); | | | |
3083 | np->n_fhsize = fhlen; | | | |
3084 | newvp = NFSTOV(np); | | | |
3085 | } else if (NFS_CMPFH(dnp, nfhp, fhlen)) { | | | |
3086 | VREF(dvp); | | | |
3087 | newvp = dvp; | | | |
3088 | np = dnp; | | | |
3089 | } else { | | | |
3090 | error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np); | | | |
3091 | if (error) { | | | |
3092 | m_freem(mrep); | | | |
3093 | return (error); | | | |
3094 | } | | | |
3095 | newvp = NFSTOV(np); | | | |
3096 | } | | | |
3097 | #ifndef NFS_V2_ONLY | | | |
3098 | if (v3) { | | | |
3099 | nfsm_postop_attr(newvp, attrflag, 0); | | | |
3100 | if (!attrflag && *npp == NULL) { | | | |
3101 | m_freem(mrep); | | | |
3102 | vput(newvp); | | | |
3103 | return (ENOENT); | | | |
3104 | } | | | |
3105 | } else | | | |
3106 | #endif | | | |
3107 | nfsm_loadattr(newvp, (struct vattr *)0, 0); | | | |
3108 | } | | | |
3109 | nfsm_reqdone; | | | |
3110 | if (npp && *npp == NULL) { | | | |
3111 | if (error) { | | | |
3112 | if (newvp) | | | |
3113 | vput(newvp); | | | |
3114 | } else | | | |
3115 | *npp = np; | | | |
3116 | } | | | |
3117 | return (error); | | | |
3118 | } | | | |
3119 | | | | |
3120 | #ifndef NFS_V2_ONLY | | | |
3121 | /* | | | |
3122 | * Nfs Version 3 commit rpc | | | |
3123 | */ | | | |
3124 | int | | | |
3125 | nfs_commit(vp, offset, cnt, l) | | | |
3126 | struct vnode *vp; | | | |
3127 | off_t offset; | | | |
3128 | uint32_t cnt; | | | |
3129 | struct lwp *l; | | | |
3130 | { | | | |
3131 | char *cp; | | | |
3132 | u_int32_t *tl; | | | |
3133 | int32_t t1, t2; | | | |
3134 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); | | | |
3135 | char *bpos, *dpos, *cp2; | | | |
3136 | int error = 0, wccflag = NFSV3_WCCRATTR; | | | |
3137 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
3138 | struct nfsnode *np; | | | |
3139 | | | | |
3140 | KASSERT(NFS_ISV3(vp)); | | | |
3141 | | | | |
3142 | #ifdef NFS_DEBUG_COMMIT | | | |
3143 | printf("commit %lu - %lu\n", (unsigned long)offset, | | | |
3144 | (unsigned long)(offset + cnt)); | | | |
3145 | #endif | | | |
3146 | | | | |
3147 | mutex_enter(&nmp->nm_lock); | | | |
3148 | if ((nmp->nm_iflag & NFSMNT_HASWRITEVERF) == 0) { | | | |
3149 | mutex_exit(&nmp->nm_lock); | | | |
3150 | return (0); | | | |
3151 | } | | | |
3152 | mutex_exit(&nmp->nm_lock); | | | |
3153 | nfsstats.rpccnt[NFSPROC_COMMIT]++; | | | |
3154 | np = VTONFS(vp); | | | |
3155 | nfsm_reqhead(np, NFSPROC_COMMIT, NFSX_FH(1)); | | | |
3156 | nfsm_fhtom(np, 1); | | | |
3157 | nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED); | | | |
3158 | txdr_hyper(offset, tl); | | | |
3159 | tl += 2; | | | |
3160 | *tl = txdr_unsigned(cnt); | | | |
3161 | nfsm_request(np, NFSPROC_COMMIT, l, np->n_wcred); | | | |
3162 | nfsm_wcc_data(vp, wccflag, NAC_NOTRUNC, false); | | | |
3163 | if (!error) { | | | |
3164 | nfsm_dissect(tl, u_int32_t *, NFSX_V3WRITEVERF); | | | |
3165 | mutex_enter(&nmp->nm_lock); | | | |
3166 | if ((nmp->nm_iflag & NFSMNT_STALEWRITEVERF) || | | | |
3167 | memcmp(nmp->nm_writeverf, tl, NFSX_V3WRITEVERF)) { | | | |
3168 | memcpy(nmp->nm_writeverf, tl, NFSX_V3WRITEVERF); | | | |
3169 | error = NFSERR_STALEWRITEVERF; | | | |
3170 | nmp->nm_iflag |= NFSMNT_STALEWRITEVERF; | | | |
3171 | } | | | |
3172 | mutex_exit(&nmp->nm_lock); | | | |
3173 | } | | | |
3174 | nfsm_reqdone; | | | |
3175 | return (error); | | | |
3176 | } | | | |
3177 | #endif | | | |
3178 | | | | |
3179 | /* | | | |
3180 | * Kludge City.. | | | |
3181 | * - make nfs_bmap() essentially a no-op that does no translation | | | |
3182 | * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc | | | |
3183 | * (Maybe I could use the process's page mapping, but I was concerned that | | | |
3184 | * Kernel Write might not be enabled and also figured copyout() would do | | | |
3185 | * a lot more work than memcpy() and also it currently happens in the | | | |
3186 | * context of the swapper process (2). | | | |
3187 | */ | | | |
3188 | int | | | |
3189 | nfs_bmap(v) | | | |
3190 | void *v; | | | |
3191 | { | | | |
3192 | struct vop_bmap_args /* { | | | |
3193 | struct vnode *a_vp; | | | |
3194 | daddr_t a_bn; | | | |
3195 | struct vnode **a_vpp; | | | |
3196 | daddr_t *a_bnp; | | | |
3197 | int *a_runp; | | | |
3198 | } */ *ap = v; | | | |
3199 | struct vnode *vp = ap->a_vp; | | | |
3200 | int bshift = vp->v_mount->mnt_fs_bshift - vp->v_mount->mnt_dev_bshift; | | | |
3201 | | | | |
3202 | if (ap->a_vpp != NULL) | | | |
3203 | *ap->a_vpp = vp; | | | |
3204 | if (ap->a_bnp != NULL) | | | |
3205 | *ap->a_bnp = ap->a_bn << bshift; | | | |
3206 | if (ap->a_runp != NULL) | | | |
3207 | *ap->a_runp = 1024 * 1024; /* XXX */ | | | |
3208 | return (0); | | | |
3209 | } | | | |
3210 | | | | |
3211 | /* | | | |
3212 | * Strategy routine. | | | |
3213 | * For async requests when nfsiod(s) are running, queue the request by | | | |
3214 | * calling nfs_asyncio(), otherwise just all nfs_doio() to do the | | | |
3215 | * request. | | | |
3216 | */ | | | |
3217 | int | | | |
3218 | nfs_strategy(v) | | | |
3219 | void *v; | | | |
3220 | { | | | |
3221 | struct vop_strategy_args *ap = v; | | | |
3222 | struct buf *bp = ap->a_bp; | | | |
3223 | int error = 0; | | | |
3224 | | | | |
3225 | if ((bp->b_flags & (B_PHYS|B_ASYNC)) == (B_PHYS|B_ASYNC)) | | | |
3226 | panic("nfs physio/async"); | | | |
3227 | | | | |
3228 | /* | | | |
3229 | * If the op is asynchronous and an i/o daemon is waiting | | | |
3230 | * queue the request, wake it up and wait for completion | | | |
3231 | * otherwise just do it ourselves. | | | |
3232 | */ | | | |
3233 | if ((bp->b_flags & B_ASYNC) == 0 || nfs_asyncio(bp)) | | | |
3234 | error = nfs_doio(bp); | | | |
3235 | return (error); | | | |
3236 | } | | | |
3237 | | | | |
3238 | /* | | | |
3239 | * fsync vnode op. Just call nfs_flush() with commit == 1. | | | |
3240 | */ | | | |
3241 | /* ARGSUSED */ | | | |
3242 | int | | | |
3243 | nfs_fsync(v) | | | |
3244 | void *v; | | | |
3245 | { | | | |
3246 | struct vop_fsync_args /* { | | | |
3247 | struct vnodeop_desc *a_desc; | | | |
3248 | struct vnode * a_vp; | | | |
3249 | kauth_cred_t a_cred; | | | |
3250 | int a_flags; | | | |
3251 | off_t offlo; | | | |
3252 | off_t offhi; | | | |
3253 | struct lwp * a_l; | | | |
3254 | } */ *ap = v; | | | |
3255 | | | | |
3256 | struct vnode *vp = ap->a_vp; | | | |
3257 | | | | |
3258 | if (vp->v_type != VREG) | | | |
3259 | return 0; | | | |
3260 | | | | |
3261 | return (nfs_flush(vp, ap->a_cred, | | | |
3262 | (ap->a_flags & FSYNC_WAIT) != 0 ? MNT_WAIT : 0, curlwp, 1)); | | | |
3263 | } | | | |
3264 | | | | |
3265 | /* | | | |
3266 | * Flush all the data associated with a vnode. | | | |
3267 | */ | | | |
3268 | int | | | |
3269 | nfs_flush(struct vnode *vp, kauth_cred_t cred, int waitfor, struct lwp *l, | | | |
3270 | int commit) | | | |
3271 | { | | | |
3272 | struct nfsnode *np = VTONFS(vp); | | | |
3273 | int error; | | | |
3274 | int flushflags = PGO_ALLPAGES|PGO_CLEANIT|PGO_SYNCIO; | | | |
3275 | UVMHIST_FUNC("nfs_flush"); UVMHIST_CALLED(ubchist); | | | |
3276 | | | | |
3277 | mutex_enter(&vp->v_interlock); | | | |
3278 | error = VOP_PUTPAGES(vp, 0, 0, flushflags); | | | |
3279 | if (np->n_flag & NWRITEERR) { | | | |
3280 | error = np->n_error; | | | |
3281 | np->n_flag &= ~NWRITEERR; | | | |
3282 | } | | | |
3283 | UVMHIST_LOG(ubchist, "returning %d", error,0,0,0); | | | |
3284 | return (error); | | | |
3285 | } | | | |
3286 | | | | |
3287 | /* | | | |
3288 | * Return POSIX pathconf information applicable to nfs. | | | |
3289 | * | | | |
3290 | * N.B. The NFS V2 protocol doesn't support this RPC. | | | |
3291 | */ | | | |
3292 | /* ARGSUSED */ | | | |
3293 | int | | | |
3294 | nfs_pathconf(v) | | | |
3295 | void *v; | | | |
3296 | { | | | |
3297 | struct vop_pathconf_args /* { | | | |
3298 | struct vnode *a_vp; | | | |
3299 | int a_name; | | | |
3300 | register_t *a_retval; | | | |
3301 | } */ *ap = v; | | | |
3302 | struct nfsv3_pathconf *pcp; | | | |
3303 | struct vnode *vp = ap->a_vp; | | | |
3304 | struct mbuf *mreq, *mrep, *md, *mb; | | | |
3305 | int32_t t1, t2; | | | |
3306 | u_int32_t *tl; | | | |
3307 | char *bpos, *dpos, *cp, *cp2; | | | |
3308 | int error = 0, attrflag; | | | |
3309 | #ifndef NFS_V2_ONLY | | | |
3310 | struct nfsmount *nmp; | | | |
3311 | unsigned int l; | | | |
3312 | u_int64_t maxsize; | | | |
3313 | #endif | | | |
3314 | const int v3 = NFS_ISV3(vp); | | | |
3315 | struct nfsnode *np = VTONFS(vp); | | | |
3316 | | | | |
3317 | switch (ap->a_name) { | | | |
3318 | /* Names that can be resolved locally. */ | | | |
3319 | case _PC_PIPE_BUF: | | | |
3320 | *ap->a_retval = PIPE_BUF; | | | |
3321 | break; | | | |
3322 | case _PC_SYNC_IO: | | | |
3323 | *ap->a_retval = 1; | | | |
3324 | break; | | | |
3325 | /* Names that cannot be resolved locally; do an RPC, if possible. */ | | | |
3326 | case _PC_LINK_MAX: | | | |
3327 | case _PC_NAME_MAX: | | | |
3328 | case _PC_CHOWN_RESTRICTED: | | | |
3329 | case _PC_NO_TRUNC: | | | |
3330 | if (!v3) { | | | |
3331 | error = EINVAL; | | | |
3332 | break; | | | |
3333 | } | | | |
3334 | nfsstats.rpccnt[NFSPROC_PATHCONF]++; | | | |
3335 | nfsm_reqhead(np, NFSPROC_PATHCONF, NFSX_FH(1)); | | | |
3336 | nfsm_fhtom(np, 1); | | | |
3337 | nfsm_request(np, NFSPROC_PATHCONF, | | | |
3338 | curlwp, curlwp->l_cred); /* XXX */ | | | |
3339 | nfsm_postop_attr(vp, attrflag, 0); | | | |
3340 | if (!error) { | | | |
3341 | nfsm_dissect(pcp, struct nfsv3_pathconf *, | | | |
3342 | NFSX_V3PATHCONF); | | | |
3343 | switch (ap->a_name) { | | | |
3344 | case _PC_LINK_MAX: | | | |
3345 | *ap->a_retval = | | | |
3346 | fxdr_unsigned(register_t, pcp->pc_linkmax); | | | |
3347 | break; | | | |
3348 | case _PC_NAME_MAX: | | | |
3349 | *ap->a_retval = | | | |
3350 | fxdr_unsigned(register_t, pcp->pc_namemax); | | | |
3351 | break; | | | |
3352 | case _PC_CHOWN_RESTRICTED: | | | |
3353 | *ap->a_retval = | | | |
3354 | (pcp->pc_chownrestricted == nfs_true); | | | |
3355 | break; | | | |
3356 | case _PC_NO_TRUNC: | | | |
3357 | *ap->a_retval = | | | |
3358 | (pcp->pc_notrunc == nfs_true); | | | |
3359 | break; | | | |
3360 | } | | | |
3361 | } | | | |
3362 | nfsm_reqdone; | | | |
3363 | break; | | | |
3364 | case _PC_FILESIZEBITS: | | | |
3365 | #ifndef NFS_V2_ONLY | | | |
3366 | if (v3) { | | | |
3367 | nmp = VFSTONFS(vp->v_mount); | | | |
3368 | if ((nmp->nm_iflag & NFSMNT_GOTFSINFO) == 0) | | | |
3369 | if ((error = nfs_fsinfo(nmp, vp, | | | |
3370 | curlwp->l_cred, curlwp)) != 0) /* XXX */ | | | |
3371 | break; | | | |
3372 | for (l = 0, maxsize = nmp->nm_maxfilesize; | | | |
3373 | (maxsize >> l) > 0; l++) | | | |
3374 | ; | | | |
3375 | *ap->a_retval = l + 1; | | | |
3376 | } else | | | |
3377 | #endif | | | |
3378 | { | | | |
3379 | *ap->a_retval = 32; /* NFS V2 limitation */ | | | |
3380 | } | | | |
3381 | break; | | | |
3382 | default: | | | |
3383 | error = EINVAL; | | | |
3384 | break; | | | |
3385 | } | | | |
3386 | | | | |
3387 | return (error); | | | |
3388 | } | | | |
3389 | | | | |
3390 | /* | | | |
3391 | * NFS advisory byte-level locks. | | | |
3392 | */ | | | |
3393 | int | | | |
3394 | nfs_advlock(v) | | | |
3395 | void *v; | | | |
3396 | { | | | |
3397 | struct vop_advlock_args /* { | | | |
3398 | struct vnode *a_vp; | | | |
3399 | void *a_id; | | | |
3400 | int a_op; | | | |
3401 | struct flock *a_fl; | | | |
3402 | int a_flags; | | | |
3403 | } */ *ap = v; | | | |
3404 | struct nfsnode *np = VTONFS(ap->a_vp); | | | |
3405 | | | | |
3406 | return lf_advlock(ap, &np->n_lockf, np->n_size); | | | |
3407 | } | | | |
3408 | | | | |
3409 | /* | | | |
3410 | * Print out the contents of an nfsnode. | | | |
3411 | */ | | | |
3412 | int | | | |
3413 | nfs_print(v) | | | |
3414 | void *v; | | | |
3415 | { | | | |
3416 | struct vop_print_args /* { | | | |
3417 | struct vnode *a_vp; | | | |
3418 | } */ *ap = v; | | | |
3419 | struct vnode *vp = ap->a_vp; | | | |
3420 | struct nfsnode *np = VTONFS(vp); | | | |
3421 | | | | |
3422 | printf("tag VT_NFS, fileid %lld fsid 0x%lx", | | | |
3423 | (unsigned long long)np->n_vattr->va_fileid, np->n_vattr->va_fsid); | | | |
3424 | if (vp->v_type == VFIFO) | | | |
3425 | fifo_printinfo(vp); | | | |
3426 | printf("\n"); | | | |
3427 | return (0); | | | |
3428 | } | | | |
3429 | | | | |
3430 | /* | | | |
3431 | * nfs unlock wrapper. | | | |
3432 | */ | | | |
3433 | int | | | |
3434 | nfs_unlock(void *v) | | | |
3435 | { | | | |
3436 | struct vop_unlock_args /* { | | | |
3437 | struct vnode *a_vp; | | | |
3438 | int a_flags; | | | |
3439 | } */ *ap = v; | | | |
3440 | struct vnode *vp = ap->a_vp; | | | |
3441 | | | | |
3442 | /* | | | |
3443 | * VOP_UNLOCK can be called by nfs_loadattrcache | | | |
3444 | * with v_data == 0. | | | |
3445 | */ | | | |
3446 | if (VTONFS(vp)) { | | | |
3447 | nfs_delayedtruncate(vp); | | | |
3448 | } | | | |
3449 | | | | |
3450 | return genfs_unlock(v); | | | |
3451 | } | | | |
3452 | | | | |
3453 | /* | | | |
3454 | * nfs special file access vnode op. | | | |
3455 | * Essentially just get vattr and then imitate iaccess() since the device is | | | |
3456 | * local to the client. | | | |
3457 | */ | | | |
3458 | int | | | |
3459 | nfsspec_access(v) | | | |
3460 | void *v; | | | |
3461 | { | | | |
3462 | struct vop_access_args /* { | | | |
3463 | struct vnode *a_vp; | | | |
3464 | int a_mode; | | | |
3465 | kauth_cred_t a_cred; | | | |
3466 | struct lwp *a_l; | | | |
3467 | } */ *ap = v; | | | |
3468 | struct vattr va; | | | |
3469 | struct vnode *vp = ap->a_vp; | | | |
3470 | int error; | | | |
3471 | | | | |
3472 | error = VOP_GETATTR(vp, &va, ap->a_cred); | | | |
3473 | if (error) | | | |
3474 | return (error); | | | |
3475 | | | | |
3476 | /* | | | |
3477 | * Disallow write attempts on filesystems mounted read-only; | | | |
3478 | * unless the file is a socket, fifo, or a block or character | | | |
3479 | * device resident on the filesystem. | | | |
3480 | */ | | | |
3481 | if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { | | | |
3482 | switch (vp->v_type) { | | | |
3483 | case VREG: | | | |
3484 | case VDIR: | | | |
3485 | case VLNK: | | | |
3486 | return (EROFS); | | | |
3487 | default: | | | |
3488 | break; | | | |
3489 | } | | | |
3490 | } | | | |
3491 | | | | |
3492 | return (vaccess(va.va_type, va.va_mode, | | | |
3493 | va.va_uid, va.va_gid, ap->a_mode, ap->a_cred)); | | | |
3494 | } | | | |
3495 | | | | |
3496 | /* | | | |
3497 | * Read wrapper for special devices. | | | |
3498 | */ | | | |
3499 | int | | | |
3500 | nfsspec_read(v) | | | |
3501 | void *v; | | | |
3502 | { | | | |
3503 | struct vop_read_args /* { | | | |
3504 | struct vnode *a_vp; | | | |
3505 | struct uio *a_uio; | | | |
3506 | int a_ioflag; | | | |
3507 | kauth_cred_t a_cred; | | | |
3508 | } */ *ap = v; | | | |
3509 | struct nfsnode *np = VTONFS(ap->a_vp); | | | |
3510 | | | | |
3511 | /* | | | |
3512 | * Set access flag. | | | |
3513 | */ | | | |
3514 | np->n_flag |= NACC; | | | |
3515 | getnanotime(&np->n_atim); | | | |
3516 | return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap)); | | | |
3517 | } | | | |
3518 | | | | |
3519 | /* | | | |
3520 | * Write wrapper for special devices. | | | |
3521 | */ | | | |
3522 | int | | | |
3523 | nfsspec_write(v) | | | |
3524 | void *v; | | | |
3525 | { | | | |
3526 | struct vop_write_args /* { | | | |
3527 | struct vnode *a_vp; | | | |
3528 | struct uio *a_uio; | | | |
3529 | int a_ioflag; | | | |
3530 | kauth_cred_t a_cred; | | | |
3531 | } */ *ap = v; | | | |
3532 | struct nfsnode *np = VTONFS(ap->a_vp); | | | |
3533 | | | | |
3534 | /* | | | |
3535 | * Set update flag. | | | |
3536 | */ | | | |
3537 | np->n_flag |= NUPD; | | | |
3538 | getnanotime(&np->n_mtim); | | | |
3539 | return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap)); | | | |
3540 | } | | | |
3541 | | | | |
3542 | /* | | | |
3543 | * Close wrapper for special devices. | | | |
3544 | * | | | |
3545 | * Update the times on the nfsnode then do device close. | | | |
3546 | */ | | | |
3547 | int | | | |
3548 | nfsspec_close(v) | | | |
3549 | void *v; | | | |
3550 | { | | | |
3551 | struct vop_close_args /* { | | | |
3552 | struct vnode *a_vp; | | | |
3553 | int a_fflag; | | | |
3554 | kauth_cred_t a_cred; | | | |
3555 | struct lwp *a_l; | | | |
3556 | } */ *ap = v; | | | |
3557 | struct vnode *vp = ap->a_vp; | | | |
3558 | struct nfsnode *np = VTONFS(vp); | | | |
3559 | struct vattr vattr; | | | |
3560 | | | | |
3561 | if (np->n_flag & (NACC | NUPD)) { | | | |
3562 | np->n_flag |= NCHG; | | | |
3563 | if (vp->v_usecount == 1 && | | | |
3564 | (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { | | | |
3565 | VATTR_NULL(&vattr); | | | |
3566 | if (np->n_flag & NACC) | | | |
3567 | vattr.va_atime = np->n_atim; | | | |
3568 | if (np->n_flag & NUPD) | | | |
3569 | vattr.va_mtime = np->n_mtim; | | | |
3570 | (void)VOP_SETATTR(vp, &vattr, ap->a_cred); | | | |
3571 | } | | | |
3572 | } | | | |
3573 | return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap)); | | | |
3574 | } | | | |
3575 | | | | |
3576 | /* | | | |
3577 | * Read wrapper for fifos. | | | |
3578 | */ | | | |
3579 | int | | | |
3580 | nfsfifo_read(v) | | | |
3581 | void *v; | | | |
3582 | { | | | |
3583 | struct vop_read_args /* { | | | |
3584 | struct vnode *a_vp; | | | |
3585 | struct uio *a_uio; | | | |
3586 | int a_ioflag; | | | |
3587 | kauth_cred_t a_cred; | | | |
3588 | } */ *ap = v; | | | |
3589 | struct nfsnode *np = VTONFS(ap->a_vp); | | | |
3590 | | | | |
3591 | /* | | | |
3592 | * Set access flag. | | | |
3593 | */ | | | |
3594 | np->n_flag |= NACC; | | | |
3595 | getnanotime(&np->n_atim); | | | |
3596 | return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap)); | | | |
3597 | } | | | |
3598 | | | | |
3599 | /* | | | |
3600 | * Write wrapper for fifos. | | | |
3601 | */ | | | |
3602 | int | | | |
3603 | nfsfifo_write(v) | | | |
3604 | void *v; | | | |
3605 | { | | | |
3606 | struct vop_write_args /* { | | | |
3607 | struct vnode *a_vp; | | | |
3608 | struct uio *a_uio; | | | |
3609 | int a_ioflag; | | | |
3610 | kauth_cred_t a_cred; | | | |
3611 | } */ *ap = v; | | | |
3612 | struct nfsnode *np = VTONFS(ap->a_vp); | | | |
3613 | | | | |
3614 | /* | | | |
3615 | * Set update flag. | | | |
3616 | */ | | | |
3617 | np->n_flag |= NUPD; | | | |
3618 | getnanotime(&np->n_mtim); | | | |
3619 | return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap)); | | | |
3620 | } | | | |
3621 | | | | |
3622 | /* | | | |
3623 | * Close wrapper for fifos. | | | |
3624 | * | | | |
3625 | * Update the times on the nfsnode then do fifo close. | | | |
3626 | */ | | | |
3627 | int | | | |
3628 | nfsfifo_close(v) | | | |
3629 | void *v; | | | |
3630 | { | | | |
3631 | struct vop_close_args /* { | | | |
3632 | struct vnode *a_vp; | | | |
3633 | int a_fflag; | | | |
3634 | kauth_cred_t a_cred; | | | |
3635 | struct lwp *a_l; | | | |
3636 | } */ *ap = v; | | | |
3637 | struct vnode *vp = ap->a_vp; | | | |
3638 | struct nfsnode *np = VTONFS(vp); | | | |
3639 | struct vattr vattr; | | | |
3640 | | | | |
3641 | if (np->n_flag & (NACC | NUPD)) { | | | |
3642 | struct timespec ts; | | | |
3643 | | | | |
3644 | getnanotime(&ts); | | | |
3645 | if (np->n_flag & NACC) | | | |
3646 | np->n_atim = ts; | | | |
3647 | if (np->n_flag & NUPD) | | | |
3648 | np->n_mtim = ts; | | | |
3649 | np->n_flag |= NCHG; | | | |
3650 | if (vp->v_usecount == 1 && | | | |
3651 | (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { | | | |
3652 | VATTR_NULL(&vattr); | | | |
3653 | if (np->n_flag & NACC) | | | |
3654 | vattr.va_atime = np->n_atim; | | | |
3655 | if (np->n_flag & NUPD) | | | |
3656 | vattr.va_mtime = np->n_mtim; | | | |
3657 | (void)VOP_SETATTR(vp, &vattr, ap->a_cred); | | | |
3658 | } | | | |
3659 | } | | | |
3660 | return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap)); | | | |
3661 | } | | | |