1 /*
2    Unix SMB/Netbios implementation.
3 
4    Copyright (C) Ralph Boehme 2019
5    Copyright (C) Stefan Metzmacher 2019
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "system/threads.h"
24 #ifdef HAVE_UNSHARE_CLONE_FS
25 #include <sched.h>
26 #endif /* HAVE_UNSHARE_CLONE_FS */
27 
28 static bool _per_thread_cwd_checked;
29 static bool _per_thread_cwd_supported;
30 #ifdef HAVE_UNSHARE_CLONE_FS
31 static __thread bool _per_thread_cwd_disabled;
32 static __thread bool _per_thread_cwd_activated;
33 #endif /* HAVE_UNSHARE_CLONE_FS */
34 
35 /*
36  * This is the first function to be called!
37  * Typically in the main() function before
38  * any threads are created.
39  *
40  * This can be called multiple times
41  * as the result is cached the first time.
42  */
per_thread_cwd_check(void)43 void per_thread_cwd_check(void)
44 {
45 	if (_per_thread_cwd_checked) {
46 		return;
47 	}
48 
49 #ifdef HAVE_UNSHARE_CLONE_FS
50 	/*
51 	 * While unshare(CLONE_FS) is available on
52 	 * Linux for ages, unshare() is also
53 	 * used to implement containers with various
54 	 * per container namespaces.
55 	 *
56 	 * It's possible that the whole unshare()
57 	 * is blocked in order to disallow neested
58 	 * containers.
59 	 *
60 	 * That's why we sadly need a runtime check
61 	 * for this.
62 	 */
63 	{
64 		int res;
65 
66 		res = unshare(CLONE_FS);
67 		if (res == 0) {
68 			_per_thread_cwd_supported = true;
69 		}
70 	}
71 
72 	/*
73 	 * We're in the main thread, so we should disallow
74 	 * per_thread_cwd_activate() here.
75 	 */
76 	_per_thread_cwd_disabled = true;
77 #endif /* HAVE_UNSHARE_CLONE_FS */
78 
79 	_per_thread_cwd_checked = true;
80 }
81 
82 /*
83  * In order to use per_thread_cwd_supported()
84  * per_thread_cwd_check() needs to be called first!
85  * Otherwise an assert will be triggered!
86  */
per_thread_cwd_supported(void)87 bool per_thread_cwd_supported(void)
88 {
89 	SMB_ASSERT(_per_thread_cwd_checked);
90 	return _per_thread_cwd_supported;
91 }
92 
93 /*
94  * In order to use per_thread_cwd_disable()
95  * should be called after any fork() in order
96  * to mark the main thread of the process,
97  * which should disallow per_thread_cwd_activate().
98  *
99  * This can be called without calling
100  * per_thread_cwd_check() first.
101  *
102  * And it can't be called after calling
103  * per_thread_cwd_activate()!
104  * Otherwise an assert will be triggered!
105  *
106  * This can be called multiple times
107  * as the result is cached the first time.
108  */
per_thread_cwd_disable(void)109 void per_thread_cwd_disable(void)
110 {
111 #ifdef HAVE_UNSHARE_CLONE_FS
112 	SMB_ASSERT(!_per_thread_cwd_activated);
113 	if (_per_thread_cwd_disabled) {
114 		return;
115 	}
116 	_per_thread_cwd_disabled = true;
117 #endif /* HAVE_UNSHARE_CLONE_FS */
118 }
119 
120 /*
121  * In order to use per_thread_cwd_activate()
122  * per_thread_cwd_supported() needs to be checked first!
123  * Otherwise an assert will be triggered!
124  *
125  * This MUST only be called within helper threads!
126  *
127  * That means it can't be called after calling
128  * per_thread_cwd_disable()!
129  * Otherwise an assert will be triggered!
130  *
131  * This can be called multiple times
132  * as the result is cached the first time.
133  */
per_thread_cwd_activate(void)134 void per_thread_cwd_activate(void)
135 {
136 	SMB_ASSERT(_per_thread_cwd_checked);
137 	SMB_ASSERT(_per_thread_cwd_supported);
138 
139 #ifdef HAVE_UNSHARE_CLONE_FS
140 	if (_per_thread_cwd_activated) {
141 		return;
142 	}
143 
144 	SMB_ASSERT(!_per_thread_cwd_disabled);
145 
146 	{
147 		int ret;
148 		ret = unshare(CLONE_FS);
149 		SMB_ASSERT(ret == 0);
150 	}
151 
152 	_per_thread_cwd_activated = true;
153 #else /* not HAVE_UNSHARE_CLONE_FS */
154 	smb_panic(__location__);
155 #endif /* not HAVE_UNSHARE_CLONE_FS */
156 }
157