xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_odir.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * General Structures Layout
30  * -------------------------
31  *
32  * This is a simplified diagram showing the relationship between most of the
33  * main structures.
34  *
35  * +-------------------+
36  * |     SMB_INFO      |
37  * +-------------------+
38  *          |
39  *          |
40  *          v
41  * +-------------------+       +-------------------+      +-------------------+
42  * |     SESSION       |<----->|     SESSION       |......|      SESSION      |
43  * +-------------------+       +-------------------+      +-------------------+
44  *          |
45  *          |
46  *          v
47  * +-------------------+       +-------------------+      +-------------------+
48  * |       USER        |<----->|       USER        |......|       USER        |
49  * +-------------------+       +-------------------+      +-------------------+
50  *          |
51  *          |
52  *          v
53  * +-------------------+       +-------------------+      +-------------------+
54  * |       TREE        |<----->|       TREE        |......|       TREE        |
55  * +-------------------+       +-------------------+      +-------------------+
56  *      |         |
57  *      |         |
58  *      |         v
59  *      |     +-------+       +-------+      +-------+
60  *      |     | OFILE |<----->| OFILE |......| OFILE |
61  *      |     +-------+       +-------+      +-------+
62  *      |
63  *      |
64  *      v
65  *  +-------+       +------+      +------+
66  *  | ODIR  |<----->| ODIR |......| ODIR |
67  *  +-------+       +------+      +------+
68  *
69  *
70  * Odir State Machine
71  * ------------------
72  *
73  *    +-------------------------+	 T0
74  *    |  SMB_ODIR_STATE_OPEN   |<----------- Creation/Allocation
75  *    +-------------------------+
76  *		    |
77  *		    | T1
78  *		    |
79  *		    v
80  *    +-------------------------+
81  *    | SMB_ODIR_STATE_CLOSING |
82  *    +-------------------------+
83  *		    |
84  *		    | T2
85  *		    |
86  *		    v
87  *    +-------------------------+    T3
88  *    | SMB_ODIR_STATE_CLOSED  |----------> Deletion/Free
89  *    +-------------------------+
90  *
91  * SMB_ODIR_STATE_OPEN
92  *
93  *    While in this state:
94  *      - The odir is queued in the list of odirs of its tree.
95  *      - References will be given out if the odir is looked up.
96  *
97  * SMB_ODIR_STATE_CLOSING
98  *
99  *    While in this state:
100  *      - The odir is queued in the list of odirs of its tree.
101  *      - References will not be given out if the odir is looked up.
102  *      - The odir is closed.
103  *      - The resources associated with the odir remain.
104  *
105  * SMB_ODIR_STATE_CLOSED
106  *
107  *    While in this state:
108  *      - The odir is queued in the list of odirs of its tree.
109  *      - References will not be given out if the odir is looked up.
110  *      - The resources associated with the odir remain.
111  *
112  * Transition T0
113  *
114  *    This transition occurs in smb_odir_open(). A new odir is created and
115  *    added to the list of odirs of a tree.
116  *
117  * Transition T1
118  *
119  *    This transition occurs in smb_odir_close().
120  *
121  * Transition T2
122  *
123  *    This transition occurs in smb_odir_release(). The resources associated
124  *    with the odir are freed as well as the odir structure. For the
125  *    transition to occur, the odir must be in the SMB_ODIR_STATE_CLOSED
126  *    state and the reference count be zero.
127  *
128  * Comments
129  * --------
130  *
131  *    The state machine of the odir structures is controlled by 3 elements:
132  *      - The list of odirs of the tree it belongs to.
133  *      - The mutex embedded in the structure itself.
134  *      - The reference count.
135  *
136  *    There's a mutex embedded in the odir structure used to protect its fields
137  *    and there's a lock embedded in the list of odirs of a tree. To
138  *    increment or to decrement the reference count the mutex must be entered.
139  *    To insert the odir into the list of odirs of the tree and to remove
140  *    the odir from it, the lock must be entered in RW_WRITER mode.
141  *
142  *    Rules of access to a odir structure:
143  *
144  *    1) In order to avoid deadlocks, when both (mutex and lock of the odir
145  *       list) have to be entered, the lock must be entered first.
146  *
147  *    2) All actions applied to an odir require a reference count.
148  *
149  *    3) There are 2 ways of getting a reference count. One is when the odir
150  *       is opened. The other when the odir is looked up. This translates
151  *       into 2 functions: smb_odir_open() and smb_odir_lookup_by_fid().
152  *
153  *    It should be noted that the reference count of an odir registers the
154  *    number of references to the odir in other structures (such as an smb
155  *    request). The reference count is not incremented in these 2 instances:
156  *
157  *    1) The odir is open. An odir is anchored by his state. If there's
158  *       no activity involving an odir currently open, the reference count
159  *       of that odir is zero.
160  *
161  *    2) The odir is queued in the list of odirs of its tree. The fact of
162  *       being queued in that list is NOT registered by incrementing the
163  *       reference count.
164  */
165 #include <smbsrv/smb_incl.h>
166 #include <smbsrv/smb_kproto.h>
167 #include <smbsrv/smb_fsops.h>
168 
169 /* Static functions defined further down this file. */
170 static void		smb_odir_delete(smb_odir_t *of);
171 static smb_odir_t	*smb_odir_close_and_next(smb_odir_t *od);
172 
173 #include <smbsrv/smb_incl.h>
174 #include <smbsrv/smb_fsops.h>
175 
176 /*
177  * smb_odir_open
178  */
179 smb_odir_t *
180 smb_odir_open(
181     smb_tree_t		*tree,
182     smb_node_t		*node,
183     char		*pattern,
184     uint16_t		pid,
185     unsigned short	sattr)
186 {
187 	smb_odir_t	*dir;
188 
189 	ASSERT(tree);
190 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
191 	ASSERT(node);
192 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
193 	ASSERT(pattern);
194 
195 	if (strlen(pattern) >= sizeof (dir->d_pattern)) {
196 		return (NULL);
197 	}
198 
199 	dir = kmem_cache_alloc(smb_info.si_cache_odir, KM_SLEEP);
200 	bzero(dir, sizeof (smb_odir_t));
201 	dir->d_refcnt = 1;
202 	dir->d_session = tree->t_session;
203 	dir->d_user = tree->t_user;
204 	dir->d_tree = tree;
205 	(void) strlcpy(dir->d_pattern, pattern, sizeof (dir->d_pattern));
206 	dir->d_wildcards = smb_convert_unicode_wildcards(pattern);
207 	dir->d_state = SMB_ODIR_STATE_OPEN;
208 
209 	if (smb_idpool_alloc(&dir->d_tree->t_sid_pool, &dir->d_sid)) {
210 		kmem_cache_free(smb_info.si_cache_odir, dir);
211 		return (NULL);
212 	}
213 	mutex_init(&dir->d_mutex, NULL, MUTEX_DEFAULT, NULL);
214 	dir->d_sattr = sattr;
215 	dir->d_opened_by_pid = pid;
216 	dir->d_dir_snode = node;
217 	dir->d_state = SMB_ODIR_STATE_OPEN;
218 	dir->d_magic = SMB_ODIR_MAGIC;
219 
220 	smb_llist_enter(&tree->t_odir_list, RW_WRITER);
221 	smb_llist_insert_tail(&tree->t_odir_list, dir);
222 	smb_llist_exit(&tree->t_odir_list);
223 
224 	atomic_inc_32(&tree->t_session->s_dir_cnt);
225 	return (dir);
226 }
227 
228 /*
229  * smb_odir_close
230  */
231 void
232 smb_odir_close(
233     smb_odir_t		*od)
234 {
235 	ASSERT(od);
236 	ASSERT(od->d_magic == SMB_ODIR_MAGIC);
237 
238 	mutex_enter(&od->d_mutex);
239 	ASSERT(od->d_refcnt);
240 	switch (od->d_state) {
241 	case SMB_ODIR_STATE_OPEN:
242 		od->d_state = SMB_ODIR_STATE_CLOSED;
243 		break;
244 	case SMB_ODIR_STATE_CLOSING:
245 	case SMB_ODIR_STATE_CLOSED:
246 		break;
247 	default:
248 		ASSERT(0);
249 		break;
250 	}
251 	mutex_exit(&od->d_mutex);
252 }
253 
254 /*
255  * smb_odir_close_all
256  *
257  *
258  */
259 void
260 smb_odir_close_all(
261     smb_tree_t		*tree)
262 {
263 	smb_odir_t	*od;
264 
265 	ASSERT(tree);
266 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
267 
268 	smb_llist_enter(&tree->t_odir_list, RW_READER);
269 	od = smb_llist_head(&tree->t_odir_list);
270 	while (od) {
271 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
272 		ASSERT(od->d_tree == tree);
273 		od = smb_odir_close_and_next(od);
274 	}
275 	smb_llist_exit(&tree->t_odir_list);
276 }
277 
278 /*
279  * smb_odir_close_all_by_pid
280  *
281  *
282  */
283 void
284 smb_odir_close_all_by_pid(
285     smb_tree_t		*tree,
286     uint16_t		pid)
287 {
288 	smb_odir_t	*od;
289 
290 	ASSERT(tree);
291 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
292 
293 	smb_llist_enter(&tree->t_odir_list, RW_READER);
294 	od = smb_llist_head(&tree->t_odir_list);
295 	while (od) {
296 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
297 		ASSERT(od->d_tree == tree);
298 		if (od->d_opened_by_pid == pid) {
299 			od = smb_odir_close_and_next(od);
300 		} else {
301 			od = smb_llist_next(&tree->t_odir_list, od);
302 		}
303 	}
304 	smb_llist_exit(&tree->t_odir_list);
305 }
306 
307 /*
308  * smb_odir_release
309  */
310 void
311 smb_odir_release(
312     smb_odir_t	*od)
313 {
314 	ASSERT(od);
315 	ASSERT(od->d_magic == SMB_ODIR_MAGIC);
316 
317 	mutex_enter(&od->d_mutex);
318 	ASSERT(od->d_refcnt);
319 	od->d_refcnt--;
320 	switch (od->d_state) {
321 	case SMB_ODIR_STATE_CLOSING:
322 	case SMB_ODIR_STATE_OPEN:
323 		break;
324 
325 	case SMB_ODIR_STATE_CLOSED:
326 		if (od->d_refcnt == 0) {
327 			mutex_exit(&od->d_mutex);
328 			smb_odir_delete(od);
329 			return;
330 		}
331 		break;
332 
333 	default:
334 		ASSERT(0);
335 		break;
336 	}
337 	mutex_exit(&od->d_mutex);
338 }
339 
340 /*
341  * smb_odir_lookup_by_sid
342  */
343 smb_odir_t *
344 smb_odir_lookup_by_sid(
345 	smb_tree_t	*tree,
346 	uint16_t	sid)
347 {
348 	smb_llist_t	*od_list;
349 	smb_odir_t	*od;
350 
351 	ASSERT(tree);
352 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
353 
354 	od_list = &tree->t_odir_list;
355 
356 	smb_llist_enter(od_list, RW_READER);
357 	od = smb_llist_head(od_list);
358 	while (od) {
359 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
360 		ASSERT(od->d_tree == tree);
361 		if (od->d_sid == sid) {
362 			mutex_enter(&od->d_mutex);
363 			if (od->d_state != SMB_ODIR_STATE_OPEN) {
364 				mutex_exit(&od->d_mutex);
365 				smb_llist_exit(od_list);
366 				return (NULL);
367 			}
368 			od->d_refcnt++;
369 			mutex_exit(&od->d_mutex);
370 			break;
371 		}
372 		od = smb_llist_next(od_list, od);
373 	}
374 	smb_llist_exit(od_list);
375 	return (od);
376 }
377 
378 /* *************************** Static Functions ***************************** */
379 
380 /*
381  * smb_odir_close_and_next
382  *
383  * This function closes the directory passed in (if appropriate) and returns the
384  * next directory in the list of directories of the tree of the directory passed
385  * in. It requires that the list of directories of the tree be entered in
386  * RW_READER mode before being called.
387  */
388 static smb_odir_t *
389 smb_odir_close_and_next(
390     smb_odir_t		*od)
391 {
392 	smb_odir_t	*next_od;
393 	smb_tree_t	*tree;
394 
395 	ASSERT(od);
396 	ASSERT(od->d_magic == SMB_ODIR_MAGIC);
397 
398 	mutex_enter(&od->d_mutex);
399 	switch (od->d_state) {
400 	case SMB_ODIR_STATE_OPEN:
401 		/* The directory is still opened. */
402 		od->d_refcnt++;
403 		ASSERT(od->d_refcnt);
404 		tree = od->d_tree;
405 		mutex_exit(&od->d_mutex);
406 		smb_llist_exit(&od->d_tree->t_odir_list);
407 		smb_odir_close(od);
408 		smb_odir_release(od);
409 		smb_llist_enter(&tree->t_odir_list, RW_READER);
410 		next_od = smb_llist_head(&tree->t_odir_list);
411 		break;
412 	case SMB_ODIR_STATE_CLOSING:
413 	case SMB_ODIR_STATE_CLOSED:
414 		/*
415 		 * The odir exists but is closed or is in the process
416 		 * of being closed.
417 		 */
418 		mutex_exit(&od->d_mutex);
419 		next_od = smb_llist_next(&od->d_tree->t_odir_list, od);
420 		break;
421 	default:
422 		ASSERT(0);
423 		mutex_exit(&od->d_mutex);
424 		next_od = smb_llist_next(&od->d_tree->t_odir_list, od);
425 		break;
426 	}
427 	return (next_od);
428 }
429 
430 /*
431  * smb_odir_delete
432  */
433 static void
434 smb_odir_delete(
435     smb_odir_t		*od)
436 {
437 	ASSERT(od);
438 	ASSERT(od->d_magic == SMB_ODIR_MAGIC);
439 	ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED);
440 	ASSERT(od->d_refcnt == 0);
441 
442 	/*
443 	 * Let's remove the odir from the list of odirs of the tree. This has
444 	 * to be done before any resources associated with the odir are
445 	 * released.
446 	 */
447 	smb_llist_enter(&od->d_tree->t_odir_list, RW_WRITER);
448 	smb_llist_remove(&od->d_tree->t_odir_list, od);
449 	smb_llist_exit(&od->d_tree->t_odir_list);
450 
451 	smb_node_release(od->d_dir_snode);
452 	atomic_dec_32(&od->d_tree->t_session->s_dir_cnt);
453 	smb_idpool_free(&od->d_tree->t_sid_pool, od->d_sid);
454 	mutex_destroy(&od->d_mutex);
455 	kmem_cache_free(smb_info.si_cache_odir, od);
456 }
457