xref: /titanic_41/usr/src/uts/common/fs/smbsrv/smb_node.c (revision da14cebe459d3275048785f25bd869cb09b5307f)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"@(#)smb_node.c	1.9	08/08/07 SMI"
27 
28 /*
29  * SMB Node State Machine
30  * ----------------------
31  *
32  *    +----------------------------+	 T0
33  *    |  SMB_NODE_STATE_AVAILABLE  |<----------- Creation/Allocation
34  *    +----------------------------+
35  *		    |
36  *		    | T1
37  *		    |
38  *		    v
39  *    +-----------------------------+    T2
40  *    |  SMB_NODE_STATE_DESTROYING  |----------> Deletion/Free
41  *    +-----------------------------+
42  *
43  * Transition T0
44  *
45  *    This transition occurs in smb_node_lookup(). If the node looked for is
46  *    not found in the has table a new node is created. The reference count is
47  *    initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE.
48  *
49  * Transition T1
50  *
51  *    This transition occurs in smb_node_release(). If the reference count
52  *    drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more
53  *    reference count will be given out for that node.
54  *
55  * Transition T2
56  *
57  *    This transition occurs in smb_node_release(). The structure is deleted.
58  *
59  * Comments
60  * --------
61  *
62  *    The reason the smb node has 2 states is the following synchronization
63  *    rule:
64  *
65  *    There's a mutex embedded in the node used to protect its fields and
66  *    there's a lock embedded in the bucket of the hash table the node belongs
67  *    to. To increment or to decrement the reference count the mutex must be
68  *    entered. To insert the node into the bucket and to remove it from the
69  *    bucket the lock must be entered in RW_WRITER mode. When both (mutex and
70  *    lock) have to be entered, the lock has always to be entered first then
71  *    the mutex. This prevents a deadlock between smb_node_lookup() and
72  *    smb_node_release() from occurring. However, in smb_node_release() when the
73  *    reference count drops to zero and triggers the deletion of the node, the
74  *    mutex has to be released before entering the lock of the bucket (to
75  *    remove the node). This creates a window during which the node that is
76  *    about to be freed could be given out by smb_node_lookup(). To close that
77  *    window the node is moved to the state SMB_NODE_STATE_DESTROYING before
78  *    releasing the mutex. That way, even if smb_node_lookup() finds it, the
79  *    state will indicate that the node should be treated as non existent (of
80  *    course the state of the node should be tested/updated under the
81  *    protection of the mutex).
82  */
83 #include <smbsrv/smb_incl.h>
84 #include <smbsrv/smb_fsops.h>
85 #include <sys/pathname.h>
86 #include <sys/sdt.h>
87 #include <sys/nbmlock.h>
88 
89 uint32_t smb_is_executable(char *path);
90 static void smb_node_delete_on_close(smb_node_t *node);
91 
92 #define	VALIDATE_DIR_NODE(_dir_, _node_) \
93     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
94     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
95     ASSERT((_dir_)->dir_snode != (_node_));
96 
97 static boolean_t	smb_node_initialized = B_FALSE;
98 static smb_llist_t	smb_node_hash_table[SMBND_HASH_MASK+1];
99 
100 /*
101  * smb_node_init
102  *
103  * Initialization of the SMB node layer.
104  *
105  * This function is not multi-thread safe. The caller must make sure only one
106  * thread makes the call.
107  */
108 int
109 smb_node_init(void)
110 {
111 	int	i;
112 
113 	if (smb_node_initialized)
114 		return (0);
115 
116 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
117 		smb_llist_constructor(&smb_node_hash_table[i],
118 		    sizeof (smb_node_t), offsetof(smb_node_t, n_lnd));
119 	}
120 	smb_node_initialized = B_TRUE;
121 	return (0);
122 }
123 
124 /*
125  * smb_node_fini
126  *
127  * This function is not multi-thread safe. The caller must make sure only one
128  * thread makes the call.
129  */
130 void
131 smb_node_fini(void)
132 {
133 	int	i;
134 
135 	if (!smb_node_initialized)
136 		return;
137 
138 #ifdef DEBUG
139 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
140 		smb_node_t	*node;
141 
142 		/*
143 		 * The following sequence is just intended for sanity check.
144 		 * This will have to be modified when the code goes into
145 		 * production.
146 		 *
147 		 * The SMB node hash table should be emtpy at this point. If the
148 		 * hash table is not empty a panic will be triggered.
149 		 *
150 		 * The reason why SMB nodes are still remaining in the hash
151 		 * table is problably due to a mismatch between calls to
152 		 * smb_node_lookup() and smb_node_release(). You must track that
153 		 * down.
154 		 */
155 		node = smb_llist_head(&smb_node_hash_table[i]);
156 		ASSERT(node == NULL);
157 	}
158 #endif
159 
160 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
161 		smb_llist_destructor(&smb_node_hash_table[i]);
162 	}
163 	smb_node_initialized = B_FALSE;
164 }
165 
166 /*
167  * smb_node_lookup()
168  *
169  * NOTE: This routine should only be called by the file system interface layer,
170  * and not by SMB.
171  *
172  * smb_node_lookup() is called upon successful lookup, mkdir, and create
173  * (for both non-streams and streams).  In each of these cases, a held vnode is
174  * passed into this routine.  If an smb_node already exists for this vnode,
175  * the vp is released.  Otherwise, a new smb_node will be created and the
176  * reference will be held until the refcnt on the node goes to 0 (see
177  * smb_node_release()).
178  *
179  * A reference is taken on the smb_node whether found in the hash table
180  * or newly created.
181  *
182  * If an smb_node needs to be created, a reference is also taken on the
183  * dir_snode (if passed in).
184  *
185  * See smb_node_release() for details on the release of these references.
186  */
187 
188 /*ARGSUSED*/
189 smb_node_t *
190 smb_node_lookup(
191     struct smb_request	*sr,
192     struct open_param	*op,
193     cred_t		*cred,
194     vnode_t		*vp,
195     char		*od_name,
196     smb_node_t		*dir_snode,
197     smb_node_t		*unnamed_node,
198     smb_attr_t		*attr)
199 {
200 	smb_llist_t		*node_hdr;
201 	smb_node_t		*node;
202 	uint32_t		hashkey = 0;
203 	fsid_t			fsid;
204 	int			error;
205 	krw_t			lock_mode;
206 	vnode_t			*unnamed_vp = NULL;
207 
208 	/*
209 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
210 	 * because the node may not yet exist.  We also do not want to call
211 	 * it with the list lock held.
212 	 */
213 
214 	if (unnamed_node)
215 		unnamed_vp = unnamed_node->vp;
216 
217 	/*
218 	 * This getattr is performed on behalf of the server
219 	 * that's why kcred is used not the user's cred
220 	 */
221 	attr->sa_mask = SMB_AT_ALL;
222 	error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred);
223 	if (error)
224 		return (NULL);
225 
226 	if (sr && sr->tid_tree) {
227 		/*
228 		 * The fsid for a file is that of the tree, even
229 		 * if the file resides in a different mountpoint
230 		 * under the share.
231 		 */
232 		fsid = SMB_TREE_FSID(sr->tid_tree);
233 	} else {
234 		/*
235 		 * This should be getting executed only for the
236 		 * tree root smb_node.
237 		 */
238 		fsid = vp->v_vfsp->vfs_fsid;
239 	}
240 
241 	hashkey = fsid.val[0] + attr->sa_vattr.va_nodeid;
242 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
243 	node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)];
244 	lock_mode = RW_READER;
245 
246 	smb_llist_enter(node_hdr, lock_mode);
247 	for (;;) {
248 		node = list_head(&node_hdr->ll_list);
249 		while (node) {
250 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
251 			ASSERT(node->n_hash_bucket == node_hdr);
252 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
253 				smb_rwx_xenter(&node->n_lock);
254 				DTRACE_PROBE1(smb_node_lookup_hit,
255 				    smb_node_t *, node);
256 				switch (node->n_state) {
257 				case SMB_NODE_STATE_AVAILABLE:
258 					/* The node was found. */
259 					node->n_refcnt++;
260 					if ((node->dir_snode == NULL) &&
261 					    (dir_snode != NULL) &&
262 					    (strcmp(od_name, "..") != 0) &&
263 					    (strcmp(od_name, ".") != 0)) {
264 						VALIDATE_DIR_NODE(dir_snode,
265 						    node);
266 						node->dir_snode = dir_snode;
267 						smb_node_ref(dir_snode);
268 					}
269 					node->attr = *attr;
270 					node->n_size = attr->sa_vattr.va_size;
271 
272 					smb_audit_node(node);
273 					smb_rwx_xexit(&node->n_lock);
274 					smb_llist_exit(node_hdr);
275 					VN_RELE(vp);
276 					return (node);
277 
278 				case SMB_NODE_STATE_DESTROYING:
279 					/*
280 					 * Although the node exists it is about
281 					 * to be destroyed. We act as it hasn't
282 					 * been found.
283 					 */
284 					smb_rwx_xexit(&node->n_lock);
285 					break;
286 				default:
287 					/*
288 					 * Although the node exists it is in an
289 					 * unknown state. We act as it hasn't
290 					 * been found.
291 					 */
292 					ASSERT(0);
293 					smb_rwx_xexit(&node->n_lock);
294 					break;
295 				}
296 			}
297 			node = smb_llist_next(node_hdr, node);
298 		}
299 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
300 			lock_mode = RW_WRITER;
301 			continue;
302 		}
303 		break;
304 	}
305 	node = kmem_cache_alloc(sr->sr_server->si_cache_node, KM_SLEEP);
306 	bzero(node, sizeof (smb_node_t));
307 
308 	node->n_state = SMB_NODE_STATE_AVAILABLE;
309 	node->n_hash_bucket = node_hdr;
310 	node->n_sr = sr;
311 	node->vp = vp;
312 	node->n_hashkey = hashkey;
313 	node->n_refcnt = 1;
314 	node->attr = *attr;
315 	node->flags |= NODE_FLAGS_ATTR_VALID;
316 	node->n_size = node->attr.sa_vattr.va_size;
317 	node->n_orig_session_id = sr->session->s_kid;
318 	node->n_orig_uid = crgetuid(sr->user_cr);
319 	node->n_cache = sr->sr_server->si_cache_node;
320 
321 	ASSERT(od_name);
322 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
323 
324 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
325 	    offsetof(smb_ofile_t, f_nnd));
326 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
327 	    offsetof(smb_lock_t, l_lnd));
328 
329 
330 	if (strcmp(od_name, XATTR_DIR) == 0)
331 		node->flags |= NODE_XATTR_DIR;
332 	if (op)
333 		node->flags |= smb_is_executable(op->fqi.last_comp);
334 
335 	if (dir_snode) {
336 		smb_node_ref(dir_snode);
337 		node->dir_snode = dir_snode;
338 		ASSERT(dir_snode->dir_snode != node);
339 		ASSERT((dir_snode->vp->v_xattrdir) ||
340 		    (dir_snode->vp->v_type == VDIR));
341 	}
342 
343 	if (unnamed_node) {
344 		smb_node_ref(unnamed_node);
345 		node->unnamed_stream_node = unnamed_node;
346 	}
347 
348 	smb_rwx_init(&node->n_lock);
349 	node->n_magic = SMB_NODE_MAGIC;
350 	smb_audit_buf_node_create(node);
351 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
352 	smb_audit_node(node);
353 	smb_llist_insert_head(node_hdr, node);
354 
355 	smb_llist_exit(node_hdr);
356 	return (node);
357 }
358 
359 /*
360  * smb_stream_node_lookup()
361  *
362  * Note: stream_name (the name that will be stored in the "od_name" field
363  * of a stream's smb_node) is the same as the on-disk name for the stream
364  * except that it does not have SMB_STREAM_PREFIX prepended.
365  */
366 
367 smb_node_t *
368 smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode,
369     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr)
370 {
371 	smb_node_t	*xattrdir_node;
372 	smb_node_t	*snode;
373 	smb_attr_t	tmp_attr;
374 
375 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
376 	    fnode, NULL, &tmp_attr);
377 
378 	if (xattrdir_node == NULL)
379 		return (NULL);
380 
381 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
382 	    fnode, ret_attr);
383 
384 	/*
385 	 * The following VN_HOLD is necessary because the caller will VN_RELE
386 	 * xattrdirvp in the case of an error.  (xattrdir_node has the original
387 	 * hold on the vnode, which the smb_node_release() call below will
388 	 * release.)
389 	 */
390 	if (snode == NULL) {
391 		VN_HOLD(xattrdirvp);
392 	}
393 	(void) smb_node_release(xattrdir_node);
394 	return (snode);
395 }
396 
397 
398 /*
399  * This function should be called whenever a reference is needed on an
400  * smb_node pointer.  The copy of an smb_node pointer from one non-local
401  * data structure to another requires a reference to be taken on the smb_node
402  * (unless the usage is localized).  Each data structure deallocation routine
403  * will call smb_node_release() on its smb_node pointers.
404  *
405  * In general, an smb_node pointer residing in a structure should never be
406  * stale.  A node pointer may be NULL, however, and care should be taken
407  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
408  * Care also needs to be taken with respect to racing deallocations of a
409  * structure.
410  */
411 
412 void
413 smb_node_ref(smb_node_t *node)
414 {
415 	ASSERT(node);
416 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
417 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
418 
419 	smb_rwx_xenter(&node->n_lock);
420 	node->n_refcnt++;
421 	ASSERT(node->n_refcnt);
422 	DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
423 	smb_audit_node(node);
424 	smb_rwx_xexit(&node->n_lock);
425 }
426 
427 /*
428  * smb_node_lookup() takes a hold on an smb_node, whether found in the
429  * hash table or newly created.  This hold is expected to be released
430  * in the following manner.
431  *
432  * smb_node_lookup() takes an address of an smb_node pointer.  This should
433  * be getting passed down via a lookup (whether path name or component), mkdir,
434  * create.  If the original smb_node pointer resides in a data structure, then
435  * the deallocation routine for the data structure is responsible for calling
436  * smb_node_release() on the smb_node pointer.  Alternatively,
437  * smb_node_release() can be called as soon as the smb_node pointer is no longer
438  * needed.  In this case, callers are responsible for setting an embedded
439  * pointer to NULL if it is known that the last reference is being released.
440  *
441  * If the passed-in address of the smb_node pointer belongs to a local variable,
442  * then the caller with the local variable should call smb_node_release()
443  * directly.
444  *
445  * smb_node_release() itself will call smb_node_release() on a node's dir_snode,
446  * as smb_node_lookup() takes a hold on dir_snode.
447  */
448 void
449 smb_node_release(smb_node_t *node)
450 {
451 	ASSERT(node);
452 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
453 
454 	smb_rwx_xenter(&node->n_lock);
455 	ASSERT(node->n_refcnt);
456 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
457 	if (--node->n_refcnt == 0) {
458 		switch (node->n_state) {
459 
460 		case SMB_NODE_STATE_AVAILABLE:
461 			node->n_state = SMB_NODE_STATE_DESTROYING;
462 			smb_rwx_xexit(&node->n_lock);
463 
464 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
465 			smb_llist_remove(node->n_hash_bucket, node);
466 			smb_llist_exit(node->n_hash_bucket);
467 
468 			/*
469 			 * Check if the file was deleted
470 			 */
471 			smb_node_delete_on_close(node);
472 			node->n_magic = (uint32_t)~SMB_NODE_MAGIC;
473 
474 			/* These lists should be empty. */
475 			smb_llist_destructor(&node->n_ofile_list);
476 			smb_llist_destructor(&node->n_lock_list);
477 
478 			if (node->dir_snode) {
479 				ASSERT(node->dir_snode->n_magic ==
480 				    SMB_NODE_MAGIC);
481 				smb_node_release(node->dir_snode);
482 			}
483 
484 			if (node->unnamed_stream_node) {
485 				ASSERT(node->unnamed_stream_node->n_magic ==
486 				    SMB_NODE_MAGIC);
487 				smb_node_release(node->unnamed_stream_node);
488 			}
489 
490 			ASSERT(node->vp);
491 			VN_RELE(node->vp);
492 
493 			smb_audit_buf_node_destroy(node);
494 			smb_rwx_destroy(&node->n_lock);
495 			kmem_cache_free(node->n_cache, node);
496 			return;
497 
498 		default:
499 			ASSERT(0);
500 			break;
501 		}
502 	}
503 	smb_audit_node(node);
504 	smb_rwx_xexit(&node->n_lock);
505 }
506 
507 static void
508 smb_node_delete_on_close(smb_node_t *node)
509 {
510 	smb_node_t	*d_snode;
511 	int		rc = 0;
512 
513 	d_snode = node->dir_snode;
514 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
515 
516 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
517 		ASSERT(node->od_name != NULL);
518 		if (node->attr.sa_vattr.va_type == VDIR)
519 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
520 			    d_snode, node->od_name, 1);
521 		else
522 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
523 			    d_snode, node->od_name, 1);
524 		smb_cred_rele(node->delete_on_close_cred);
525 	}
526 	if (rc != 0)
527 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
528 		    node->od_name, rc);
529 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
530 }
531 
532 /*
533  * smb_node_rename()
534  *
535  */
536 int
537 smb_node_rename(
538     smb_node_t	*from_dir_snode,
539     smb_node_t	*ret_snode,
540     smb_node_t	*to_dir_snode,
541     char	*to_name)
542 {
543 	ASSERT(from_dir_snode);
544 	ASSERT(to_dir_snode);
545 	ASSERT(ret_snode);
546 	ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC);
547 	ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC);
548 	ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC);
549 	ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
550 	ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
551 	ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE);
552 
553 	smb_node_ref(to_dir_snode);
554 	smb_rwx_xenter(&ret_snode->n_lock);
555 	ret_snode->dir_snode = to_dir_snode;
556 	smb_rwx_xexit(&ret_snode->n_lock);
557 	ASSERT(to_dir_snode->dir_snode != ret_snode);
558 	ASSERT((to_dir_snode->vp->v_xattrdir) ||
559 	    (to_dir_snode->vp->v_type == VDIR));
560 	smb_node_release(from_dir_snode);
561 
562 	(void) strcpy(ret_snode->od_name, to_name);
563 
564 	/*
565 	 * XXX Need to update attributes?
566 	 */
567 
568 	return (0);
569 }
570 
571 int
572 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root)
573 {
574 	smb_attr_t	va;
575 	int		error;
576 	uint32_t	hashkey;
577 	smb_llist_t	*node_hdr;
578 	smb_node_t	*node;
579 
580 	/*
581 	 * Take an explicit hold on rootdir.  This goes with the
582 	 * corresponding release in smb_node_root_fini()/smb_node_release().
583 	 */
584 	VN_HOLD(vp);
585 
586 	va.sa_mask = SMB_AT_ALL;
587 	error = smb_vop_getattr(vp, NULL, &va, 0, kcred);
588 	if (error) {
589 		VN_RELE(vp);
590 		return (error);
591 	}
592 
593 	hashkey = vp->v_vfsp->vfs_fsid.val[0] + va.sa_vattr.va_nodeid;
594 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
595 	node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)];
596 
597 	node = kmem_cache_alloc(sv->si_cache_node, KM_SLEEP);
598 	bzero(node, sizeof (smb_node_t));
599 
600 	node->n_state = SMB_NODE_STATE_AVAILABLE;
601 	node->n_hash_bucket = node_hdr;
602 	node->vp = vp;
603 	node->n_hashkey = hashkey;
604 	node->n_refcnt = 1;
605 	node->attr = va;
606 	node->flags |= NODE_FLAGS_ATTR_VALID;
607 	node->n_size = node->attr.sa_vattr.va_size;
608 	node->n_cache = sv->si_cache_node;
609 	(void) strlcpy(node->od_name, ROOTVOL, sizeof (node->od_name));
610 
611 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
612 	    offsetof(smb_ofile_t, f_nnd));
613 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
614 	    offsetof(smb_lock_t, l_lnd));
615 
616 	smb_rwx_init(&node->n_lock);
617 	node->n_magic = SMB_NODE_MAGIC;
618 	smb_audit_buf_node_create(node);
619 
620 	sv->si_root_smb_node = node;
621 
622 	smb_audit_node(node);
623 	smb_llist_enter(node_hdr, RW_WRITER);
624 	smb_llist_insert_head(node_hdr, node);
625 	smb_llist_exit(node_hdr);
626 
627 	*root = node;
628 
629 	return (0);
630 }
631 
632 /*
633  * smb_node_get_size
634  */
635 u_offset_t
636 smb_node_get_size(smb_node_t *node, smb_attr_t *attr)
637 {
638 	u_offset_t size;
639 
640 	if (attr->sa_vattr.va_type == VDIR)
641 		return (0);
642 
643 	smb_rwx_xenter(&node->n_lock);
644 	if (node && (node->flags & NODE_FLAGS_SET_SIZE))
645 		size = node->n_size;
646 	else
647 		size = attr->sa_vattr.va_size;
648 	smb_rwx_xexit(&node->n_lock);
649 	return (size);
650 }
651 
652 static int
653 timeval_cmp(timestruc_t *a, timestruc_t *b)
654 {
655 	if (a->tv_sec < b->tv_sec)
656 		return (-1);
657 	if (a->tv_sec > b->tv_sec)
658 		return (1);
659 	/* Seconds are equal compare tv_nsec */
660 	if (a->tv_nsec < b->tv_nsec)
661 		return (-1);
662 	return (a->tv_nsec > b->tv_nsec);
663 }
664 
665 /*
666  * smb_node_set_time
667  *
668  * This function will update the time stored in the node and
669  * set the appropriate flags. If there is nothing to update,
670  * the function will return without any updates.  The update
671  * is only in the node level and the attribute in the file system
672  * will be updated when client close the file.
673  */
674 void
675 smb_node_set_time(struct smb_node *node, struct timestruc *crtime,
676     struct timestruc *mtime, struct timestruc *atime,
677     struct timestruc *ctime, unsigned int what)
678 {
679 	if (what == 0)
680 		return;
681 
682 	if ((what & SMB_AT_CRTIME && crtime == 0) ||
683 	    (what & SMB_AT_MTIME && mtime == 0) ||
684 	    (what & SMB_AT_ATIME && atime == 0) ||
685 	    (what & SMB_AT_CTIME && ctime == 0))
686 		return;
687 
688 	smb_rwx_xenter(&node->n_lock);
689 
690 	if ((what & SMB_AT_CRTIME) &&
691 	    timeval_cmp((timestruc_t *)&node->attr.sa_crtime,
692 	    crtime) != 0) {
693 		node->what |= SMB_AT_CRTIME;
694 		node->attr.sa_crtime = *((timestruc_t *)crtime);
695 	}
696 
697 	if ((what & SMB_AT_MTIME) &&
698 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime,
699 	    mtime) != 0) {
700 		node->what |= SMB_AT_MTIME;
701 		node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime);
702 	}
703 
704 	if ((what & SMB_AT_ATIME) &&
705 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime,
706 	    atime) != 0) {
707 			node->what |= SMB_AT_ATIME;
708 			node->attr.sa_vattr.va_atime = *((timestruc_t *)atime);
709 	}
710 
711 	/*
712 	 * The ctime handling is trickier. It has three scenarios.
713 	 * 1. Only ctime need to be set and it is the same as the ctime
714 	 *    stored in the node. (update not necessary)
715 	 * 2. The ctime is the same as the ctime stored in the node but
716 	 *    is not the only time need to be set. (update required)
717 	 * 3. The ctime need to be set and is not the same as the ctime
718 	 *    stored in the node. (update required)
719 	 * Unlike other time setting, the ctime needs to be set even when
720 	 * it is the same as the ctime in the node if there are other time
721 	 * needs to be set (#2). This will ensure the ctime not being
722 	 * updated when other times are being updated in the file system.
723 	 *
724 	 * Retained file rules:
725 	 *
726 	 * 1. Don't add SMB_AT_CTIME to node->what by default because the
727 	 *    request will be rejected by filesystem
728 	 * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e.
729 	 *    any request for changing ctime on these files should have
730 	 *    been already rejected
731 	 */
732 	node->what |= SMB_AT_CTIME;
733 	if (what & SMB_AT_CTIME) {
734 		if ((what == SMB_AT_CTIME) &&
735 		    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime,
736 		    ctime) == 0) {
737 			node->what &= ~SMB_AT_CTIME;
738 		} else {
739 			gethrestime(&node->attr.sa_vattr.va_ctime);
740 		}
741 	} else {
742 		gethrestime(&node->attr.sa_vattr.va_ctime);
743 	}
744 	smb_rwx_xexit(&node->n_lock);
745 }
746 
747 
748 timestruc_t *
749 smb_node_get_crtime(smb_node_t *node)
750 {
751 	return ((timestruc_t *)&node->attr.sa_crtime);
752 }
753 
754 timestruc_t *
755 smb_node_get_atime(smb_node_t *node)
756 {
757 	return ((timestruc_t *)&node->attr.sa_vattr.va_atime);
758 }
759 
760 timestruc_t *
761 smb_node_get_ctime(smb_node_t *node)
762 {
763 	return ((timestruc_t *)&node->attr.sa_vattr.va_ctime);
764 }
765 
766 timestruc_t *
767 smb_node_get_mtime(smb_node_t *node)
768 {
769 	return ((timestruc_t *)&node->attr.sa_vattr.va_mtime);
770 }
771 
772 /*
773  * smb_node_set_dosattr
774  *
775  * Parse the specified DOS attributes and, if they have been modified,
776  * update the node cache. This call should be followed by a
777  * smb_sync_fsattr() call to write the attribute changes to filesystem.
778  */
779 void
780 smb_node_set_dosattr(smb_node_t *node, uint32_t dosattr)
781 {
782 	uint32_t mode = dosattr & (FILE_ATTRIBUTE_ARCHIVE |
783 	    FILE_ATTRIBUTE_READONLY |
784 	    FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
785 
786 	smb_rwx_xenter(&node->n_lock);
787 	if (node->attr.sa_dosattr != mode) {
788 		node->attr.sa_dosattr = mode;
789 		node->what |= SMB_AT_DOSATTR;
790 	}
791 	smb_rwx_xexit(&node->n_lock);
792 }
793 
794 /*
795  * smb_node_get_dosattr()
796  *
797  * This function is used to provide clients with information as to whether
798  * the readonly bit is set.  Hence both the node attribute cache (which
799  * reflects the on-disk attributes) and node->readonly_creator (which
800  * reflects whether a readonly set is pending from a readonly create) are
801  * checked.  In the latter case, the readonly attribute should be visible to
802  * all clients even though the readonly creator fid is immune to the readonly
803  * bit until close.
804  */
805 
806 uint32_t
807 smb_node_get_dosattr(smb_node_t *node)
808 {
809 	uint32_t dosattr = node->attr.sa_dosattr;
810 
811 	if (node->readonly_creator)
812 		dosattr |= FILE_ATTRIBUTE_READONLY;
813 
814 	if (!dosattr)
815 		dosattr = FILE_ATTRIBUTE_NORMAL;
816 
817 	return (dosattr);
818 }
819 
820 int
821 smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr)
822 {
823 	int	rc = -1;
824 
825 	smb_rwx_xenter(&node->n_lock);
826 	if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) &&
827 	    !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) {
828 		crhold(cr);
829 		node->delete_on_close_cred = cr;
830 		node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
831 		rc = 0;
832 	}
833 	smb_rwx_xexit(&node->n_lock);
834 	return (rc);
835 }
836 
837 void
838 smb_node_reset_delete_on_close(smb_node_t *node)
839 {
840 	smb_rwx_xenter(&node->n_lock);
841 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
842 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
843 		crfree(node->delete_on_close_cred);
844 		node->delete_on_close_cred = NULL;
845 	}
846 	smb_rwx_xexit(&node->n_lock);
847 }
848 
849 /*
850  * smb_node_open_check
851  *
852  * check file sharing rules for current open request
853  * against all existing opens for a file.
854  *
855  * Returns NT_STATUS_SHARING_VIOLATION if there is any
856  * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
857  */
858 uint32_t
859 smb_node_open_check(struct smb_node *node, cred_t *cr,
860     uint32_t desired_access, uint32_t share_access)
861 {
862 	smb_ofile_t *of;
863 	uint32_t status;
864 
865 	ASSERT(node);
866 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
867 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
868 
869 	smb_llist_enter(&node->n_ofile_list, RW_READER);
870 	of = smb_llist_head(&node->n_ofile_list);
871 	while (of) {
872 		status = smb_ofile_open_check(of, cr, desired_access,
873 		    share_access);
874 
875 		switch (status) {
876 		case NT_STATUS_INVALID_HANDLE:
877 		case NT_STATUS_SUCCESS:
878 			of = smb_llist_next(&node->n_ofile_list, of);
879 			break;
880 		default:
881 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
882 			smb_llist_exit(&node->n_ofile_list);
883 			return (status);
884 		}
885 	}
886 
887 	smb_llist_exit(&node->n_ofile_list);
888 	return (NT_STATUS_SUCCESS);
889 }
890 
891 uint32_t
892 smb_node_rename_check(struct smb_node *node)
893 {
894 	struct smb_ofile *of;
895 	uint32_t status;
896 
897 	ASSERT(node);
898 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
899 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
900 
901 	/*
902 	 * Intra-CIFS check
903 	 */
904 
905 	smb_llist_enter(&node->n_ofile_list, RW_READER);
906 	of = smb_llist_head(&node->n_ofile_list);
907 	while (of) {
908 		status = smb_ofile_rename_check(of);
909 
910 		switch (status) {
911 		case NT_STATUS_INVALID_HANDLE:
912 		case NT_STATUS_SUCCESS:
913 			of = smb_llist_next(&node->n_ofile_list, of);
914 			break;
915 		default:
916 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
917 			smb_llist_exit(&node->n_ofile_list);
918 			return (status);
919 		}
920 	}
921 	smb_llist_exit(&node->n_ofile_list);
922 
923 	/*
924 	 * system-wide share check
925 	 */
926 
927 	if (nbl_share_conflict(node->vp, NBL_RENAME, NULL))
928 		return (NT_STATUS_SHARING_VIOLATION);
929 	else
930 		return (NT_STATUS_SUCCESS);
931 }
932 
933 uint32_t
934 smb_node_delete_check(smb_node_t *node)
935 {
936 	smb_ofile_t *of;
937 	uint32_t status;
938 
939 	ASSERT(node);
940 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
941 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
942 
943 	if (node->attr.sa_vattr.va_type == VDIR)
944 		return (NT_STATUS_SUCCESS);
945 
946 	/*
947 	 * intra-CIFS check
948 	 */
949 
950 	smb_llist_enter(&node->n_ofile_list, RW_READER);
951 	of = smb_llist_head(&node->n_ofile_list);
952 	while (of) {
953 		status = smb_ofile_delete_check(of);
954 
955 		switch (status) {
956 		case NT_STATUS_INVALID_HANDLE:
957 		case NT_STATUS_SUCCESS:
958 			of = smb_llist_next(&node->n_ofile_list, of);
959 			break;
960 		default:
961 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
962 			smb_llist_exit(&node->n_ofile_list);
963 			return (status);
964 		}
965 	}
966 	smb_llist_exit(&node->n_ofile_list);
967 
968 	/*
969 	 * system-wide share check
970 	 */
971 
972 	if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL))
973 		return (NT_STATUS_SHARING_VIOLATION);
974 	else
975 		return (NT_STATUS_SUCCESS);
976 }
977 
978 /*
979  * smb_node_start_crit()
980  *
981  * Enter critical region for share reservations.
982  * See comments above smb_fsop_shrlock().
983  */
984 
985 void
986 smb_node_start_crit(smb_node_t *node, krw_t mode)
987 {
988 	rw_enter(&node->n_share_lock, mode);
989 	nbl_start_crit(node->vp, mode);
990 }
991 
992 /*
993  * smb_node_end_crit()
994  *
995  * Exit critical region for share reservations.
996  */
997 
998 void
999 smb_node_end_crit(smb_node_t *node)
1000 {
1001 	nbl_end_crit(node->vp);
1002 	rw_exit(&node->n_share_lock);
1003 }
1004 
1005 int
1006 smb_node_in_crit(smb_node_t *node)
1007 {
1008 	return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock));
1009 }
1010