summaryrefslogtreecommitdiff
path: root/source3/lib/per_thread_cwd.c
blob: fcec572a759c5176025d4319d0737b5157cb39a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
   Unix SMB/Netbios implementation.

   Copyright (C) Ralph Boehme 2019
   Copyright (C) Stefan Metzmacher 2019

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "includes.h"
#include "system/filesys.h"
#include "system/threads.h"
#ifdef HAVE_UNSHARE_CLONE_FS
#include <sched.h>
#endif /* HAVE_UNSHARE_CLONE_FS */

static bool _per_thread_cwd_checked;
static bool _per_thread_cwd_supported;
#ifdef HAVE_UNSHARE_CLONE_FS
static __thread bool _per_thread_cwd_disabled;
static __thread bool _per_thread_cwd_activated;
#endif /* HAVE_UNSHARE_CLONE_FS */

/*
 * This is the first function to be called!
 * Typically in the main() function before
 * any threads are created.
 *
 * This can be called multiple times
 * as the result is cached the first time.
 */
void per_thread_cwd_check(void)
{
	if (_per_thread_cwd_checked) {
		return;
	}

#ifdef HAVE_UNSHARE_CLONE_FS
	/*
	 * While unshare(CLONE_FS) is available on
	 * Linux for ages, unshare() is also
	 * used to implement containers with various
	 * per container namespaces.
	 *
	 * It's possible that the whole unshare()
	 * is blocked in order to disallow neested
	 * containers.
	 *
	 * That's why we sadly need a runtime check
	 * for this.
	 */
	{
		int res;

		res = unshare(CLONE_FS);
		if (res == 0) {
			_per_thread_cwd_supported = true;
		}
	}

	/*
	 * We're in the main thread, so we should disallow
	 * per_thread_cwd_activate() here.
	 */
	_per_thread_cwd_disabled = true;
#endif /* HAVE_UNSHARE_CLONE_FS */

	_per_thread_cwd_checked = true;
}

/*
 * In order to use per_thread_cwd_supported()
 * per_thread_cwd_check() needs to be called first!
 * Otherwise an assert will be triggered!
 */
bool per_thread_cwd_supported(void)
{
	SMB_ASSERT(_per_thread_cwd_checked);
	return _per_thread_cwd_supported;
}

/*
 * In order to use per_thread_cwd_disable()
 * should be called after any fork() in order
 * to mark the main thread of the process,
 * which should disallow per_thread_cwd_activate().
 *
 * This can be called without calling
 * per_thread_cwd_check() first.
 *
 * And it can't be called after calling
 * per_thread_cwd_activate()!
 * Otherwise an assert will be triggered!
 *
 * This can be called multiple times
 * as the result is cached the first time.
 */
void per_thread_cwd_disable(void)
{
#ifdef HAVE_UNSHARE_CLONE_FS
	SMB_ASSERT(!_per_thread_cwd_activated);
	if (_per_thread_cwd_disabled) {
		return;
	}
	_per_thread_cwd_disabled = true;
#endif /* HAVE_UNSHARE_CLONE_FS */
}

/*
 * In order to use per_thread_cwd_activate()
 * per_thread_cwd_supported() needs to be checked first!
 * Otherwise an assert will be triggered!
 *
 * This MUST only be called within helper threads!
 *
 * That means it can't be called after calling
 * per_thread_cwd_disable()!
 * Otherwise an assert will be triggered!
 *
 * This can be called multiple times
 * as the result is cached the first time.
 */
void per_thread_cwd_activate(void)
{
	SMB_ASSERT(_per_thread_cwd_checked);
	SMB_ASSERT(_per_thread_cwd_supported);

#ifdef HAVE_UNSHARE_CLONE_FS
	if (_per_thread_cwd_activated) {
		return;
	}

	SMB_ASSERT(!_per_thread_cwd_disabled);

	{
		int ret;
		ret = unshare(CLONE_FS);
		SMB_ASSERT(ret == 0);
	}

	_per_thread_cwd_activated = true;
#else /* not HAVE_UNSHARE_CLONE_FS */
	smb_panic(__location__);
#endif /* not HAVE_UNSHARE_CLONE_FS */
}