]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
37839358 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
1c79356b | 11 | * |
37839358 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
37839358 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ | |
23 | /* | |
24 | * Copyright (c) 1989, 1993 | |
25 | * The Regents of the University of California. All rights reserved. | |
26 | * | |
27 | * This code is derived from software contributed to Berkeley by | |
28 | * Rick Macklem at The University of Guelph. | |
29 | * | |
30 | * Redistribution and use in source and binary forms, with or without | |
31 | * modification, are permitted provided that the following conditions | |
32 | * are met: | |
33 | * 1. Redistributions of source code must retain the above copyright | |
34 | * notice, this list of conditions and the following disclaimer. | |
35 | * 2. Redistributions in binary form must reproduce the above copyright | |
36 | * notice, this list of conditions and the following disclaimer in the | |
37 | * documentation and/or other materials provided with the distribution. | |
38 | * 3. All advertising materials mentioning features or use of this software | |
39 | * must display the following acknowledgement: | |
40 | * This product includes software developed by the University of | |
41 | * California, Berkeley and its contributors. | |
42 | * 4. Neither the name of the University nor the names of its contributors | |
43 | * may be used to endorse or promote products derived from this software | |
44 | * without specific prior written permission. | |
45 | * | |
46 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
47 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
48 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
49 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
50 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
51 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
52 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
53 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
54 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
55 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
56 | * SUCH DAMAGE. | |
57 | * | |
58 | * @(#)nfs_syscalls.c 8.5 (Berkeley) 3/30/95 | |
59 | * FreeBSD-Id: nfs_syscalls.c,v 1.32 1997/11/07 08:53:25 phk Exp $ | |
60 | */ | |
61 | ||
62 | #include <sys/param.h> | |
63 | #include <sys/systm.h> | |
64 | /* XXX CSM 11/25/97 FreeBSD's generated syscall prototypes */ | |
65 | #ifdef notyet | |
66 | #include <sys/sysproto.h> | |
67 | #endif | |
68 | #include <sys/kernel.h> | |
91447636 | 69 | #include <sys/file_internal.h> |
1c79356b A |
70 | #include <sys/filedesc.h> |
71 | #include <sys/stat.h> | |
91447636 A |
72 | #include <sys/vnode_internal.h> |
73 | #include <sys/mount_internal.h> | |
74 | #include <sys/proc_internal.h> /* for fdflags */ | |
75 | #include <sys/kauth.h> | |
1c79356b | 76 | #include <sys/sysctl.h> |
55e303ae | 77 | #include <sys/ubc.h> |
1c79356b A |
78 | #include <sys/uio.h> |
79 | #include <sys/malloc.h> | |
91447636 | 80 | #include <sys/kpi_mbuf.h> |
1c79356b A |
81 | #include <sys/socket.h> |
82 | #include <sys/socketvar.h> | |
83 | #include <sys/domain.h> | |
84 | #include <sys/protosw.h> | |
55e303ae A |
85 | #include <sys/fcntl.h> |
86 | #include <sys/lockf.h> | |
1c79356b A |
87 | #include <sys/syslog.h> |
88 | #include <sys/user.h> | |
91447636 A |
89 | #include <sys/sysproto.h> |
90 | #include <sys/kpi_socket.h> | |
91 | #include <libkern/OSAtomic.h> | |
1c79356b | 92 | |
ccc36f2f A |
93 | #include <bsm/audit_kernel.h> |
94 | ||
1c79356b A |
95 | #include <netinet/in.h> |
96 | #include <netinet/tcp.h> | |
97 | #if ISO | |
98 | #include <netiso/iso.h> | |
99 | #endif | |
100 | #include <nfs/xdr_subs.h> | |
101 | #include <nfs/rpcv2.h> | |
102 | #include <nfs/nfsproto.h> | |
103 | #include <nfs/nfs.h> | |
104 | #include <nfs/nfsm_subs.h> | |
105 | #include <nfs/nfsrvcache.h> | |
106 | #include <nfs/nfsmount.h> | |
107 | #include <nfs/nfsnode.h> | |
1c79356b | 108 | #include <nfs/nfsrtt.h> |
55e303ae | 109 | #include <nfs/nfs_lock.h> |
1c79356b | 110 | |
91447636 A |
111 | extern void unix_syscall_return(int); |
112 | ||
1c79356b | 113 | /* Global defs. */ |
91447636 | 114 | extern int (*nfsrv3_procs[NFS_NPROCS])(struct nfsrv_descript *nd, |
1c79356b | 115 | struct nfssvc_sock *slp, |
91447636 A |
116 | proc_t procp, |
117 | mbuf_t *mreqp); | |
1c79356b | 118 | extern int nfs_numasync; |
55e303ae | 119 | extern int nfs_ioddelwri; |
1c79356b A |
120 | extern int nfsrtton; |
121 | extern struct nfsstats nfsstats; | |
122 | extern int nfsrvw_procrastinate; | |
123 | extern int nfsrvw_procrastinate_v3; | |
91447636 | 124 | |
1c79356b A |
125 | struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock; |
126 | static int nuidhash_max = NFS_MAXUIDHASH; | |
127 | ||
91447636 A |
128 | static void nfsrv_zapsock(struct nfssvc_sock *slp); |
129 | static int nfssvc_iod(proc_t); | |
130 | static int nfskerb_clientd(struct nfsmount *, struct nfsd_cargs *, int, user_addr_t, proc_t); | |
1c79356b A |
131 | |
132 | static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON]; | |
133 | ||
134 | #ifndef NFS_NOSERVER | |
135 | int nfsd_waiting = 0; | |
136 | static struct nfsdrt nfsdrt; | |
91447636 A |
137 | int nfs_numnfsd = 0; |
138 | static void nfsd_rt(int sotype, struct nfsrv_descript *nd, int cacherep); | |
139 | static int nfssvc_addsock(socket_t, mbuf_t, proc_t); | |
140 | static int nfssvc_nfsd(struct nfsd_srvargs *,user_addr_t, proc_t); | |
141 | static int nfssvc_export(user_addr_t, proc_t); | |
1c79356b A |
142 | |
143 | static int nfs_privport = 0; | |
144 | /* XXX CSM 11/25/97 Upgrade sysctl.h someday */ | |
145 | #ifdef notyet | |
146 | SYSCTL_INT(_vfs_nfs, NFS_NFSPRIVPORT, nfs_privport, CTLFLAG_RW, &nfs_privport, 0, ""); | |
147 | SYSCTL_INT(_vfs_nfs, OID_AUTO, gatherdelay, CTLFLAG_RW, &nfsrvw_procrastinate, 0, ""); | |
148 | SYSCTL_INT(_vfs_nfs, OID_AUTO, gatherdelay_v3, CTLFLAG_RW, &nfsrvw_procrastinate_v3, 0, ""); | |
149 | #endif | |
150 | ||
151 | /* | |
152 | * NFS server system calls | |
153 | * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c | |
154 | */ | |
155 | ||
156 | /* | |
157 | * Get file handle system call | |
158 | */ | |
1c79356b | 159 | int |
91447636 | 160 | getfh(proc_t p, struct getfh_args *uap, __unused int *retval) |
1c79356b | 161 | { |
91447636 A |
162 | vnode_t vp; |
163 | struct nfs_filehandle nfh; | |
1c79356b A |
164 | int error; |
165 | struct nameidata nd; | |
91447636 A |
166 | struct vfs_context context; |
167 | char path[MAXPATHLEN], *ptr; | |
168 | u_int pathlen; | |
169 | struct nfs_exportfs *nxfs; | |
170 | struct nfs_export *nx; | |
171 | ||
172 | context.vc_proc = p; | |
173 | context.vc_ucred = kauth_cred_get(); | |
1c79356b A |
174 | |
175 | /* | |
176 | * Must be super user | |
177 | */ | |
91447636 A |
178 | error = proc_suser(p); |
179 | if (error) | |
180 | return (error); | |
181 | ||
182 | error = copyinstr(uap->fname, path, MAXPATHLEN, (size_t *)&pathlen); | |
183 | if (error) | |
1c79356b | 184 | return (error); |
91447636 A |
185 | |
186 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, | |
187 | UIO_SYSSPACE, path, &context); | |
1c79356b A |
188 | error = namei(&nd); |
189 | if (error) | |
190 | return (error); | |
91447636 A |
191 | nameidone(&nd); |
192 | ||
1c79356b | 193 | vp = nd.ni_vp; |
91447636 A |
194 | |
195 | // find exportfs that matches f_mntonname | |
196 | lck_rw_lock_shared(&nfs_export_rwlock); | |
197 | ptr = vnode_mount(vp)->mnt_vfsstat.f_mntonname; | |
198 | LIST_FOREACH(nxfs, &nfs_exports, nxfs_next) { | |
199 | if (!strcmp(nxfs->nxfs_path, ptr)) | |
200 | break; | |
201 | } | |
202 | if (!nxfs || strncmp(nxfs->nxfs_path, path, strlen(nxfs->nxfs_path))) { | |
203 | error = EINVAL; | |
204 | goto out; | |
205 | } | |
206 | // find export that best matches remainder of path | |
207 | ptr = path + strlen(nxfs->nxfs_path); | |
208 | while (*ptr && (*ptr == '/')) | |
209 | ptr++; | |
210 | LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) { | |
211 | int len = strlen(nx->nx_path); | |
212 | if (len == 0) // we've hit the export entry for the root directory | |
213 | break; | |
214 | if (!strncmp(nx->nx_path, ptr, len)) | |
215 | break; | |
216 | } | |
217 | if (!nx) { | |
218 | error = EINVAL; | |
219 | goto out; | |
220 | } | |
221 | ||
222 | bzero(&nfh, sizeof(nfh)); | |
c0fea474 A |
223 | nfh.nfh_xh.nxh_version = htonl(NFS_FH_VERSION); |
224 | nfh.nfh_xh.nxh_fsid = htonl(nxfs->nxfs_id); | |
225 | nfh.nfh_xh.nxh_expid = htonl(nx->nx_id); | |
91447636 A |
226 | nfh.nfh_xh.nxh_flags = 0; |
227 | nfh.nfh_xh.nxh_reserved = 0; | |
228 | nfh.nfh_len = NFS_MAX_FID_SIZE; | |
229 | error = VFS_VPTOFH(vp, &nfh.nfh_len, &nfh.nfh_fid[0], NULL); | |
230 | if (nfh.nfh_len > (int)NFS_MAX_FID_SIZE) | |
231 | error = EOVERFLOW; | |
232 | nfh.nfh_xh.nxh_fidlen = nfh.nfh_len; | |
233 | nfh.nfh_len += sizeof(nfh.nfh_xh); | |
234 | ||
235 | out: | |
236 | lck_rw_done(&nfs_export_rwlock); | |
237 | vnode_put(vp); | |
1c79356b A |
238 | if (error) |
239 | return (error); | |
91447636 | 240 | error = copyout((caddr_t)&nfh, uap->fhp, sizeof(nfh)); |
1c79356b A |
241 | return (error); |
242 | } | |
243 | ||
244 | #endif /* NFS_NOSERVER */ | |
55e303ae | 245 | |
91447636 A |
246 | extern struct fileops vnops; |
247 | ||
55e303ae A |
248 | /* |
249 | * syscall for the rpc.lockd to use to translate a NFS file handle into | |
250 | * an open descriptor. | |
251 | * | |
252 | * warning: do not remove the suser() call or this becomes one giant | |
253 | * security hole. | |
254 | */ | |
55e303ae | 255 | int |
91447636 A |
256 | fhopen( proc_t p, |
257 | struct fhopen_args *uap, | |
258 | register_t *retval) | |
55e303ae | 259 | { |
91447636 A |
260 | vnode_t vp; |
261 | struct nfs_filehandle nfh; | |
262 | struct nfs_export *nx; | |
263 | struct nfs_export_options *nxo; | |
55e303ae | 264 | struct flock lf; |
91447636 A |
265 | struct fileproc *fp, *nfp; |
266 | int fmode, error, type; | |
55e303ae | 267 | int indx; |
91447636 A |
268 | kauth_cred_t cred = proc_ucred(p); |
269 | struct vfs_context context; | |
270 | kauth_action_t action; | |
271 | ||
272 | context.vc_proc = p; | |
273 | context.vc_ucred = cred; | |
55e303ae A |
274 | |
275 | /* | |
276 | * Must be super user | |
277 | */ | |
91447636 | 278 | error = suser(cred, 0); |
55e303ae A |
279 | if (error) |
280 | return (error); | |
281 | ||
282 | fmode = FFLAGS(uap->flags); | |
283 | /* why not allow a non-read/write open for our lockd? */ | |
284 | if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) | |
285 | return (EINVAL); | |
91447636 A |
286 | |
287 | error = copyin(uap->u_fhp, &nfh.nfh_len, sizeof(nfh.nfh_len)); | |
288 | if (error) | |
289 | return (error); | |
290 | if ((nfh.nfh_len < (int)sizeof(struct nfs_exphandle)) || | |
291 | (nfh.nfh_len > (int)NFS_MAX_FH_SIZE)) | |
292 | return (EINVAL); | |
293 | error = copyin(uap->u_fhp, &nfh, sizeof(nfh.nfh_len) + nfh.nfh_len); | |
55e303ae A |
294 | if (error) |
295 | return (error); | |
91447636 A |
296 | |
297 | lck_rw_lock_shared(&nfs_export_rwlock); | |
298 | /* now give me my vnode, it gets returned to me with a reference */ | |
299 | error = nfsrv_fhtovp(&nfh, NULL, TRUE, &vp, &nx, &nxo); | |
300 | lck_rw_done(&nfs_export_rwlock); | |
55e303ae A |
301 | if (error) |
302 | return (error); | |
91447636 | 303 | |
55e303ae | 304 | /* |
91447636 A |
305 | * From now on we have to make sure not |
306 | * to forget about the vnode. | |
307 | * Any error that causes an abort must vnode_put(vp). | |
308 | * Just set error = err and 'goto bad;'. | |
55e303ae A |
309 | */ |
310 | ||
311 | /* | |
312 | * from vn_open | |
313 | */ | |
91447636 | 314 | if (vnode_vtype(vp) == VSOCK) { |
55e303ae A |
315 | error = EOPNOTSUPP; |
316 | goto bad; | |
317 | } | |
318 | ||
91447636 A |
319 | /* disallow write operations on directories */ |
320 | if (vnode_isdir(vp) && (fmode & (FWRITE | O_TRUNC))) { | |
321 | error = EISDIR; | |
55e303ae A |
322 | goto bad; |
323 | } | |
324 | ||
91447636 A |
325 | /* compute action to be authorized */ |
326 | action = 0; | |
327 | if (fmode & FREAD) | |
328 | action |= KAUTH_VNODE_READ_DATA; | |
329 | if (fmode & (FWRITE | O_TRUNC)) | |
330 | action |= KAUTH_VNODE_WRITE_DATA; | |
331 | if ((error = vnode_authorize(vp, NULL, action, &context)) != 0) | |
332 | goto bad; | |
55e303ae | 333 | |
91447636 A |
334 | if ((error = VNOP_OPEN(vp, fmode, &context))) |
335 | goto bad; | |
336 | if ((error = vnode_ref_ext(vp, fmode))) | |
55e303ae A |
337 | goto bad; |
338 | ||
55e303ae A |
339 | /* |
340 | * end of vn_open code | |
341 | */ | |
342 | ||
91447636 | 343 | // starting here... error paths should call vn_close/vnode_put |
55e303ae | 344 | if ((error = falloc(p, &nfp, &indx)) != 0) { |
91447636 | 345 | vn_close(vp, fmode & FMASK, cred, p); |
55e303ae A |
346 | goto bad; |
347 | } | |
348 | fp = nfp; | |
349 | ||
91447636 A |
350 | fp->f_fglob->fg_flag = fmode & FMASK; |
351 | fp->f_fglob->fg_type = DTYPE_VNODE; | |
352 | fp->f_fglob->fg_ops = &vnops; | |
353 | fp->f_fglob->fg_data = (caddr_t)vp; | |
354 | ||
355 | // XXX do we really need to support this with fhopen()? | |
55e303ae A |
356 | if (fmode & (O_EXLOCK | O_SHLOCK)) { |
357 | lf.l_whence = SEEK_SET; | |
358 | lf.l_start = 0; | |
359 | lf.l_len = 0; | |
360 | if (fmode & O_EXLOCK) | |
361 | lf.l_type = F_WRLCK; | |
362 | else | |
363 | lf.l_type = F_RDLCK; | |
364 | type = F_FLOCK; | |
365 | if ((fmode & FNONBLOCK) == 0) | |
366 | type |= F_WAIT; | |
91447636 A |
367 | if ((error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob, F_SETLK, &lf, type, &context))) { |
368 | vn_close(vp, fp->f_fglob->fg_flag, fp->f_fglob->fg_cred, p); | |
369 | fp_free(p, indx, fp); | |
55e303ae A |
370 | return (error); |
371 | } | |
91447636 | 372 | fp->f_fglob->fg_flag |= FHASLOCK; |
55e303ae A |
373 | } |
374 | ||
91447636 A |
375 | vnode_put(vp); |
376 | ||
377 | proc_fdlock(p); | |
55e303ae | 378 | *fdflags(p, indx) &= ~UF_RESERVED; |
91447636 A |
379 | fp_drop(p, indx, fp, 1); |
380 | proc_fdunlock(p); | |
381 | ||
55e303ae A |
382 | *retval = indx; |
383 | return (0); | |
384 | ||
385 | bad: | |
91447636 | 386 | vnode_put(vp); |
55e303ae A |
387 | return (error); |
388 | } | |
389 | ||
1c79356b A |
390 | /* |
391 | * Nfs server psuedo system call for the nfsd's | |
392 | * Based on the flag value it either: | |
393 | * - adds a socket to the selection list | |
394 | * - remains in the kernel as an nfsd | |
395 | * - remains in the kernel as an nfsiod | |
396 | */ | |
1c79356b | 397 | int |
91447636 | 398 | nfssvc(proc_t p, struct nfssvc_args *uap, __unused int *retval) |
1c79356b A |
399 | { |
400 | #ifndef NFS_NOSERVER | |
401 | struct nameidata nd; | |
91447636 A |
402 | mbuf_t nam; |
403 | struct user_nfsd_args user_nfsdarg; | |
1c79356b A |
404 | struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs; |
405 | struct nfsd_cargs ncd; | |
406 | struct nfsd *nfsd; | |
407 | struct nfssvc_sock *slp; | |
408 | struct nfsuid *nuidp; | |
409 | struct nfsmount *nmp; | |
55e303ae | 410 | struct timeval now; |
91447636 A |
411 | socket_t so; |
412 | struct vfs_context context; | |
413 | struct ucred temp_cred; | |
1c79356b A |
414 | #endif /* NFS_NOSERVER */ |
415 | int error; | |
416 | ||
ccc36f2f A |
417 | AUDIT_ARG(cmd, uap->flag); |
418 | ||
1c79356b A |
419 | /* |
420 | * Must be super user | |
421 | */ | |
91447636 | 422 | error = proc_suser(p); |
1c79356b A |
423 | if(error) |
424 | return (error); | |
1c79356b A |
425 | if (uap->flag & NFSSVC_BIOD) |
426 | error = nfssvc_iod(p); | |
427 | #ifdef NFS_NOSERVER | |
428 | else | |
429 | error = ENXIO; | |
430 | #else /* !NFS_NOSERVER */ | |
431 | else if (uap->flag & NFSSVC_MNTD) { | |
91447636 A |
432 | |
433 | context.vc_proc = p; | |
434 | context.vc_ucred = kauth_cred_get(); | |
435 | ||
1c79356b A |
436 | error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd)); |
437 | if (error) | |
438 | return (error); | |
91447636 A |
439 | |
440 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, | |
441 | (proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), | |
442 | CAST_USER_ADDR_T(ncd.ncd_dirp), &context); | |
1c79356b A |
443 | error = namei(&nd); |
444 | if (error) | |
445 | return (error); | |
91447636 A |
446 | nameidone(&nd); |
447 | ||
448 | if (vnode_isvroot(nd.ni_vp) == 0) | |
1c79356b | 449 | error = EINVAL; |
91447636 A |
450 | nmp = VFSTONFS(vnode_mount(nd.ni_vp)); |
451 | vnode_put(nd.ni_vp); | |
1c79356b A |
452 | if (error) |
453 | return (error); | |
454 | ||
55e303ae | 455 | if ((nmp->nm_state & NFSSTA_MNTD) && |
1c79356b A |
456 | (uap->flag & NFSSVC_GOTAUTH) == 0) |
457 | return (0); | |
55e303ae | 458 | nmp->nm_state |= NFSSTA_MNTD; |
91447636 | 459 | error = nfskerb_clientd(nmp, &ncd, uap->flag, uap->argp, p); |
1c79356b | 460 | } else if (uap->flag & NFSSVC_ADDSOCK) { |
91447636 A |
461 | if (IS_64BIT_PROCESS(p)) { |
462 | error = copyin(uap->argp, (caddr_t)&user_nfsdarg, sizeof(user_nfsdarg)); | |
463 | } else { | |
464 | struct nfsd_args tmp_args; | |
465 | error = copyin(uap->argp, (caddr_t)&tmp_args, sizeof(tmp_args)); | |
466 | if (error == 0) { | |
467 | user_nfsdarg.sock = tmp_args.sock; | |
468 | user_nfsdarg.name = CAST_USER_ADDR_T(tmp_args.name); | |
469 | user_nfsdarg.namelen = tmp_args.namelen; | |
470 | } | |
471 | } | |
1c79356b A |
472 | if (error) |
473 | return (error); | |
91447636 A |
474 | /* get the socket */ |
475 | error = file_socket(user_nfsdarg.sock, &so); | |
1c79356b A |
476 | if (error) |
477 | return (error); | |
91447636 A |
478 | /* Get the client address for connected sockets. */ |
479 | if (user_nfsdarg.name == USER_ADDR_NULL || user_nfsdarg.namelen == 0) { | |
480 | nam = NULL; | |
481 | } else { | |
482 | error = sockargs(&nam, user_nfsdarg.name, user_nfsdarg.namelen, MBUF_TYPE_SONAME); | |
483 | if (error) { | |
484 | /* drop the iocount file_socket() grabbed on the file descriptor */ | |
485 | file_drop(user_nfsdarg.sock); | |
1c79356b | 486 | return (error); |
91447636 | 487 | } |
1c79356b | 488 | } |
91447636 A |
489 | /* |
490 | * nfssvc_addsock() will grab a retain count on the socket | |
491 | * to keep the socket from being closed when nfsd closes its | |
492 | * file descriptor for it. | |
493 | */ | |
494 | error = nfssvc_addsock(so, nam, p); | |
495 | /* drop the iocount file_socket() grabbed on the file descriptor */ | |
496 | file_drop(user_nfsdarg.sock); | |
497 | } else if (uap->flag & NFSSVC_NFSD) { | |
1c79356b A |
498 | error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd)); |
499 | if (error) | |
500 | return (error); | |
501 | ||
1c79356b A |
502 | if ((uap->flag & NFSSVC_AUTHIN) && ((nfsd = nsd->nsd_nfsd)) && |
503 | (nfsd->nfsd_slp->ns_flag & SLP_VALID)) { | |
504 | slp = nfsd->nfsd_slp; | |
505 | ||
506 | /* | |
507 | * First check to see if another nfsd has already | |
508 | * added this credential. | |
509 | */ | |
510 | for (nuidp = NUIDHASH(slp,nsd->nsd_cr.cr_uid)->lh_first; | |
511 | nuidp != 0; nuidp = nuidp->nu_hash.le_next) { | |
91447636 | 512 | if (kauth_cred_getuid(nuidp->nu_cr) == nsd->nsd_cr.cr_uid && |
1c79356b A |
513 | (!nfsd->nfsd_nd->nd_nam2 || |
514 | netaddr_match(NU_NETFAM(nuidp), | |
515 | &nuidp->nu_haddr, nfsd->nfsd_nd->nd_nam2))) | |
516 | break; | |
517 | } | |
518 | if (nuidp) { | |
91447636 | 519 | nfsrv_setcred(nuidp->nu_cr,nfsd->nfsd_nd->nd_cr); |
1c79356b A |
520 | nfsd->nfsd_nd->nd_flag |= ND_KERBFULL; |
521 | } else { | |
522 | /* | |
523 | * Nope, so we will. | |
524 | */ | |
525 | if (slp->ns_numuids < nuidhash_max) { | |
526 | slp->ns_numuids++; | |
527 | nuidp = (struct nfsuid *) | |
528 | _MALLOC_ZONE(sizeof (struct nfsuid), | |
529 | M_NFSUID, M_WAITOK); | |
530 | } else | |
531 | nuidp = (struct nfsuid *)0; | |
532 | if ((slp->ns_flag & SLP_VALID) == 0) { | |
91447636 | 533 | if (nuidp) { |
55e303ae | 534 | FREE_ZONE((caddr_t)nuidp, |
1c79356b | 535 | sizeof (struct nfsuid), M_NFSUID); |
91447636 A |
536 | slp->ns_numuids--; |
537 | } | |
1c79356b A |
538 | } else { |
539 | if (nuidp == (struct nfsuid *)0) { | |
540 | nuidp = slp->ns_uidlruhead.tqh_first; | |
91447636 A |
541 | if (!nuidp) |
542 | return (ENOMEM); | |
1c79356b A |
543 | LIST_REMOVE(nuidp, nu_hash); |
544 | TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, | |
545 | nu_lru); | |
546 | if (nuidp->nu_flag & NU_NAM) | |
91447636 A |
547 | mbuf_freem(nuidp->nu_nam); |
548 | kauth_cred_rele(nuidp->nu_cr); | |
55e303ae | 549 | } |
1c79356b | 550 | nuidp->nu_flag = 0; |
91447636 A |
551 | |
552 | if (nsd->nsd_cr.cr_ngroups > NGROUPS) | |
553 | nsd->nsd_cr.cr_ngroups = NGROUPS; | |
554 | ||
555 | nfsrv_setcred(&nsd->nsd_cr, &temp_cred); | |
556 | nuidp->nu_cr = kauth_cred_create(&temp_cred); | |
557 | ||
558 | if (!nuidp->nu_cr) { | |
559 | FREE_ZONE(nuidp, sizeof(struct nfsuid), M_NFSUID); | |
560 | slp->ns_numuids--; | |
561 | return (ENOMEM); | |
562 | } | |
1c79356b | 563 | nuidp->nu_timestamp = nsd->nsd_timestamp; |
55e303ae A |
564 | microtime(&now); |
565 | nuidp->nu_expire = now.tv_sec + nsd->nsd_ttl; | |
1c79356b A |
566 | /* |
567 | * and save the session key in nu_key. | |
568 | */ | |
569 | bcopy(nsd->nsd_key, nuidp->nu_key, | |
570 | sizeof (nsd->nsd_key)); | |
571 | if (nfsd->nfsd_nd->nd_nam2) { | |
572 | struct sockaddr_in *saddr; | |
573 | ||
91447636 | 574 | saddr = mbuf_data(nfsd->nfsd_nd->nd_nam2); |
1c79356b A |
575 | switch (saddr->sin_family) { |
576 | case AF_INET: | |
577 | nuidp->nu_flag |= NU_INETADDR; | |
578 | nuidp->nu_inetaddr = | |
579 | saddr->sin_addr.s_addr; | |
580 | break; | |
581 | case AF_ISO: | |
582 | default: | |
583 | nuidp->nu_flag |= NU_NAM; | |
91447636 A |
584 | error = mbuf_copym(nfsd->nfsd_nd->nd_nam2, 0, |
585 | MBUF_COPYALL, MBUF_WAITOK, | |
586 | &nuidp->nu_nam); | |
587 | if (error) { | |
588 | kauth_cred_rele(nuidp->nu_cr); | |
589 | FREE_ZONE(nuidp, sizeof(struct nfsuid), M_NFSUID); | |
590 | slp->ns_numuids--; | |
591 | return (error); | |
592 | } | |
1c79356b A |
593 | break; |
594 | }; | |
595 | } | |
596 | TAILQ_INSERT_TAIL(&slp->ns_uidlruhead, nuidp, | |
597 | nu_lru); | |
598 | LIST_INSERT_HEAD(NUIDHASH(slp, nsd->nsd_uid), | |
599 | nuidp, nu_hash); | |
91447636 A |
600 | nfsrv_setcred(nuidp->nu_cr, |
601 | nfsd->nfsd_nd->nd_cr); | |
1c79356b A |
602 | nfsd->nfsd_nd->nd_flag |= ND_KERBFULL; |
603 | } | |
604 | } | |
605 | } | |
606 | if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd)) | |
607 | nfsd->nfsd_flag |= NFSD_AUTHFAIL; | |
608 | error = nfssvc_nfsd(nsd, uap->argp, p); | |
91447636 A |
609 | } else if (uap->flag & NFSSVC_EXPORT) { |
610 | error = nfssvc_export(uap->argp, p); | |
611 | } else { | |
612 | error = EINVAL; | |
1c79356b A |
613 | } |
614 | #endif /* NFS_NOSERVER */ | |
615 | if (error == EINTR || error == ERESTART) | |
616 | error = 0; | |
617 | return (error); | |
618 | } | |
619 | ||
91447636 A |
620 | /* |
621 | * NFSKERB client helper daemon. | |
622 | * Gets authorization strings for "kerb" mounts. | |
623 | */ | |
624 | static int | |
625 | nfskerb_clientd( | |
626 | struct nfsmount *nmp, | |
627 | struct nfsd_cargs *ncd, | |
628 | int flag, | |
629 | user_addr_t argp, | |
630 | proc_t p) | |
631 | { | |
632 | struct nfsuid *nuidp, *nnuidp; | |
633 | int error = 0; | |
634 | struct nfsreq *rp; | |
635 | struct timeval now; | |
636 | ||
637 | /* | |
638 | * First initialize some variables | |
639 | */ | |
640 | microtime(&now); | |
641 | ||
642 | /* | |
643 | * If an authorization string is being passed in, get it. | |
644 | */ | |
645 | if ((flag & NFSSVC_GOTAUTH) && (nmp->nm_state & NFSSTA_MOUNTED) && | |
646 | ((nmp->nm_state & NFSSTA_WAITAUTH) == 0)) { | |
647 | if (nmp->nm_state & NFSSTA_HASAUTH) | |
648 | panic("cld kerb"); | |
649 | if ((flag & NFSSVC_AUTHINFAIL) == 0) { | |
650 | if (ncd->ncd_authlen <= nmp->nm_authlen && | |
651 | ncd->ncd_verflen <= nmp->nm_verflen && | |
652 | !copyin(CAST_USER_ADDR_T(ncd->ncd_authstr),nmp->nm_authstr,ncd->ncd_authlen)&& | |
653 | !copyin(CAST_USER_ADDR_T(ncd->ncd_verfstr),nmp->nm_verfstr,ncd->ncd_verflen)){ | |
654 | nmp->nm_authtype = ncd->ncd_authtype; | |
655 | nmp->nm_authlen = ncd->ncd_authlen; | |
656 | nmp->nm_verflen = ncd->ncd_verflen; | |
657 | #if NFSKERB | |
658 | nmp->nm_key = ncd->ncd_key; | |
659 | #endif | |
660 | } else | |
661 | nmp->nm_state |= NFSSTA_AUTHERR; | |
662 | } else | |
663 | nmp->nm_state |= NFSSTA_AUTHERR; | |
664 | nmp->nm_state |= NFSSTA_HASAUTH; | |
665 | wakeup((caddr_t)&nmp->nm_authlen); | |
666 | } else { | |
667 | nmp->nm_state |= NFSSTA_WAITAUTH; | |
668 | } | |
669 | ||
670 | /* | |
671 | * Loop every second updating queue until there is a termination sig. | |
672 | */ | |
673 | while (nmp->nm_state & NFSSTA_MOUNTED) { | |
674 | /* Get an authorization string, if required. */ | |
675 | if ((nmp->nm_state & (NFSSTA_WAITAUTH | NFSSTA_HASAUTH)) == 0) { | |
676 | ncd->ncd_authuid = nmp->nm_authuid; | |
677 | if (copyout((caddr_t)ncd, argp, sizeof (struct nfsd_cargs))) | |
678 | nmp->nm_state |= NFSSTA_WAITAUTH; | |
679 | else | |
680 | return (ENEEDAUTH); | |
681 | } | |
682 | /* Wait a bit (no pun) and do it again. */ | |
683 | if ((nmp->nm_state & NFSSTA_MOUNTED) && | |
684 | (nmp->nm_state & (NFSSTA_WAITAUTH | NFSSTA_HASAUTH))) { | |
685 | error = tsleep((caddr_t)&nmp->nm_authstr, PSOCK | PCATCH, | |
686 | "nfskrbtimr", hz / 3); | |
687 | if (error == EINTR || error == ERESTART) | |
688 | dounmount(nmp->nm_mountp, 0, p); | |
689 | } | |
690 | } | |
691 | ||
692 | /* | |
693 | * Finally, we can free up the mount structure. | |
694 | */ | |
695 | for (nuidp = nmp->nm_uidlruhead.tqh_first; nuidp != 0; nuidp = nnuidp) { | |
696 | nnuidp = nuidp->nu_lru.tqe_next; | |
697 | LIST_REMOVE(nuidp, nu_hash); | |
698 | TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru); | |
699 | kauth_cred_rele(nuidp->nu_cr); | |
700 | FREE_ZONE((caddr_t)nuidp, sizeof (struct nfsuid), M_NFSUID); | |
701 | } | |
702 | /* | |
703 | * Loop through outstanding request list and remove dangling | |
704 | * references to defunct nfsmount struct | |
705 | */ | |
706 | for (rp = nfs_reqq.tqh_first; rp; rp = rp->r_chain.tqe_next) | |
707 | if (rp->r_nmp == nmp) | |
708 | rp->r_nmp = (struct nfsmount *)0; | |
709 | /* Need to wake up any rcvlock waiters so they notice the unmount. */ | |
710 | if (nmp->nm_state & NFSSTA_WANTRCV) { | |
711 | nmp->nm_state &= ~NFSSTA_WANTRCV; | |
712 | wakeup(&nmp->nm_state); | |
713 | } | |
714 | FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT); | |
715 | if (error == EWOULDBLOCK) | |
716 | error = 0; | |
717 | return (error); | |
718 | } | |
719 | ||
1c79356b A |
720 | #ifndef NFS_NOSERVER |
721 | /* | |
722 | * Adds a socket to the list for servicing by nfsds. | |
723 | */ | |
724 | static int | |
91447636 A |
725 | nfssvc_addsock( |
726 | socket_t so, | |
727 | mbuf_t mynam, | |
728 | __unused proc_t p) | |
1c79356b | 729 | { |
91447636 A |
730 | int siz; |
731 | struct nfssvc_sock *slp; | |
732 | struct nfssvc_sock *tslp = NULL; | |
733 | int error, sodomain, sotype, soprotocol, on = 1; | |
734 | struct timeval timeo; | |
735 | ||
736 | /* make sure mbuf constants are set up */ | |
737 | if (!nfs_mbuf_mlen) | |
738 | nfs_mbuf_init(); | |
739 | ||
740 | sock_gettype(so, &sodomain, &sotype, &soprotocol); | |
741 | ||
1c79356b A |
742 | /* |
743 | * Add it to the list, as required. | |
744 | */ | |
91447636 | 745 | if (soprotocol == IPPROTO_UDP) { |
1c79356b | 746 | tslp = nfs_udpsock; |
91447636 A |
747 | if (!tslp || (tslp->ns_flag & SLP_VALID)) { |
748 | mbuf_freem(mynam); | |
1c79356b A |
749 | return (EPERM); |
750 | } | |
751 | #if ISO | |
91447636 | 752 | } else if (soprotocol == ISOPROTO_CLTP) { |
1c79356b | 753 | tslp = nfs_cltpsock; |
91447636 A |
754 | if (!tslp || (tslp->ns_flag & SLP_VALID)) { |
755 | mbuf_freem(mynam); | |
1c79356b A |
756 | return (EPERM); |
757 | } | |
758 | #endif /* ISO */ | |
759 | } | |
55e303ae A |
760 | /* reserve buffer space for 2 maximally-sized packets */ |
761 | siz = NFS_MAXPACKET; | |
91447636 | 762 | if (sotype == SOCK_STREAM) |
55e303ae A |
763 | siz += sizeof (u_long); |
764 | siz *= 2; | |
765 | if (siz > NFS_MAXSOCKBUF) | |
766 | siz = NFS_MAXSOCKBUF; | |
91447636 A |
767 | if ((error = sock_setsockopt(so, SOL_SOCKET, SO_SNDBUF, &siz, sizeof(siz))) || |
768 | (error = sock_setsockopt(so, SOL_SOCKET, SO_RCVBUF, &siz, sizeof(siz)))) { | |
769 | mbuf_freem(mynam); | |
1c79356b A |
770 | return (error); |
771 | } | |
772 | ||
773 | /* | |
774 | * Set protocol specific options { for now TCP only } and | |
775 | * reserve some space. For datagram sockets, this can get called | |
776 | * repeatedly for the same socket, but that isn't harmful. | |
777 | */ | |
91447636 A |
778 | if (sotype == SOCK_STREAM) { |
779 | sock_setsockopt(so, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); | |
1c79356b | 780 | } |
91447636 A |
781 | if (sodomain == AF_INET && soprotocol == IPPROTO_TCP) { |
782 | sock_setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); | |
1c79356b A |
783 | } |
784 | ||
91447636 A |
785 | sock_nointerrupt(so, 0); |
786 | ||
787 | timeo.tv_usec = 0; | |
788 | timeo.tv_sec = 0; | |
789 | error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); | |
790 | error = sock_setsockopt(so, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)); | |
791 | ||
792 | if (tslp) { | |
1c79356b | 793 | slp = tslp; |
91447636 A |
794 | lck_mtx_lock(nfsd_mutex); |
795 | } else { | |
1c79356b A |
796 | MALLOC(slp, struct nfssvc_sock *, sizeof(struct nfssvc_sock), |
797 | M_NFSSVC, M_WAITOK); | |
91447636 A |
798 | if (!slp) { |
799 | mbuf_freem(mynam); | |
800 | return (ENOMEM); | |
801 | } | |
1c79356b | 802 | bzero((caddr_t)slp, sizeof (struct nfssvc_sock)); |
91447636 A |
803 | lck_rw_init(&slp->ns_rwlock, nfs_slp_rwlock_group, nfs_slp_lock_attr); |
804 | lck_mtx_init(&slp->ns_wgmutex, nfs_slp_mutex_group, nfs_slp_lock_attr); | |
1c79356b | 805 | TAILQ_INIT(&slp->ns_uidlruhead); |
91447636 | 806 | lck_mtx_lock(nfsd_mutex); |
1c79356b A |
807 | TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain); |
808 | } | |
91447636 A |
809 | |
810 | sock_retain(so); /* grab a retain count on the socket */ | |
1c79356b | 811 | slp->ns_so = so; |
91447636 | 812 | slp->ns_sotype = sotype; |
1c79356b | 813 | slp->ns_nam = mynam; |
91447636 A |
814 | |
815 | socket_lock(so, 1); | |
1c79356b A |
816 | so->so_upcallarg = (caddr_t)slp; |
817 | so->so_upcall = nfsrv_rcv; | |
818 | so->so_rcv.sb_flags |= SB_UPCALL; /* required for freebsd merge */ | |
91447636 A |
819 | socket_unlock(so, 1); |
820 | ||
821 | slp->ns_flag = SLP_VALID | SLP_NEEDQ; | |
822 | ||
1c79356b | 823 | nfsrv_wakenfsd(slp); |
91447636 A |
824 | lck_mtx_unlock(nfsd_mutex); |
825 | ||
1c79356b A |
826 | return (0); |
827 | } | |
828 | ||
829 | /* | |
830 | * Called by nfssvc() for nfsds. Just loops around servicing rpc requests | |
831 | * until it is killed by a signal. | |
832 | */ | |
833 | static int | |
834 | nfssvc_nfsd(nsd, argp, p) | |
835 | struct nfsd_srvargs *nsd; | |
91447636 A |
836 | user_addr_t argp; |
837 | proc_t p; | |
1c79356b | 838 | { |
91447636 A |
839 | mbuf_t m, mreq; |
840 | struct nfssvc_sock *slp; | |
1c79356b A |
841 | struct nfsd *nfsd = nsd->nsd_nfsd; |
842 | struct nfsrv_descript *nd = NULL; | |
91447636 A |
843 | int error = 0, cacherep, writes_todo; |
844 | int siz, procrastinate; | |
1c79356b | 845 | u_quad_t cur_usec; |
55e303ae | 846 | struct timeval now; |
91447636 | 847 | boolean_t funnel_state; |
1c79356b A |
848 | |
849 | #ifndef nolint | |
850 | cacherep = RC_DOIT; | |
851 | writes_todo = 0; | |
852 | #endif | |
1c79356b A |
853 | if (nfsd == (struct nfsd *)0) { |
854 | MALLOC(nfsd, struct nfsd *, sizeof(struct nfsd), M_NFSD, M_WAITOK); | |
91447636 A |
855 | if (!nfsd) |
856 | return (ENOMEM); | |
1c79356b A |
857 | nsd->nsd_nfsd = nfsd; |
858 | bzero((caddr_t)nfsd, sizeof (struct nfsd)); | |
859 | nfsd->nfsd_procp = p; | |
91447636 | 860 | lck_mtx_lock(nfsd_mutex); |
1c79356b A |
861 | TAILQ_INSERT_TAIL(&nfsd_head, nfsd, nfsd_chain); |
862 | nfs_numnfsd++; | |
91447636 | 863 | lck_mtx_unlock(nfsd_mutex); |
1c79356b | 864 | } |
91447636 A |
865 | |
866 | funnel_state = thread_funnel_set(kernel_flock, FALSE); | |
867 | ||
1c79356b A |
868 | /* |
869 | * Loop getting rpc requests until SIGKILL. | |
870 | */ | |
871 | for (;;) { | |
872 | if ((nfsd->nfsd_flag & NFSD_REQINPROG) == 0) { | |
91447636 A |
873 | lck_mtx_lock(nfsd_mutex); |
874 | while ((nfsd->nfsd_slp == NULL) && !(nfsd_head_flag & NFSD_CHECKSLP)) { | |
1c79356b A |
875 | nfsd->nfsd_flag |= NFSD_WAITING; |
876 | nfsd_waiting++; | |
91447636 | 877 | error = msleep(nfsd, nfsd_mutex, PSOCK | PCATCH, "nfsd", 0); |
1c79356b | 878 | nfsd_waiting--; |
91447636 A |
879 | if (error) { |
880 | lck_mtx_unlock(nfsd_mutex); | |
1c79356b | 881 | goto done; |
91447636 | 882 | } |
1c79356b | 883 | } |
91447636 A |
884 | if ((nfsd->nfsd_slp == NULL) && (nfsd_head_flag & NFSD_CHECKSLP)) { |
885 | TAILQ_FOREACH(slp, &nfssvc_sockhead, ns_chain) { | |
886 | lck_rw_lock_shared(&slp->ns_rwlock); | |
1c79356b A |
887 | if ((slp->ns_flag & (SLP_VALID | SLP_DOREC)) |
888 | == (SLP_VALID | SLP_DOREC)) { | |
91447636 A |
889 | if (lck_rw_lock_shared_to_exclusive(&slp->ns_rwlock)) { |
890 | /* upgrade failed and we lost the lock; take exclusive and recheck */ | |
891 | lck_rw_lock_exclusive(&slp->ns_rwlock); | |
892 | if ((slp->ns_flag & (SLP_VALID | SLP_DOREC)) | |
893 | != (SLP_VALID | SLP_DOREC)) { | |
894 | /* flags no longer set, so skip this socket */ | |
895 | lck_rw_done(&slp->ns_rwlock); | |
896 | continue; | |
897 | } | |
898 | } | |
1c79356b A |
899 | slp->ns_flag &= ~SLP_DOREC; |
900 | slp->ns_sref++; | |
901 | nfsd->nfsd_slp = slp; | |
91447636 | 902 | lck_rw_done(&slp->ns_rwlock); |
1c79356b A |
903 | break; |
904 | } | |
91447636 | 905 | lck_rw_done(&slp->ns_rwlock); |
1c79356b A |
906 | } |
907 | if (slp == 0) | |
908 | nfsd_head_flag &= ~NFSD_CHECKSLP; | |
909 | } | |
91447636 A |
910 | lck_mtx_unlock(nfsd_mutex); |
911 | if ((slp = nfsd->nfsd_slp) == NULL) | |
1c79356b | 912 | continue; |
91447636 | 913 | lck_rw_lock_exclusive(&slp->ns_rwlock); |
1c79356b | 914 | if (slp->ns_flag & SLP_VALID) { |
743b1565 | 915 | if ((slp->ns_flag & (SLP_NEEDQ|SLP_DISCONN)) == SLP_NEEDQ) { |
91447636 A |
916 | slp->ns_flag &= ~SLP_NEEDQ; |
917 | nfsrv_rcv_locked(slp->ns_so, slp, MBUF_WAITOK); | |
918 | } | |
743b1565 A |
919 | if (slp->ns_flag & SLP_DISCONN) |
920 | nfsrv_zapsock(slp); | |
1c79356b | 921 | error = nfsrv_dorec(slp, nfsd, &nd); |
55e303ae A |
922 | microuptime(&now); |
923 | cur_usec = (u_quad_t)now.tv_sec * 1000000 + | |
924 | (u_quad_t)now.tv_usec; | |
91447636 | 925 | if (error && slp->ns_wgtime && (slp->ns_wgtime <= cur_usec)) { |
1c79356b A |
926 | error = 0; |
927 | cacherep = RC_DOIT; | |
928 | writes_todo = 1; | |
929 | } else | |
930 | writes_todo = 0; | |
931 | nfsd->nfsd_flag |= NFSD_REQINPROG; | |
932 | } | |
91447636 | 933 | lck_rw_done(&slp->ns_rwlock); |
1c79356b A |
934 | } else { |
935 | error = 0; | |
936 | slp = nfsd->nfsd_slp; | |
937 | } | |
938 | if (error || (slp->ns_flag & SLP_VALID) == 0) { | |
939 | if (nd) { | |
55e303ae | 940 | if (nd->nd_nam2) |
91447636 A |
941 | mbuf_freem(nd->nd_nam2); |
942 | if (nd->nd_cr) | |
943 | kauth_cred_rele(nd->nd_cr); | |
55e303ae | 944 | FREE_ZONE((caddr_t)nd, |
1c79356b A |
945 | sizeof *nd, M_NFSRVDESC); |
946 | nd = NULL; | |
947 | } | |
91447636 | 948 | nfsd->nfsd_slp = NULL; |
1c79356b A |
949 | nfsd->nfsd_flag &= ~NFSD_REQINPROG; |
950 | nfsrv_slpderef(slp); | |
951 | continue; | |
952 | } | |
1c79356b | 953 | if (nd) { |
55e303ae | 954 | microuptime(&nd->nd_starttime); |
1c79356b A |
955 | if (nd->nd_nam2) |
956 | nd->nd_nam = nd->nd_nam2; | |
957 | else | |
958 | nd->nd_nam = slp->ns_nam; | |
959 | ||
960 | /* | |
961 | * Check to see if authorization is needed. | |
962 | */ | |
963 | if (nfsd->nfsd_flag & NFSD_NEEDAUTH) { | |
964 | nfsd->nfsd_flag &= ~NFSD_NEEDAUTH; | |
91447636 | 965 | nsd->nsd_haddr = ((struct sockaddr_in *)mbuf_data(nd->nd_nam))->sin_addr.s_addr; |
1c79356b A |
966 | nsd->nsd_authlen = nfsd->nfsd_authlen; |
967 | nsd->nsd_verflen = nfsd->nfsd_verflen; | |
91447636 | 968 | if (!copyout(nfsd->nfsd_authstr,CAST_USER_ADDR_T(nsd->nsd_authstr), |
1c79356b | 969 | nfsd->nfsd_authlen) && |
91447636 | 970 | !copyout(nfsd->nfsd_verfstr, CAST_USER_ADDR_T(nsd->nsd_verfstr), |
1c79356b | 971 | nfsd->nfsd_verflen) && |
91447636 A |
972 | !copyout((caddr_t)nsd, argp, sizeof (*nsd))) { |
973 | thread_funnel_set(kernel_flock, funnel_state); | |
1c79356b | 974 | return (ENEEDAUTH); |
91447636 | 975 | } |
1c79356b A |
976 | cacherep = RC_DROPIT; |
977 | } else | |
978 | cacherep = nfsrv_getcache(nd, slp, &mreq); | |
979 | ||
91447636 | 980 | if (nfsd->nfsd_flag & NFSD_AUTHFAIL) { |
1c79356b A |
981 | nfsd->nfsd_flag &= ~NFSD_AUTHFAIL; |
982 | nd->nd_procnum = NFSPROC_NOOP; | |
983 | nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK); | |
984 | cacherep = RC_DOIT; | |
985 | } else if (nfs_privport) { | |
986 | /* Check if source port is privileged */ | |
987 | u_short port; | |
91447636 | 988 | struct sockaddr *nam = mbuf_data(nd->nd_nam); |
1c79356b A |
989 | struct sockaddr_in *sin; |
990 | ||
991 | sin = (struct sockaddr_in *)nam; | |
992 | port = ntohs(sin->sin_port); | |
993 | if (port >= IPPORT_RESERVED && | |
994 | nd->nd_procnum != NFSPROC_NULL) { | |
91447636 | 995 | char strbuf[MAX_IPv4_STR_LEN]; |
1c79356b A |
996 | nd->nd_procnum = NFSPROC_NOOP; |
997 | nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK); | |
998 | cacherep = RC_DOIT; | |
999 | printf("NFS request from unprivileged port (%s:%d)\n", | |
91447636 A |
1000 | inet_ntop(AF_INET, &sin->sin_addr, strbuf, sizeof(strbuf)), |
1001 | port); | |
1c79356b A |
1002 | } |
1003 | } | |
1004 | ||
1005 | } | |
1006 | ||
1007 | /* | |
1008 | * Loop to get all the write rpc relies that have been | |
1009 | * gathered together. | |
1010 | */ | |
1011 | do { | |
1012 | switch (cacherep) { | |
1013 | case RC_DOIT: | |
1014 | if (nd && (nd->nd_flag & ND_NFSV3)) | |
1015 | procrastinate = nfsrvw_procrastinate_v3; | |
1016 | else | |
1017 | procrastinate = nfsrvw_procrastinate; | |
91447636 A |
1018 | lck_rw_lock_shared(&nfs_export_rwlock); |
1019 | if (writes_todo || ((nd->nd_procnum == NFSPROC_WRITE) && (procrastinate > 0))) | |
1020 | error = nfsrv_writegather(&nd, slp, nfsd->nfsd_procp, &mreq); | |
1c79356b | 1021 | else |
91447636 A |
1022 | error = (*(nfsrv3_procs[nd->nd_procnum]))(nd, slp, nfsd->nfsd_procp, &mreq); |
1023 | lck_rw_done(&nfs_export_rwlock); | |
1c79356b A |
1024 | if (mreq == NULL) |
1025 | break; | |
1026 | if (error) { | |
91447636 | 1027 | OSAddAtomic(1, (SInt32*)&nfsstats.srv_errs); |
1c79356b | 1028 | nfsrv_updatecache(nd, FALSE, mreq); |
55e303ae | 1029 | if (nd->nd_nam2) { |
91447636 | 1030 | mbuf_freem(nd->nd_nam2); |
55e303ae A |
1031 | nd->nd_nam2 = NULL; |
1032 | } | |
1c79356b A |
1033 | break; |
1034 | } | |
91447636 | 1035 | OSAddAtomic(1, (SInt32*)&nfsstats.srvrpccnt[nd->nd_procnum]); |
1c79356b | 1036 | nfsrv_updatecache(nd, TRUE, mreq); |
91447636 | 1037 | nd->nd_mrep = NULL; |
1c79356b A |
1038 | case RC_REPLY: |
1039 | m = mreq; | |
1040 | siz = 0; | |
1041 | while (m) { | |
91447636 A |
1042 | siz += mbuf_len(m); |
1043 | m = mbuf_next(m); | |
1c79356b A |
1044 | } |
1045 | if (siz <= 0 || siz > NFS_MAXPACKET) { | |
1046 | printf("mbuf siz=%d\n",siz); | |
1047 | panic("Bad nfs svc reply"); | |
1048 | } | |
1049 | m = mreq; | |
91447636 A |
1050 | mbuf_pkthdr_setlen(m, siz); |
1051 | error = mbuf_pkthdr_setrcvif(m, NULL); | |
1052 | if (error) | |
1053 | panic("nfsd setrcvif failed: %d", error); | |
1c79356b A |
1054 | /* |
1055 | * For stream protocols, prepend a Sun RPC | |
1056 | * Record Mark. | |
1057 | */ | |
91447636 A |
1058 | if (slp->ns_sotype == SOCK_STREAM) { |
1059 | error = mbuf_prepend(&m, NFSX_UNSIGNED, MBUF_WAITOK); | |
1060 | if (!error) | |
1061 | *(u_long*)mbuf_data(m) = htonl(0x80000000 | siz); | |
1c79356b | 1062 | } |
91447636 A |
1063 | if (!error) { |
1064 | if (slp->ns_flag & SLP_VALID) { | |
1065 | error = nfs_send(slp->ns_so, nd->nd_nam2, m, NULL); | |
1066 | } else { | |
1067 | error = EPIPE; | |
1068 | mbuf_freem(m); | |
1069 | } | |
1070 | } else { | |
1071 | mbuf_freem(m); | |
1c79356b | 1072 | } |
55e303ae | 1073 | mreq = NULL; |
1c79356b | 1074 | if (nfsrtton) |
91447636 | 1075 | nfsd_rt(slp->ns_sotype, nd, cacherep); |
55e303ae | 1076 | if (nd->nd_nam2) { |
91447636 | 1077 | mbuf_freem(nd->nd_nam2); |
55e303ae A |
1078 | nd->nd_nam2 = NULL; |
1079 | } | |
1080 | if (nd->nd_mrep) { | |
91447636 | 1081 | mbuf_freem(nd->nd_mrep); |
55e303ae A |
1082 | nd->nd_mrep = NULL; |
1083 | } | |
91447636 A |
1084 | if (error == EPIPE) { |
1085 | lck_rw_lock_exclusive(&slp->ns_rwlock); | |
1c79356b | 1086 | nfsrv_zapsock(slp); |
91447636 A |
1087 | lck_rw_done(&slp->ns_rwlock); |
1088 | } | |
1c79356b | 1089 | if (error == EINTR || error == ERESTART) { |
91447636 A |
1090 | if (nd->nd_cr) |
1091 | kauth_cred_rele(nd->nd_cr); | |
1092 | FREE_ZONE((caddr_t)nd, sizeof *nd, M_NFSRVDESC); | |
1c79356b | 1093 | nfsrv_slpderef(slp); |
1c79356b A |
1094 | goto done; |
1095 | } | |
1096 | break; | |
1097 | case RC_DROPIT: | |
1098 | if (nfsrtton) | |
91447636 A |
1099 | nfsd_rt(slp->ns_sotype, nd, cacherep); |
1100 | mbuf_freem(nd->nd_mrep); | |
1101 | mbuf_freem(nd->nd_nam2); | |
55e303ae | 1102 | nd->nd_mrep = nd->nd_nam2 = NULL; |
1c79356b A |
1103 | break; |
1104 | }; | |
1105 | if (nd) { | |
55e303ae | 1106 | if (nd->nd_mrep) |
91447636 | 1107 | mbuf_freem(nd->nd_mrep); |
55e303ae | 1108 | if (nd->nd_nam2) |
91447636 A |
1109 | mbuf_freem(nd->nd_nam2); |
1110 | if (nd->nd_cr) | |
1111 | kauth_cred_rele(nd->nd_cr); | |
1c79356b A |
1112 | FREE_ZONE((caddr_t)nd, sizeof *nd, M_NFSRVDESC); |
1113 | nd = NULL; | |
1114 | } | |
1115 | ||
1116 | /* | |
1117 | * Check to see if there are outstanding writes that | |
1118 | * need to be serviced. | |
1119 | */ | |
55e303ae A |
1120 | microuptime(&now); |
1121 | cur_usec = (u_quad_t)now.tv_sec * 1000000 + | |
1122 | (u_quad_t)now.tv_usec; | |
91447636 | 1123 | if (slp->ns_wgtime && (slp->ns_wgtime <= cur_usec)) { |
1c79356b A |
1124 | cacherep = RC_DOIT; |
1125 | writes_todo = 1; | |
91447636 | 1126 | } else { |
1c79356b | 1127 | writes_todo = 0; |
91447636 | 1128 | } |
1c79356b | 1129 | } while (writes_todo); |
91447636 | 1130 | lck_rw_lock_exclusive(&slp->ns_rwlock); |
1c79356b | 1131 | if (nfsrv_dorec(slp, nfsd, &nd)) { |
91447636 | 1132 | lck_rw_done(&slp->ns_rwlock); |
1c79356b A |
1133 | nfsd->nfsd_flag &= ~NFSD_REQINPROG; |
1134 | nfsd->nfsd_slp = NULL; | |
1135 | nfsrv_slpderef(slp); | |
91447636 A |
1136 | } else { |
1137 | lck_rw_done(&slp->ns_rwlock); | |
1138 | } | |
1c79356b A |
1139 | } |
1140 | done: | |
91447636 A |
1141 | thread_funnel_set(kernel_flock, funnel_state); |
1142 | lck_mtx_lock(nfsd_mutex); | |
1c79356b | 1143 | TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain); |
91447636 | 1144 | FREE(nfsd, M_NFSD); |
1c79356b A |
1145 | nsd->nsd_nfsd = (struct nfsd *)0; |
1146 | if (--nfs_numnfsd == 0) | |
1147 | nfsrv_init(TRUE); /* Reinitialize everything */ | |
91447636 | 1148 | lck_mtx_unlock(nfsd_mutex); |
1c79356b A |
1149 | return (error); |
1150 | } | |
91447636 A |
1151 | |
1152 | static int | |
1153 | nfssvc_export(user_addr_t argp, proc_t p) | |
1154 | { | |
1155 | int error = 0, is_64bit; | |
1156 | struct user_nfs_export_args unxa; | |
1157 | struct vfs_context context; | |
1158 | ||
1159 | context.vc_proc = p; | |
1160 | context.vc_ucred = kauth_cred_get(); | |
1161 | is_64bit = IS_64BIT_PROCESS(p); | |
1162 | ||
1163 | /* copy in pointers to path and export args */ | |
1164 | if (is_64bit) { | |
1165 | error = copyin(argp, (caddr_t)&unxa, sizeof(unxa)); | |
1166 | } else { | |
1167 | struct nfs_export_args tnxa; | |
1168 | error = copyin(argp, (caddr_t)&tnxa, sizeof(tnxa)); | |
1169 | if (error == 0) { | |
1170 | /* munge into LP64 version of nfs_export_args structure */ | |
1171 | unxa.nxa_fsid = tnxa.nxa_fsid; | |
1172 | unxa.nxa_expid = tnxa.nxa_expid; | |
1173 | unxa.nxa_fspath = CAST_USER_ADDR_T(tnxa.nxa_fspath); | |
1174 | unxa.nxa_exppath = CAST_USER_ADDR_T(tnxa.nxa_exppath); | |
1175 | unxa.nxa_flags = tnxa.nxa_flags; | |
1176 | unxa.nxa_netcount = tnxa.nxa_netcount; | |
1177 | unxa.nxa_nets = CAST_USER_ADDR_T(tnxa.nxa_nets); | |
1178 | } | |
1179 | } | |
1180 | if (error) | |
1181 | return (error); | |
1182 | ||
1183 | error = nfsrv_export(&unxa, &context); | |
1184 | ||
1185 | return (error); | |
1186 | } | |
1187 | ||
1c79356b A |
1188 | #endif /* NFS_NOSERVER */ |
1189 | ||
1190 | int nfs_defect = 0; | |
1191 | /* XXX CSM 11/25/97 Upgrade sysctl.h someday */ | |
1192 | #ifdef notyet | |
1193 | SYSCTL_INT(_vfs_nfs, OID_AUTO, defect, CTLFLAG_RW, &nfs_defect, 0, ""); | |
1194 | #endif | |
1195 | ||
55e303ae | 1196 | int |
91447636 | 1197 | nfsclnt(proc_t p, struct nfsclnt_args *uap, __unused int *retval) |
55e303ae A |
1198 | { |
1199 | struct lockd_ans la; | |
1200 | int error; | |
1201 | ||
1202 | if (uap->flag == NFSCLNT_LOCKDWAIT) { | |
1203 | return (nfslockdwait(p)); | |
1204 | } | |
1205 | if (uap->flag == NFSCLNT_LOCKDANS) { | |
1206 | error = copyin(uap->argp, &la, sizeof(la)); | |
1207 | return (error != 0 ? error : nfslockdans(p, &la)); | |
1208 | } | |
1209 | if (uap->flag == NFSCLNT_LOCKDFD) | |
91447636 | 1210 | return (nfslockdfd(p, CAST_DOWN(int, uap->argp))); |
55e303ae A |
1211 | return EINVAL; |
1212 | } | |
1213 | ||
1214 | ||
1c79356b A |
1215 | static int nfssvc_iod_continue(int); |
1216 | ||
1217 | /* | |
1218 | * Asynchronous I/O daemons for client nfs. | |
1219 | * They do read-ahead and write-behind operations on the block I/O cache. | |
1220 | * Never returns unless it fails or gets killed. | |
1221 | */ | |
1222 | static int | |
91447636 | 1223 | nfssvc_iod(__unused proc_t p) |
1c79356b | 1224 | { |
1c79356b | 1225 | register int i, myiod; |
1c79356b A |
1226 | struct uthread *ut; |
1227 | ||
1228 | /* | |
1229 | * Assign my position or return error if too many already running | |
1230 | */ | |
1231 | myiod = -1; | |
1232 | for (i = 0; i < NFS_MAXASYNCDAEMON; i++) | |
1233 | if (nfs_asyncdaemon[i] == 0) { | |
1234 | nfs_asyncdaemon[i]++; | |
1235 | myiod = i; | |
1236 | break; | |
1237 | } | |
1238 | if (myiod == -1) | |
1239 | return (EBUSY); | |
1240 | nfs_numasync++; | |
1241 | ||
55e303ae | 1242 | /* stuff myiod into uthread to get off local stack for continuation */ |
1c79356b | 1243 | |
91447636 | 1244 | ut = (struct uthread *)get_bsdthread_info(current_thread()); |
1c79356b A |
1245 | ut->uu_state.uu_nfs_myiod = myiod; /* squirrel away for continuation */ |
1246 | ||
1247 | nfssvc_iod_continue(0); | |
1248 | /* NOTREACHED */ | |
91447636 | 1249 | return (0); |
1c79356b A |
1250 | } |
1251 | ||
1252 | /* | |
1253 | * Continuation for Asynchronous I/O daemons for client nfs. | |
1254 | */ | |
1255 | static int | |
91447636 | 1256 | nfssvc_iod_continue(int error) |
1c79356b | 1257 | { |
55e303ae | 1258 | register struct nfsbuf *bp; |
1c79356b A |
1259 | register int i, myiod; |
1260 | struct nfsmount *nmp; | |
1261 | struct uthread *ut; | |
91447636 | 1262 | proc_t p; |
1c79356b A |
1263 | |
1264 | /* | |
1265 | * real myiod is stored in uthread, recover it | |
1266 | */ | |
91447636 | 1267 | ut = (struct uthread *)get_bsdthread_info(current_thread()); |
1c79356b | 1268 | myiod = ut->uu_state.uu_nfs_myiod; |
91447636 | 1269 | p = current_proc(); // XXX |
1c79356b A |
1270 | |
1271 | /* | |
1272 | * Just loop around doin our stuff until SIGKILL | |
55e303ae | 1273 | * - actually we don't loop with continuations... |
1c79356b | 1274 | */ |
91447636 | 1275 | lck_mtx_lock(nfs_iod_mutex); |
1c79356b A |
1276 | for (;;) { |
1277 | while (((nmp = nfs_iodmount[myiod]) == NULL | |
1278 | || nmp->nm_bufq.tqh_first == NULL) | |
55e303ae | 1279 | && error == 0 && nfs_ioddelwri == 0) { |
1c79356b A |
1280 | if (nmp) |
1281 | nmp->nm_bufqiods--; | |
91447636 | 1282 | nfs_iodwant[myiod] = p; // XXX this doesn't need to be a proc_t |
1c79356b | 1283 | nfs_iodmount[myiod] = NULL; |
91447636 A |
1284 | error = msleep0((caddr_t)&nfs_iodwant[myiod], nfs_iod_mutex, |
1285 | PWAIT | PCATCH | PDROP, "nfsidl", 0, nfssvc_iod_continue); | |
1286 | lck_mtx_lock(nfs_iod_mutex); | |
1c79356b A |
1287 | } |
1288 | if (error) { | |
1289 | nfs_asyncdaemon[myiod] = 0; | |
1290 | if (nmp) nmp->nm_bufqiods--; | |
1291 | nfs_iodwant[myiod] = NULL; | |
1292 | nfs_iodmount[myiod] = NULL; | |
91447636 | 1293 | lck_mtx_unlock(nfs_iod_mutex); |
1c79356b A |
1294 | nfs_numasync--; |
1295 | if (error == EINTR || error == ERESTART) | |
1296 | error = 0; | |
1c79356b | 1297 | unix_syscall_return(error); |
1c79356b | 1298 | } |
55e303ae | 1299 | if (nmp != NULL) { |
91447636 | 1300 | while ((bp = TAILQ_FIRST(&nmp->nm_bufq)) != NULL) { |
55e303ae A |
1301 | /* Take one off the front of the list */ |
1302 | TAILQ_REMOVE(&nmp->nm_bufq, bp, nb_free); | |
1303 | bp->nb_free.tqe_next = NFSNOLIST; | |
1304 | nmp->nm_bufqlen--; | |
1305 | if (nmp->nm_bufqwant && nmp->nm_bufqlen < 2 * nfs_numasync) { | |
1306 | nmp->nm_bufqwant = FALSE; | |
91447636 | 1307 | lck_mtx_unlock(nfs_iod_mutex); |
55e303ae | 1308 | wakeup(&nmp->nm_bufq); |
91447636 A |
1309 | } else { |
1310 | lck_mtx_unlock(nfs_iod_mutex); | |
55e303ae | 1311 | } |
91447636 A |
1312 | |
1313 | SET(bp->nb_flags, NB_IOD); | |
55e303ae | 1314 | if (ISSET(bp->nb_flags, NB_READ)) |
91447636 | 1315 | nfs_doio(bp, bp->nb_rcred, NULL); |
55e303ae | 1316 | else |
91447636 | 1317 | nfs_doio(bp, bp->nb_wcred, NULL); |
1c79356b | 1318 | |
91447636 | 1319 | lck_mtx_lock(nfs_iod_mutex); |
55e303ae A |
1320 | /* |
1321 | * If there are more than one iod on this mount, then defect | |
1322 | * so that the iods can be shared out fairly between the mounts | |
1323 | */ | |
1324 | if (nfs_defect && nmp->nm_bufqiods > 1) { | |
55e303ae A |
1325 | nfs_iodmount[myiod] = NULL; |
1326 | nmp->nm_bufqiods--; | |
1327 | break; | |
1328 | } | |
1329 | } | |
1330 | } | |
91447636 A |
1331 | lck_mtx_unlock(nfs_iod_mutex); |
1332 | ||
55e303ae A |
1333 | if (nfs_ioddelwri) { |
1334 | i = 0; | |
1335 | nfs_ioddelwri = 0; | |
91447636 | 1336 | lck_mtx_lock(nfs_buf_mutex); |
55e303ae A |
1337 | while (i < 8 && (bp = TAILQ_FIRST(&nfsbufdelwri)) != NULL) { |
1338 | struct nfsnode *np = VTONFS(bp->nb_vp); | |
1339 | nfs_buf_remfree(bp); | |
91447636 A |
1340 | nfs_buf_refget(bp); |
1341 | while ((error = nfs_buf_acquire(bp, 0, 0, 0)) == EAGAIN); | |
1342 | nfs_buf_refrele(bp); | |
1343 | if (error) | |
1344 | break; | |
1345 | if (!bp->nb_vp) { | |
1346 | /* buffer is no longer valid */ | |
1347 | nfs_buf_drop(bp); | |
1348 | continue; | |
1349 | } | |
55e303ae A |
1350 | if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) { |
1351 | /* put buffer at end of delwri list */ | |
1352 | TAILQ_INSERT_TAIL(&nfsbufdelwri, bp, nb_free); | |
1353 | nfsbufdelwricnt++; | |
91447636 A |
1354 | nfs_buf_drop(bp); |
1355 | lck_mtx_unlock(nfs_buf_mutex); | |
1356 | nfs_flushcommits(np->n_vnode, NULL, 1); | |
55e303ae | 1357 | } else { |
91447636 A |
1358 | SET(bp->nb_flags, (NB_ASYNC | NB_IOD)); |
1359 | lck_mtx_unlock(nfs_buf_mutex); | |
55e303ae A |
1360 | nfs_buf_write(bp); |
1361 | } | |
1362 | i++; | |
91447636 | 1363 | lck_mtx_lock(nfs_buf_mutex); |
1c79356b | 1364 | } |
91447636 | 1365 | lck_mtx_unlock(nfs_buf_mutex); |
1c79356b | 1366 | } |
91447636 A |
1367 | |
1368 | lck_mtx_lock(nfs_iod_mutex); | |
1c79356b A |
1369 | } |
1370 | } | |
1371 | ||
1372 | /* | |
1373 | * Shut down a socket associated with an nfssvc_sock structure. | |
1374 | * Should be called with the send lock set, if required. | |
1375 | * The trick here is to increment the sref at the start, so that the nfsds | |
1376 | * will stop using it and clear ns_flag at the end so that it will not be | |
1377 | * reassigned during cleanup. | |
1378 | */ | |
1379 | static void | |
91447636 | 1380 | nfsrv_zapsock(struct nfssvc_sock *slp) |
1c79356b | 1381 | { |
91447636 | 1382 | socket_t so; |
1c79356b | 1383 | |
91447636 A |
1384 | if ((slp->ns_flag & SLP_VALID) == 0) |
1385 | return; | |
1c79356b | 1386 | slp->ns_flag &= ~SLP_ALLFLAGS; |
91447636 A |
1387 | |
1388 | so = slp->ns_so; | |
1389 | if (so == NULL) | |
1390 | return; | |
1391 | ||
743b1565 A |
1392 | /* |
1393 | * Attempt to deter future upcalls, but leave the | |
1394 | * upcall info in place to avoid a race with the | |
1395 | * networking code. | |
1396 | */ | |
91447636 | 1397 | socket_lock(so, 1); |
91447636 A |
1398 | so->so_rcv.sb_flags &= ~SB_UPCALL; |
1399 | socket_unlock(so, 1); | |
743b1565 | 1400 | |
91447636 | 1401 | sock_shutdown(so, SHUT_RDWR); |
1c79356b A |
1402 | } |
1403 | ||
1404 | /* | |
1405 | * Get an authorization string for the uid by having the mount_nfs sitting | |
1406 | * on this mount point porpous out of the kernel and do it. | |
1407 | */ | |
1408 | int | |
1409 | nfs_getauth(nmp, rep, cred, auth_str, auth_len, verf_str, verf_len, key) | |
1410 | register struct nfsmount *nmp; | |
1411 | struct nfsreq *rep; | |
91447636 | 1412 | kauth_cred_t cred; |
1c79356b A |
1413 | char **auth_str; |
1414 | int *auth_len; | |
1415 | char *verf_str; | |
1416 | int *verf_len; | |
1417 | NFSKERBKEY_T key; /* return session key */ | |
1418 | { | |
1419 | int error = 0; | |
1420 | ||
55e303ae A |
1421 | while ((nmp->nm_state & NFSSTA_WAITAUTH) == 0) { |
1422 | nmp->nm_state |= NFSSTA_WANTAUTH; | |
1c79356b A |
1423 | (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK, |
1424 | "nfsauth1", 2 * hz); | |
1425 | error = nfs_sigintr(nmp, rep, rep->r_procp); | |
1426 | if (error) { | |
55e303ae | 1427 | nmp->nm_state &= ~NFSSTA_WANTAUTH; |
1c79356b A |
1428 | return (error); |
1429 | } | |
1430 | } | |
91447636 | 1431 | nmp->nm_state &= ~NFSSTA_WANTAUTH; |
1c79356b | 1432 | MALLOC(*auth_str, char *, RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK); |
91447636 A |
1433 | if (!*auth_str) |
1434 | return (ENOMEM); | |
1c79356b A |
1435 | nmp->nm_authstr = *auth_str; |
1436 | nmp->nm_authlen = RPCAUTH_MAXSIZ; | |
1437 | nmp->nm_verfstr = verf_str; | |
1438 | nmp->nm_verflen = *verf_len; | |
91447636 A |
1439 | nmp->nm_authuid = kauth_cred_getuid(cred); |
1440 | nmp->nm_state &= ~NFSSTA_WAITAUTH; | |
1c79356b A |
1441 | wakeup((caddr_t)&nmp->nm_authstr); |
1442 | ||
1443 | /* | |
1444 | * And wait for mount_nfs to do its stuff. | |
1445 | */ | |
55e303ae | 1446 | while ((nmp->nm_state & NFSSTA_HASAUTH) == 0 && error == 0) { |
1c79356b A |
1447 | (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK, |
1448 | "nfsauth2", 2 * hz); | |
1449 | error = nfs_sigintr(nmp, rep, rep->r_procp); | |
1450 | } | |
55e303ae A |
1451 | if (nmp->nm_state & NFSSTA_AUTHERR) { |
1452 | nmp->nm_state &= ~NFSSTA_AUTHERR; | |
1c79356b A |
1453 | error = EAUTH; |
1454 | } | |
1455 | if (error) | |
91447636 | 1456 | FREE(*auth_str, M_TEMP); |
1c79356b A |
1457 | else { |
1458 | *auth_len = nmp->nm_authlen; | |
1459 | *verf_len = nmp->nm_verflen; | |
1460 | bcopy((caddr_t)nmp->nm_key, (caddr_t)key, sizeof (key)); | |
1461 | } | |
55e303ae A |
1462 | nmp->nm_state &= ~NFSSTA_HASAUTH; |
1463 | nmp->nm_state |= NFSSTA_WAITAUTH; | |
1464 | if (nmp->nm_state & NFSSTA_WANTAUTH) { | |
1465 | nmp->nm_state &= ~NFSSTA_WANTAUTH; | |
1c79356b A |
1466 | wakeup((caddr_t)&nmp->nm_authtype); |
1467 | } | |
1468 | return (error); | |
1469 | } | |
1470 | ||
1471 | /* | |
1472 | * Get a nickname authenticator and verifier. | |
1473 | */ | |
1474 | int | |
91447636 A |
1475 | nfs_getnickauth( |
1476 | struct nfsmount *nmp, | |
1477 | kauth_cred_t cred, | |
1478 | char **auth_str, | |
1479 | int *auth_len, | |
1480 | char *verf_str, | |
1481 | __unused int verf_len) | |
1c79356b A |
1482 | { |
1483 | register struct nfsuid *nuidp; | |
1484 | register u_long *nickp, *verfp; | |
55e303ae | 1485 | struct timeval ktvin, ktvout, now; |
1c79356b A |
1486 | |
1487 | #if DIAGNOSTIC | |
1488 | if (verf_len < (4 * NFSX_UNSIGNED)) | |
1489 | panic("nfs_getnickauth verf too small"); | |
1490 | #endif | |
91447636 | 1491 | for (nuidp = NMUIDHASH(nmp, kauth_cred_getuid(cred))->lh_first; |
1c79356b | 1492 | nuidp != 0; nuidp = nuidp->nu_hash.le_next) { |
91447636 | 1493 | if (kauth_cred_getuid(nuidp->nu_cr) == kauth_cred_getuid(cred)) |
1c79356b A |
1494 | break; |
1495 | } | |
55e303ae A |
1496 | microtime(&now); |
1497 | if (!nuidp || nuidp->nu_expire < now.tv_sec) | |
1c79356b A |
1498 | return (EACCES); |
1499 | ||
91447636 A |
1500 | MALLOC(nickp, u_long *, 2 * NFSX_UNSIGNED, M_TEMP, M_WAITOK); |
1501 | if (!nickp) | |
1502 | return (ENOMEM); | |
1503 | ||
1c79356b A |
1504 | /* |
1505 | * Move to the end of the lru list (end of lru == most recently used). | |
1506 | */ | |
1507 | TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru); | |
1508 | TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, nu_lru); | |
1509 | ||
1c79356b A |
1510 | *nickp++ = txdr_unsigned(RPCAKN_NICKNAME); |
1511 | *nickp = txdr_unsigned(nuidp->nu_nickname); | |
1512 | *auth_str = (char *)nickp; | |
1513 | *auth_len = 2 * NFSX_UNSIGNED; | |
1514 | ||
1515 | /* | |
1516 | * Now we must encrypt the verifier and package it up. | |
1517 | */ | |
1518 | verfp = (u_long *)verf_str; | |
1519 | *verfp++ = txdr_unsigned(RPCAKN_NICKNAME); | |
55e303ae A |
1520 | microtime(&now); |
1521 | if (now.tv_sec > nuidp->nu_timestamp.tv_sec || | |
1522 | (now.tv_sec == nuidp->nu_timestamp.tv_sec && | |
1523 | now.tv_usec > nuidp->nu_timestamp.tv_usec)) | |
1524 | nuidp->nu_timestamp = now; | |
1c79356b A |
1525 | else |
1526 | nuidp->nu_timestamp.tv_usec++; | |
1527 | ktvin.tv_sec = txdr_unsigned(nuidp->nu_timestamp.tv_sec); | |
1528 | ktvin.tv_usec = txdr_unsigned(nuidp->nu_timestamp.tv_usec); | |
1529 | ||
1530 | /* | |
1531 | * Now encrypt the timestamp verifier in ecb mode using the session | |
1532 | * key. | |
1533 | */ | |
1534 | #if NFSKERB | |
1535 | XXX | |
1536 | #endif | |
1537 | ||
1538 | *verfp++ = ktvout.tv_sec; | |
1539 | *verfp++ = ktvout.tv_usec; | |
1540 | *verfp = 0; | |
1541 | return (0); | |
1542 | } | |
1543 | ||
1544 | /* | |
1545 | * Save the current nickname in a hash list entry on the mount point. | |
1546 | */ | |
1547 | int | |
1548 | nfs_savenickauth(nmp, cred, len, key, mdp, dposp, mrep) | |
1549 | register struct nfsmount *nmp; | |
91447636 | 1550 | kauth_cred_t cred; |
1c79356b A |
1551 | int len; |
1552 | NFSKERBKEY_T key; | |
91447636 | 1553 | mbuf_t *mdp; |
1c79356b | 1554 | char **dposp; |
91447636 | 1555 | mbuf_t mrep; |
1c79356b A |
1556 | { |
1557 | register struct nfsuid *nuidp; | |
1558 | register u_long *tl; | |
1559 | register long t1; | |
91447636 | 1560 | mbuf_t md = *mdp; |
55e303ae | 1561 | struct timeval ktvin, ktvout, now; |
1c79356b A |
1562 | u_long nick; |
1563 | char *dpos = *dposp, *cp2; | |
1564 | int deltasec, error = 0; | |
1565 | ||
1566 | if (len == (3 * NFSX_UNSIGNED)) { | |
1567 | nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); | |
1568 | ktvin.tv_sec = *tl++; | |
1569 | ktvin.tv_usec = *tl++; | |
1570 | nick = fxdr_unsigned(u_long, *tl); | |
1571 | ||
1572 | /* | |
1573 | * Decrypt the timestamp in ecb mode. | |
1574 | */ | |
1575 | #if NFSKERB | |
1576 | XXX | |
1577 | #endif | |
1578 | ktvout.tv_sec = fxdr_unsigned(long, ktvout.tv_sec); | |
1579 | ktvout.tv_usec = fxdr_unsigned(long, ktvout.tv_usec); | |
55e303ae A |
1580 | microtime(&now); |
1581 | deltasec = now.tv_sec - ktvout.tv_sec; | |
1c79356b A |
1582 | if (deltasec < 0) |
1583 | deltasec = -deltasec; | |
1584 | /* | |
1585 | * If ok, add it to the hash list for the mount point. | |
1586 | */ | |
1587 | if (deltasec <= NFS_KERBCLOCKSKEW) { | |
1588 | if (nmp->nm_numuids < nuidhash_max) { | |
1589 | nmp->nm_numuids++; | |
1590 | MALLOC_ZONE(nuidp, struct nfsuid *, | |
1591 | sizeof (struct nfsuid), | |
1592 | M_NFSUID, M_WAITOK); | |
1593 | } else { | |
91447636 A |
1594 | nuidp = NULL; |
1595 | } | |
1596 | if (!nuidp) { | |
1c79356b | 1597 | nuidp = nmp->nm_uidlruhead.tqh_first; |
91447636 A |
1598 | if (!nuidp) { |
1599 | error = ENOMEM; | |
1600 | goto nfsmout; | |
1601 | } | |
1c79356b | 1602 | LIST_REMOVE(nuidp, nu_hash); |
91447636 A |
1603 | TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru); |
1604 | kauth_cred_rele(nuidp->nu_cr); | |
1c79356b A |
1605 | } |
1606 | nuidp->nu_flag = 0; | |
91447636 A |
1607 | kauth_cred_ref(cred); |
1608 | nuidp->nu_cr = cred; | |
55e303ae | 1609 | nuidp->nu_expire = now.tv_sec + NFS_KERBTTL; |
1c79356b A |
1610 | nuidp->nu_timestamp = ktvout; |
1611 | nuidp->nu_nickname = nick; | |
1612 | bcopy(key, nuidp->nu_key, sizeof (key)); | |
91447636 A |
1613 | TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, nu_lru); |
1614 | LIST_INSERT_HEAD(NMUIDHASH(nmp, kauth_cred_getuid(cred)), | |
1c79356b A |
1615 | nuidp, nu_hash); |
1616 | } | |
1617 | } else | |
1618 | nfsm_adv(nfsm_rndup(len)); | |
1619 | nfsmout: | |
1620 | *mdp = md; | |
1621 | *dposp = dpos; | |
1622 | return (error); | |
1623 | } | |
1624 | ||
1625 | #ifndef NFS_NOSERVER | |
1626 | ||
1627 | /* | |
91447636 | 1628 | * cleanup and release a server socket structure. |
1c79356b | 1629 | */ |
743b1565 | 1630 | void |
91447636 | 1631 | nfsrv_slpfree(struct nfssvc_sock *slp) |
1c79356b | 1632 | { |
91447636 A |
1633 | struct nfsuid *nuidp, *nnuidp; |
1634 | struct nfsrv_descript *nwp, *nnwp; | |
1c79356b | 1635 | |
91447636 A |
1636 | if (slp->ns_so) { |
1637 | sock_release(slp->ns_so); | |
1638 | slp->ns_so = NULL; | |
1639 | } | |
1640 | if (slp->ns_nam) | |
1641 | mbuf_free(slp->ns_nam); | |
1642 | if (slp->ns_raw) | |
1643 | mbuf_freem(slp->ns_raw); | |
1644 | if (slp->ns_rec) | |
1645 | mbuf_freem(slp->ns_rec); | |
1646 | slp->ns_nam = slp->ns_raw = slp->ns_rec = NULL; | |
1647 | ||
1648 | for (nuidp = slp->ns_uidlruhead.tqh_first; nuidp != 0; | |
1649 | nuidp = nnuidp) { | |
1650 | nnuidp = nuidp->nu_lru.tqe_next; | |
1651 | LIST_REMOVE(nuidp, nu_hash); | |
1652 | TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru); | |
1653 | if (nuidp->nu_flag & NU_NAM) | |
1654 | mbuf_freem(nuidp->nu_nam); | |
1655 | kauth_cred_rele(nuidp->nu_cr); | |
1656 | FREE_ZONE((caddr_t)nuidp, | |
1657 | sizeof (struct nfsuid), M_NFSUID); | |
1658 | } | |
55e303ae | 1659 | |
91447636 A |
1660 | for (nwp = slp->ns_tq.lh_first; nwp; nwp = nnwp) { |
1661 | nnwp = nwp->nd_tq.le_next; | |
1662 | LIST_REMOVE(nwp, nd_tq); | |
1663 | if (nwp->nd_cr) | |
1664 | kauth_cred_rele(nwp->nd_cr); | |
1665 | FREE_ZONE((caddr_t)nwp, sizeof *nwp, M_NFSRVDESC); | |
55e303ae | 1666 | } |
91447636 A |
1667 | LIST_INIT(&slp->ns_tq); |
1668 | ||
1669 | lck_rw_destroy(&slp->ns_rwlock, nfs_slp_rwlock_group); | |
1670 | lck_mtx_destroy(&slp->ns_wgmutex, nfs_slp_mutex_group); | |
1671 | FREE(slp, M_NFSSVC); | |
55e303ae A |
1672 | } |
1673 | ||
1674 | /* | |
91447636 A |
1675 | * Derefence a server socket structure. If it has no more references and |
1676 | * is no longer valid, you can throw it away. | |
55e303ae A |
1677 | */ |
1678 | void | |
91447636 | 1679 | nfsrv_slpderef(struct nfssvc_sock *slp) |
55e303ae | 1680 | { |
743b1565 A |
1681 | struct timeval now; |
1682 | ||
91447636 A |
1683 | lck_mtx_lock(nfsd_mutex); |
1684 | lck_rw_lock_exclusive(&slp->ns_rwlock); | |
1685 | slp->ns_sref--; | |
1686 | if (slp->ns_sref || (slp->ns_flag & SLP_VALID)) { | |
1687 | lck_rw_done(&slp->ns_rwlock); | |
1688 | lck_mtx_unlock(nfsd_mutex); | |
1689 | return; | |
55e303ae | 1690 | } |
91447636 | 1691 | |
743b1565 A |
1692 | /* queue the socket up for deletion */ |
1693 | microuptime(&now); | |
1694 | slp->ns_timestamp = now.tv_sec; | |
91447636 | 1695 | TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain); |
743b1565 A |
1696 | TAILQ_INSERT_TAIL(&nfssvc_deadsockhead, slp, ns_chain); |
1697 | lck_rw_done(&slp->ns_rwlock); | |
91447636 | 1698 | lck_mtx_unlock(nfsd_mutex); |
55e303ae A |
1699 | } |
1700 | ||
91447636 | 1701 | |
1c79356b A |
1702 | /* |
1703 | * Initialize the data structures for the server. | |
1704 | * Handshake with any new nfsds starting up to avoid any chance of | |
1705 | * corruption. | |
1706 | */ | |
1707 | void | |
1708 | nfsrv_init(terminating) | |
1709 | int terminating; | |
1710 | { | |
91447636 | 1711 | struct nfssvc_sock *slp, *nslp; |
743b1565 | 1712 | struct timeval now; |
1c79356b | 1713 | |
1c79356b | 1714 | if (terminating) { |
743b1565 | 1715 | microuptime(&now); |
91447636 A |
1716 | for (slp = TAILQ_FIRST(&nfssvc_sockhead); slp != 0; slp = nslp) { |
1717 | nslp = TAILQ_NEXT(slp, ns_chain); | |
1718 | if (slp->ns_flag & SLP_VALID) { | |
1719 | lck_rw_lock_exclusive(&slp->ns_rwlock); | |
1c79356b | 1720 | nfsrv_zapsock(slp); |
91447636 A |
1721 | lck_rw_done(&slp->ns_rwlock); |
1722 | } | |
743b1565 A |
1723 | /* queue the socket up for deletion */ |
1724 | slp->ns_timestamp = now.tv_sec; | |
1c79356b | 1725 | TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain); |
743b1565 | 1726 | TAILQ_INSERT_TAIL(&nfssvc_deadsockhead, slp, ns_chain); |
1c79356b A |
1727 | } |
1728 | nfsrv_cleancache(); /* And clear out server cache */ | |
91447636 A |
1729 | /* XXX Revisit when enabling WebNFS */ |
1730 | #ifdef WEBNFS_ENABLED | |
1c79356b A |
1731 | } else |
1732 | nfs_pub.np_valid = 0; | |
1733 | #else | |
1734 | } | |
1735 | #endif | |
1736 | ||
1737 | TAILQ_INIT(&nfssvc_sockhead); | |
743b1565 | 1738 | TAILQ_INIT(&nfssvc_deadsockhead); |
1c79356b A |
1739 | |
1740 | TAILQ_INIT(&nfsd_head); | |
1741 | nfsd_head_flag &= ~NFSD_CHECKSLP; | |
1742 | ||
1743 | MALLOC(nfs_udpsock, struct nfssvc_sock *, sizeof(struct nfssvc_sock), | |
1744 | M_NFSSVC, M_WAITOK); | |
91447636 A |
1745 | if (nfs_udpsock) { |
1746 | bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock)); | |
1747 | lck_rw_init(&nfs_udpsock->ns_rwlock, nfs_slp_rwlock_group, nfs_slp_lock_attr); | |
1748 | TAILQ_INIT(&nfs_udpsock->ns_uidlruhead); | |
1749 | TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain); | |
1750 | } else { | |
1751 | printf("nfsrv_init() failed to allocate UDP socket\n"); | |
1752 | } | |
1c79356b | 1753 | |
91447636 | 1754 | #if ISO |
1c79356b A |
1755 | MALLOC(nfs_cltpsock, struct nfssvc_sock *, sizeof(struct nfssvc_sock), |
1756 | M_NFSSVC, M_WAITOK); | |
91447636 A |
1757 | if (nfs_cltpsock) { |
1758 | bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock)); | |
1759 | lck_rw_init(&nfs_cltpsock->ns_rwlock, nfs_slp_rwlock_group, nfs_slp_lock_attr); | |
1760 | TAILQ_INIT(&nfs_cltpsock->ns_uidlruhead); | |
1761 | TAILQ_INSERT_TAIL(&nfssvc_sockhead, nfs_cltpsock, ns_chain); | |
1762 | } else { | |
1763 | printf("nfsrv_init() failed to allocate CLTP socket\n"); | |
1764 | } | |
1765 | #endif | |
1c79356b A |
1766 | } |
1767 | ||
1768 | /* | |
1769 | * Add entries to the server monitor log. | |
1770 | */ | |
1771 | static void | |
1772 | nfsd_rt(sotype, nd, cacherep) | |
1773 | int sotype; | |
1774 | register struct nfsrv_descript *nd; | |
1775 | int cacherep; | |
1776 | { | |
1777 | register struct drt *rt; | |
55e303ae | 1778 | struct timeval now; |
1c79356b A |
1779 | |
1780 | rt = &nfsdrt.drt[nfsdrt.pos]; | |
1781 | if (cacherep == RC_DOIT) | |
1782 | rt->flag = 0; | |
1783 | else if (cacherep == RC_REPLY) | |
1784 | rt->flag = DRT_CACHEREPLY; | |
1785 | else | |
1786 | rt->flag = DRT_CACHEDROP; | |
1787 | if (sotype == SOCK_STREAM) | |
1788 | rt->flag |= DRT_TCP; | |
1c79356b A |
1789 | else if (nd->nd_flag & ND_NFSV3) |
1790 | rt->flag |= DRT_NFSV3; | |
1791 | rt->proc = nd->nd_procnum; | |
91447636 A |
1792 | if (((struct sockaddr *)mbuf_data(nd->nd_nam))->sa_family == AF_INET) |
1793 | rt->ipadr = ((struct sockaddr_in *)mbuf_data(nd->nd_nam))->sin_addr.s_addr; | |
1c79356b A |
1794 | else |
1795 | rt->ipadr = INADDR_ANY; | |
55e303ae A |
1796 | microuptime(&now); |
1797 | rt->resptime = ((now.tv_sec - nd->nd_starttime.tv_sec) * 1000000) + | |
1798 | (now.tv_usec - nd->nd_starttime.tv_usec); | |
1799 | microtime(&rt->tstamp); // XXX unused | |
1c79356b A |
1800 | nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ; |
1801 | } | |
1802 | #endif /* NFS_NOSERVER */ |