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