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