forked from CollaboraOnline/online
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmount.cpp
252 lines (218 loc) · 7.01 KB
/
mount.cpp
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* This is a very tiny helper to allow overlay mounting.
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <unistd.h>
#include <security.h>
#ifdef __FreeBSD__
#include <stdlib.h>
#include <sys/uio.h>
#define MOUNT mount_wrapper
#define MS_MGC_VAL 0
#define MS_SILENT 0
#define MS_NODEV 0
#define MS_UNBINDABLE 0
#define MS_BIND 1
#define MS_REC 2
#define MS_REMOUNT 4
#define MS_NOATIME 8
#define MS_NOSUID 16
#define MS_RDONLY 32
void
build_iovec(struct iovec **iov, int *iovlen, const char *name, const void *val,
size_t len)
{
int i;
if (*iovlen < 0)
return;
i = *iovlen;
*iov = reinterpret_cast<struct iovec*>(realloc(*iov, sizeof **iov * (i + 2)));
if (*iov == NULL) {
*iovlen = -1;
return;
}
(*iov)[i].iov_base = strdup(name);
(*iov)[i].iov_len = strlen(name) + 1;
i++;
(*iov)[i].iov_base = const_cast<void*>(val);
if (len == (size_t)-1) {
if (val != NULL)
len = strlen(reinterpret_cast<const char*>(val)) + 1;
else
len = 0;
}
(*iov)[i].iov_len = (int)len;
*iovlen = ++i;
}
int mount_wrapper(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data)
{
struct iovec *iov = NULL;
int iovlen = 0;
int freebsd_flags = 0;
(void)data;
if(mountflags & MS_BIND)
filesystemtype = "nullfs";
if(mountflags & MS_REMOUNT)
freebsd_flags |= MNT_UPDATE;
if(mountflags & MS_NOATIME)
freebsd_flags |= MNT_NOATIME;
if(mountflags & MS_NOSUID)
freebsd_flags |= MNT_NOSUID;
if(mountflags & MS_RDONLY)
freebsd_flags |= MNT_RDONLY;
// TODO: handle MS_REC?
build_iovec(&iov, &iovlen, "fstype", reinterpret_cast<const void*>(filesystemtype), (size_t)-1);
build_iovec(&iov, &iovlen, "fspath", reinterpret_cast<const void*>(target), (size_t)-1);
build_iovec(&iov, &iovlen, "from", reinterpret_cast<const void*>(source), (size_t)-1);
return nmount(iov, iovlen, freebsd_flags);
}
#define MNT_DETACH 1
int umount2(const char *target, int flags)
{
struct statfs* mntbufs;
int numInfos = getmntinfo(&mntbufs, MNT_WAIT);
bool targetMounted = false;
for (int i = 0; i < numInfos; i++)
{
if (!strcmp(target, mntbufs[i].f_mntonname))
{
targetMounted = true;
break;
}
}
if (!targetMounted)
{
errno = EINVAL;
return -1;
}
if(flags == MNT_DETACH)
flags = 0;
return unmount(target, flags);
}
#else
#define MOUNT mount
#endif
void usage(const char* program)
{
fprintf(stderr, "Usage: %s <-b|-r> <source path> <target path>\n", program);
fprintf(stderr, " %s -u <target>.\n", program);
fprintf(stderr, " -b bind and mount the source to target.\n");
fprintf(stderr, " -r bind and mount the source to target as readonly.\n");
fprintf(stderr, " -u to unmount the target.\n");
}
int main(int argc, char** argv)
{
if (!hasCorrectUID(/* appName = */"coolmount"))
{
fprintf(stderr, "Aborting.\n");
return EX_SOFTWARE;
}
const char* program = argv[0];
if (argc < 3)
{
usage(program);
return EX_USAGE;
}
const char* option = argv[1];
if (argc == 3 && strcmp(option, "-u") == 0) // Unmount
{
const char* target = argv[2];
struct stat sb;
const bool target_exists = (stat(target, &sb) == 0 && S_ISDIR(sb.st_mode));
// Do nothing if target doesn't exist.
if (target_exists)
{
// Unmount the target, first by detaching. This should succeed.
int retval = umount2(target, MNT_DETACH);
if (retval != 0)
{
if (errno != EINVAL)
fprintf(stderr, "%s: unmount failed to detach [%s]: %s.\n", program, target,
strerror(errno));
}
// Now try to force the unmounting, which isn't supported on all filesystems.
retval = umount2(target, MNT_FORCE);
if (retval && errno != EINVAL)
{
fprintf(stderr, "%s: forced unmount of [%s] failed: %s.\n", program, target,
strerror(errno));
return EX_SOFTWARE;
}
}
}
else if (argc == 4) // Mount
{
const char* source = argv[2];
struct stat sb;
if (stat(source, &sb) != 0 || !S_ISDIR(sb.st_mode))
{
fprintf(stderr, "%s: cannot mount from invalid source directory [%s].\n", program,
source);
return EX_USAGE;
}
const char* target = argv[3];
const bool target_exists = (stat(target, &sb) == 0 && S_ISDIR(sb.st_mode));
if (!target_exists)
{
fprintf(stderr, "%s: cannot mount on invalid target directory [%s].\n", program,
target);
return EX_USAGE;
}
// Mount the source path as the target path.
// First bind to mount an existing directory node into the chroot.
// MS_BIND ignores other flags.
if (strcmp(option, "-b") == 0) // Shared or Bind Mount.
{
const int retval
= MOUNT(source, target, nullptr, (MS_MGC_VAL | MS_BIND | MS_REC), nullptr);
if (retval)
{
fprintf(stderr, "%s: mount failed to bind [%s] to [%s]: %s.\n", program, source,
target, strerror(errno));
return EX_SOFTWARE;
}
}
else if (strcmp(option, "-r") == 0) // Readonly Mount.
{
// Now we need to set read-only and other flags with a remount.
int retval = MOUNT(source, target, nullptr,
(MS_BIND | MS_REC | MS_REMOUNT | MS_NOATIME | MS_NODEV | MS_NOSUID
| MS_RDONLY | MS_SILENT),
nullptr);
if (retval)
{
fprintf(stderr, "%s: mount failed remount [%s] readonly: %s.\n", program, target,
strerror(errno));
return EX_SOFTWARE;
}
retval = MOUNT(source, target, nullptr, (MS_UNBINDABLE | MS_REC), nullptr);
if (retval)
{
fprintf(stderr, "%s: mount failed make [%s] private: %s.\n", program, target,
strerror(errno));
return EX_SOFTWARE;
}
}
}
else
{
usage(program);
return EX_USAGE;
}
fflush(stderr);
return EX_OK;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */