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