1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  *  	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
27  *	All rights reserved.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/thread.h>
33 #include <sys/t_lock.h>
34 #include <sys/time.h>
35 #include <sys/vnode.h>
36 #include <sys/vfs.h>
37 #include <sys/errno.h>
38 #include <sys/buf.h>
39 #include <sys/stat.h>
40 #include <sys/cred.h>
41 #include <sys/kmem.h>
42 #include <sys/debug.h>
43 #include <sys/vmsystm.h>
44 #include <sys/flock.h>
45 #include <sys/share.h>
46 #include <sys/cmn_err.h>
47 #include <sys/tiuser.h>
48 #include <sys/sysmacros.h>
49 #include <sys/callb.h>
50 #include <sys/acl.h>
51 #include <sys/kstat.h>
52 #include <sys/signal.h>
53 #include <sys/list.h>
54 #include <sys/zone.h>
55 
56 #include <netsmb/smb.h>
57 #include <netsmb/smb_conn.h>
58 #include <netsmb/smb_subr.h>
59 
60 #include <smbfs/smbfs.h>
61 #include <smbfs/smbfs_node.h>
62 #include <smbfs/smbfs_subr.h>
63 
64 #include <vm/hat.h>
65 #include <vm/as.h>
66 #include <vm/page.h>
67 #include <vm/pvn.h>
68 #include <vm/seg.h>
69 #include <vm/seg_map.h>
70 #include <vm/seg_vn.h>
71 
72 static int smbfs_getattr_cache(vnode_t *, struct smbfattr *);
73 static int smbfattr_to_vattr(vnode_t *, struct smbfattr *,
74 	struct vattr *);
75 
76 /*
77  * The following code provide zone support in order to perform an action
78  * for each smbfs mount in a zone.  This is also where we would add
79  * per-zone globals and kernel threads for the smbfs module (since
80  * they must be terminated by the shutdown callback).
81  */
82 
83 struct smi_globals {
84 	kmutex_t	smg_lock;  /* lock protecting smg_list */
85 	list_t		smg_list;  /* list of SMBFS mounts in zone */
86 	boolean_t	smg_destructor_called;
87 };
88 typedef struct smi_globals smi_globals_t;
89 
90 static zone_key_t smi_list_key;
91 
92 /*
93  * Attributes caching:
94  *
95  * Attributes are cached in the smbnode in struct vattr form.
96  * There is a time associated with the cached attributes (r_attrtime)
97  * which tells whether the attributes are valid. The time is initialized
98  * to the difference between current time and the modify time of the vnode
99  * when new attributes are cached. This allows the attributes for
100  * files that have changed recently to be timed out sooner than for files
101  * that have not changed for a long time. There are minimum and maximum
102  * timeout values that can be set per mount point.
103  */
104 
105 /*
106  * Validate caches by checking cached attributes. If they have timed out
107  * get the attributes from the server and compare mtimes. If mtimes are
108  * different purge all caches for this vnode.
109  */
110 int
111 smbfs_validate_caches(
112 	struct vnode *vp,
113 	cred_t *cr)
114 {
115 	struct vattr va;
116 
117 	va.va_mask = AT_SIZE;
118 	return (smbfsgetattr(vp, &va, cr));
119 }
120 
121 /*
122  * Purge all of the various data caches.
123  */
124 /*ARGSUSED*/
125 void
126 smbfs_purge_caches(struct vnode *vp)
127 {
128 #if 0	/* not yet: mmap support */
129 	/*
130 	 * NFS: Purge the DNLC for this vp,
131 	 * Clear any readdir state bits,
132 	 * the readlink response cache, ...
133 	 */
134 	smbnode_t *np = VTOSMB(vp);
135 
136 	/*
137 	 * Flush the page cache.
138 	 */
139 	if (vn_has_cached_data(vp)) {
140 		(void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL);
141 	}
142 #endif	/* not yet */
143 }
144 
145 /*
146  * Check the attribute cache to see if the new attributes match
147  * those cached.  If they do, the various `data' caches are
148  * considered to be good.  Otherwise, purge the cached data.
149  */
150 void
151 smbfs_cache_check(
152 	struct vnode *vp,
153 	struct smbfattr *fap)
154 {
155 	smbnode_t *np;
156 	int purge_data = 0;
157 #if 0	/* not yet: ACL support */
158 	int purge_acl = 0;
159 	vsecattr_t *vsp = NULL;
160 #endif	/* not yet */
161 
162 	np = VTOSMB(vp);
163 	mutex_enter(&np->r_statelock);
164 
165 	/*
166 	 * Compare with NFS macro: CACHE_VALID
167 	 * If the mtime or size has changed,
168 	 * purge cached data.
169 	 */
170 	if (np->r_attr.fa_mtime.tv_sec != fap->fa_mtime.tv_sec ||
171 	    np->r_attr.fa_mtime.tv_nsec != fap->fa_mtime.tv_nsec)
172 		purge_data = 1;
173 	if (np->r_attr.fa_size != fap->fa_size)
174 		purge_data = 1;
175 
176 #if 0	/* not yet: ACL support */
177 	if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec ||
178 	    np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec)
179 		purge_acl = 1;
180 #endif	/* not yet */
181 
182 	mutex_exit(&np->r_statelock);
183 
184 	if (purge_data)
185 		smbfs_purge_caches(vp);
186 
187 #if 0	/* not yet: ACL support */
188 	if (purge_acl) {
189 		vsecattr_t *vsp;
190 
191 		if (np->r_secattr != NULL) {
192 			mutex_enter(&np->r_statelock);
193 			vsp = np->r_secattr;
194 			np->r_secattr = NULL;
195 			mutex_exit(&np->r_statelock);
196 			if (vsp != NULL)
197 				smbfs_acl_free(vsp);
198 		}
199 	}
200 #endif	/* not yet */
201 }
202 
203 /*
204  * Set attributes cache for given vnode using vnode attributes.
205  * From NFS: nfs_attrcache_va
206  */
207 #if 0 	/* not yet (not sure if we need this) */
208 void
209 smbfs_attrcache_va(vnode_t *vp, struct vattr *vap)
210 {
211 	smbfattr_t fa;
212 	smbnode_t *np;
213 
214 	vattr_to_fattr(vp, vap, &fa);
215 	smbfs_attrcache_fa(vp, &fa);
216 }
217 #endif	/* not yet */
218 
219 /*
220  * Set attributes cache for given vnode using SMB fattr
221  * and update the attribute cache timeout.
222  *
223  * From NFS: nfs_attrcache, nfs_attrcache_va
224  */
225 void
226 smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap)
227 {
228 	smbnode_t *np;
229 	smbmntinfo_t *smi;
230 	hrtime_t delta, now;
231 	u_offset_t newsize;
232 	vtype_t	 vtype, oldvt;
233 	mode_t mode;
234 
235 	np = VTOSMB(vp);
236 	smi = VTOSMI(vp);
237 
238 	/*
239 	 * We allow v_type to change, so set that here
240 	 * (and the mode, which is derived from it).
241 	 */
242 	if (fap->fa_attr & SMB_FA_DIR) {
243 		vtype = VDIR;
244 		mode = S_IFDIR | smi->smi_dmode;
245 	} else {
246 		vtype = VREG;
247 		mode = S_IFREG | smi->smi_fmode;
248 	}
249 
250 	/*
251 	 * For now, n_uid/n_gid never change after they are
252 	 * set by: smbfs_node_findcreate / make_smbnode.
253 	 * Later, they will change in getsecattr.
254 	 */
255 
256 	mutex_enter(&np->r_statelock);
257 
258 	now = gethrtime();
259 
260 	/*
261 	 * Delta is the number of nanoseconds that we will
262 	 * cache the attributes of the file.  It is based on
263 	 * the number of nanoseconds since the last time that
264 	 * we detected a change.  The assumption is that files
265 	 * that changed recently are likely to change again.
266 	 * There is a minimum and a maximum for regular files
267 	 * and for directories which is enforced though.
268 	 *
269 	 * Using the time since last change was detected
270 	 * eliminates direct comparison or calculation
271 	 * using mixed client and server times.  SMBFS
272 	 * does not make any assumptions regarding the
273 	 * client and server clocks being synchronized.
274 	 */
275 	if (fap->fa_mtime.tv_sec  != np->r_attr.fa_mtime.tv_sec ||
276 	    fap->fa_mtime.tv_nsec != np->r_attr.fa_mtime.tv_nsec ||
277 	    fap->fa_size	  != np->r_attr.fa_size)
278 		np->r_mtime = now;
279 
280 	if ((smi->smi_flags & SMI_NOAC) || (vp->v_flag & VNOCACHE))
281 		delta = 0;
282 	else {
283 		delta = now - np->r_mtime;
284 		if (vtype == VDIR) {
285 			if (delta < smi->smi_acdirmin)
286 				delta = smi->smi_acdirmin;
287 			else if (delta > smi->smi_acdirmax)
288 				delta = smi->smi_acdirmax;
289 		} else {
290 			if (delta < smi->smi_acregmin)
291 				delta = smi->smi_acregmin;
292 			else if (delta > smi->smi_acregmax)
293 				delta = smi->smi_acregmax;
294 		}
295 	}
296 
297 	np->r_attrtime = now + delta;
298 	np->r_attr = *fap;
299 	np->n_mode = mode;
300 	oldvt = vp->v_type;
301 	vp->v_type = vtype;
302 
303 	/*
304 	 * Shall we update r_size? (local notion of size)
305 	 *
306 	 * The real criteria for updating r_size should be:
307 	 * if the file has grown on the server, or if
308 	 * the client has not modified the file.
309 	 *
310 	 * Also deal with the fact that SMB presents
311 	 * directories as having size=0.  Doing that
312 	 * here and leaving fa_size as returned OtW
313 	 * avoids fixing the size lots of places.
314 	 */
315 	newsize = fap->fa_size;
316 	if (vtype == VDIR && newsize < DEV_BSIZE)
317 		newsize = DEV_BSIZE;
318 
319 	if (np->r_size != newsize) {
320 #if 0	/* not yet: mmap support */
321 		if (!vn_has_cached_data(vp) || ...)
322 			/* XXX: See NFS page cache code. */
323 #endif	/* not yet */
324 		/* OK to set the size. */
325 		np->r_size = newsize;
326 	}
327 
328 	/* NFS: np->r_flags &= ~RWRITEATTR; */
329 	np->n_flag &= ~NATTRCHANGED;
330 
331 	mutex_exit(&np->r_statelock);
332 
333 	if (oldvt != vtype) {
334 		SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype);
335 	}
336 }
337 
338 /*
339  * Fill in attribute from the cache.
340  *
341  * If valid, copy to *fap and return zero,
342  * otherwise return an error.
343  *
344  * From NFS: nfs_getattr_cache()
345  */
346 int
347 smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap)
348 {
349 	smbnode_t *np;
350 	int error;
351 
352 	np = VTOSMB(vp);
353 
354 	mutex_enter(&np->r_statelock);
355 	if (gethrtime() >= np->r_attrtime) {
356 		/* cache expired */
357 		error = ENOENT;
358 	} else {
359 		/* cache is valid */
360 		*fap = np->r_attr;
361 		error = 0;
362 	}
363 	mutex_exit(&np->r_statelock);
364 
365 	return (error);
366 }
367 
368 /*
369  * Get attributes over-the-wire and update attributes cache
370  * if no error occurred in the over-the-wire operation.
371  * Return 0 if successful, otherwise error.
372  * From NFS: nfs_getattr_otw
373  */
374 int
375 smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr)
376 {
377 	struct smbnode *np;
378 	struct smb_cred scred;
379 	int error;
380 
381 	np = VTOSMB(vp);
382 
383 	/*
384 	 * NFS uses the ACL rpc here
385 	 * (if smi_flags & SMI_ACL)
386 	 */
387 
388 	/* Shared lock for (possible) n_fid use. */
389 	if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
390 		return (EINTR);
391 	smb_credinit(&scred, cr);
392 
393 	bzero(fap, sizeof (*fap));
394 	error = smbfs_smb_getfattr(np, fap, &scred);
395 
396 	smb_credrele(&scred);
397 	smbfs_rw_exit(&np->r_lkserlock);
398 
399 	if (error) {
400 		/* NFS had: PURGE_STALE_FH(error, vp, cr) */
401 		smbfs_attrcache_remove(np);
402 		if (error == ENOENT || error == ENOTDIR) {
403 			/*
404 			 * Getattr failed because the object was
405 			 * removed or renamed by another client.
406 			 * Remove any cached attributes under it.
407 			 */
408 			smbfs_attrcache_prune(np);
409 		}
410 		return (error);
411 	}
412 
413 	/*
414 	 * NFS: smbfs_cache_fattr(vap, fa, vap, t, cr);
415 	 * which did: fattr_to_vattr, nfs_attr_cache.
416 	 * We cache the fattr form, so just do the
417 	 * cache check and store the attributes.
418 	 */
419 	smbfs_cache_check(vp, fap);
420 	smbfs_attrcache_fa(vp, fap);
421 
422 	return (0);
423 }
424 
425 /*
426  * Return either cached or remote attributes. If get remote attr
427  * use them to check and invalidate caches, then cache the new attributes.
428  *
429  * From NFS: nfsgetattr()
430  */
431 int
432 smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
433 {
434 	struct smbfattr fa;
435 	int error;
436 
437 	ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone);
438 
439 	/*
440 	 * If we've got cached attributes, just use them;
441 	 * otherwise go to the server to get attributes,
442 	 * which will update the cache in the process.
443 	 */
444 	error = smbfs_getattr_cache(vp, &fa);
445 	if (error)
446 		error = smbfs_getattr_otw(vp, &fa, cr);
447 	if (error)
448 		return (error);
449 
450 	/*
451 	 * Re. client's view of the file size, see:
452 	 * smbfs_attrcache_fa, smbfs_getattr_otw
453 	 */
454 
455 	error = smbfattr_to_vattr(vp, &fa, vap);
456 	return (error);
457 }
458 
459 
460 /*
461  * Convert SMB over the wire attributes to vnode form.
462  * Returns 0 for success, error if failed (overflow, etc).
463  * From NFS: nattr_to_vattr()
464  */
465 int
466 smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
467 {
468 	struct smbnode *np = VTOSMB(vp);
469 
470 	vap->va_mask = AT_ALL;
471 
472 	/*
473 	 * Take type, mode, uid, gid from the smbfs node,
474 	 * which has have been updated by _getattr_otw.
475 	 */
476 	vap->va_type = vp->v_type;
477 	vap->va_mode = np->n_mode;
478 
479 	vap->va_uid = np->n_uid;
480 	vap->va_gid = np->n_gid;
481 
482 	vap->va_fsid = vp->v_vfsp->vfs_dev;
483 	vap->va_nodeid = np->n_ino;
484 	vap->va_nlink = 1;
485 
486 	/*
487 	 * Difference from NFS here:  We cache attributes as
488 	 * reported by the server, so r_attr.fa_size is the
489 	 * server's idea of the file size.  This is called
490 	 * for getattr, so we want to return the client's
491 	 * idea of the file size.  NFS deals with that in
492 	 * nfsgetattr(), the equivalent of our caller.
493 	 */
494 	vap->va_size = np->r_size;
495 
496 	/*
497 	 * Times.  Note, already converted from NT to
498 	 * Unix form (in the unmarshalling code).
499 	 */
500 	vap->va_atime = fa->fa_atime;
501 	vap->va_mtime = fa->fa_mtime;
502 	vap->va_ctime = fa->fa_ctime;
503 
504 	/*
505 	 * rdev, blksize, seq are made up.
506 	 * va_nblocks is 512 byte blocks.
507 	 */
508 	vap->va_rdev = vp->v_rdev;
509 	vap->va_blksize = MAXBSIZE;
510 	vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz);
511 	vap->va_seq = 0;
512 
513 	return (0);
514 }
515 
516 
517 /*
518  * SMB Client initialization and cleanup.
519  * Much of it is per-zone now.
520  */
521 
522 
523 /* ARGSUSED */
524 static void *
525 smbfs_zone_init(zoneid_t zoneid)
526 {
527 	smi_globals_t *smg;
528 
529 	smg = kmem_alloc(sizeof (*smg), KM_SLEEP);
530 	mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL);
531 	list_create(&smg->smg_list, sizeof (smbmntinfo_t),
532 	    offsetof(smbmntinfo_t, smi_zone_node));
533 	smg->smg_destructor_called = B_FALSE;
534 	return (smg);
535 }
536 
537 /*
538  * Callback routine to tell all SMBFS mounts in the zone to stop creating new
539  * threads.  Existing threads should exit.
540  */
541 /* ARGSUSED */
542 static void
543 smbfs_zone_shutdown(zoneid_t zoneid, void *data)
544 {
545 	smi_globals_t *smg = data;
546 	smbmntinfo_t *smi;
547 
548 	ASSERT(smg != NULL);
549 again:
550 	mutex_enter(&smg->smg_lock);
551 	for (smi = list_head(&smg->smg_list); smi != NULL;
552 	    smi = list_next(&smg->smg_list, smi)) {
553 
554 		/*
555 		 * If we've done the shutdown work for this FS, skip.
556 		 * Once we go off the end of the list, we're done.
557 		 */
558 		if (smi->smi_flags & SMI_DEAD)
559 			continue;
560 
561 		/*
562 		 * We will do work, so not done.  Get a hold on the FS.
563 		 */
564 		VFS_HOLD(smi->smi_vfsp);
565 
566 		mutex_enter(&smi->smi_lock);
567 		smi->smi_flags |= SMI_DEAD;
568 		mutex_exit(&smi->smi_lock);
569 
570 		/*
571 		 * Drop lock and release FS, which may change list, then repeat.
572 		 * We're done when every mi has been done or the list is empty.
573 		 */
574 		mutex_exit(&smg->smg_lock);
575 		VFS_RELE(smi->smi_vfsp);
576 		goto again;
577 	}
578 	mutex_exit(&smg->smg_lock);
579 }
580 
581 static void
582 smbfs_zone_free_globals(smi_globals_t *smg)
583 {
584 	list_destroy(&smg->smg_list);	/* makes sure the list is empty */
585 	mutex_destroy(&smg->smg_lock);
586 	kmem_free(smg, sizeof (*smg));
587 
588 }
589 
590 /* ARGSUSED */
591 static void
592 smbfs_zone_destroy(zoneid_t zoneid, void *data)
593 {
594 	smi_globals_t *smg = data;
595 
596 	ASSERT(smg != NULL);
597 	mutex_enter(&smg->smg_lock);
598 	if (list_head(&smg->smg_list) != NULL) {
599 		/* Still waiting for VFS_FREEVFS() */
600 		smg->smg_destructor_called = B_TRUE;
601 		mutex_exit(&smg->smg_lock);
602 		return;
603 	}
604 	smbfs_zone_free_globals(smg);
605 }
606 
607 /*
608  * Add an SMBFS mount to the per-zone list of SMBFS mounts.
609  */
610 void
611 smbfs_zonelist_add(smbmntinfo_t *smi)
612 {
613 	smi_globals_t *smg;
614 
615 	smg = zone_getspecific(smi_list_key, smi->smi_zone);
616 	mutex_enter(&smg->smg_lock);
617 	list_insert_head(&smg->smg_list, smi);
618 	mutex_exit(&smg->smg_lock);
619 }
620 
621 /*
622  * Remove an SMBFS mount from the per-zone list of SMBFS mounts.
623  */
624 void
625 smbfs_zonelist_remove(smbmntinfo_t *smi)
626 {
627 	smi_globals_t *smg;
628 
629 	smg = zone_getspecific(smi_list_key, smi->smi_zone);
630 	mutex_enter(&smg->smg_lock);
631 	list_remove(&smg->smg_list, smi);
632 	/*
633 	 * We can be called asynchronously by VFS_FREEVFS() after the zone
634 	 * shutdown/destroy callbacks have executed; if so, clean up the zone's
635 	 * smi_globals.
636 	 */
637 	if (list_head(&smg->smg_list) == NULL &&
638 	    smg->smg_destructor_called == B_TRUE) {
639 		smbfs_zone_free_globals(smg);
640 		return;
641 	}
642 	mutex_exit(&smg->smg_lock);
643 }
644 
645 #ifdef	lint
646 #define	NEED_SMBFS_CALLBACKS	1
647 #endif
648 
649 #ifdef NEED_SMBFS_CALLBACKS
650 /*
651  * Call-back hooks for netsmb, in case we want them.
652  * Apple's VFS wants them.  We may not need them.
653  */
654 /*ARGSUSED*/
655 static void smbfs_dead(smb_share_t *ssp)
656 {
657 	/*
658 	 * Walk the mount list, finding all mounts
659 	 * using this share...
660 	 */
661 }
662 
663 /*ARGSUSED*/
664 static void smbfs_cb_nop(smb_share_t *ss)
665 {
666 	/* no-op */
667 }
668 
669 smb_fscb_t smbfs_cb = {
670 	.fscb_disconn	= smbfs_dead,
671 	.fscb_connect	= smbfs_cb_nop,
672 	.fscb_down	= smbfs_cb_nop,
673 	.fscb_up	= smbfs_cb_nop };
674 
675 #endif /* NEED_SMBFS_CALLBACKS */
676 
677 /*
678  * SMBFS Client initialization routine.  This routine should only be called
679  * once.  It performs the following tasks:
680  *      - Initalize all global locks
681  *      - Call sub-initialization routines (localize access to variables)
682  */
683 int
684 smbfs_clntinit(void)
685 {
686 
687 	zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown,
688 	    smbfs_zone_destroy);
689 #ifdef NEED_SMBFS_CALLBACKS
690 	(void) smb_fscb_set(&smbfs_cb);
691 #endif /* NEED_SMBFS_CALLBACKS */
692 	return (0);
693 }
694 
695 /*
696  * This routine is called when the modunload is called. This will cleanup
697  * the previously allocated/initialized nodes.
698  */
699 void
700 smbfs_clntfini(void)
701 {
702 #ifdef NEED_SMBFS_CALLBACKS
703 	(void) smb_fscb_set(NULL);
704 #endif /* NEED_SMBFS_CALLBACKS */
705 	(void) zone_key_delete(smi_list_key);
706 }
707