]>
Commit | Line | Data |
---|---|---|
758ad80c SH |
1 | /* lxcfs |
2 | * | |
b11c6ec0 | 3 | * Copyright © 2014-2016 Canonical, Inc |
758ad80c SH |
4 | * Author: Serge Hallyn <serge.hallyn@ubuntu.com> |
5 | * | |
f2799430 | 6 | * See COPYING file for details. |
758ad80c SH |
7 | */ |
8 | ||
758ad80c SH |
9 | #define FUSE_USE_VERSION 26 |
10 | ||
4e3f18fb | 11 | #include <alloca.h> |
758ad80c | 12 | #include <dirent.h> |
1ece80b2 CB |
13 | #include <dlfcn.h> |
14 | #include <errno.h> | |
758ad80c SH |
15 | #include <fcntl.h> |
16 | #include <fuse.h> | |
758ad80c | 17 | #include <libgen.h> |
b11c6ec0 | 18 | #include <pthread.h> |
1ece80b2 CB |
19 | #include <sched.h> |
20 | #include <stdbool.h> | |
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | #include <time.h> | |
25 | #include <unistd.h> | |
26 | #include <wait.h> | |
41bb9357 | 27 | #include <linux/sched.h> |
5b2dfd85 | 28 | #include <sys/epoll.h> |
1ece80b2 CB |
29 | #include <sys/mount.h> |
30 | #include <sys/socket.h> | |
9dd49ba5 | 31 | #include <linux/limits.h> |
758ad80c | 32 | |
237e200e | 33 | #include "bindings.h" |
1ece80b2 | 34 | #include "config.h" // for VERSION |
758ad80c | 35 | |
237e200e | 36 | void *dlopen_handle; |
b11c6ec0 | 37 | |
237e200e | 38 | /* Functions to keep track of number of threads using the library */ |
6d1308cb | 39 | |
237e200e SH |
40 | static int users_count; |
41 | static pthread_mutex_t user_count_mutex = PTHREAD_MUTEX_INITIALIZER; | |
b11c6ec0 | 42 | static void lock_mutex(pthread_mutex_t *l) |
2c51f8dd | 43 | { |
b11c6ec0 | 44 | int ret; |
2c51f8dd | 45 | |
b11c6ec0 | 46 | if ((ret = pthread_mutex_lock(l)) != 0) { |
b8defc3d | 47 | lxcfs_error("returned:%d %s\n", ret, strerror(ret)); |
b11c6ec0 SH |
48 | exit(1); |
49 | } | |
50 | } | |
2c51f8dd | 51 | |
237e200e | 52 | static void unlock_mutex(pthread_mutex_t *l) |
23ce2127 | 53 | { |
237e200e | 54 | int ret; |
2dc17609 | 55 | |
237e200e | 56 | if ((ret = pthread_mutex_unlock(l)) != 0) { |
b8defc3d | 57 | lxcfs_error("returned:%d %s\n", ret, strerror(ret)); |
237e200e | 58 | exit(1); |
97f1f27b | 59 | } |
237e200e | 60 | } |
2dc17609 | 61 | |
237e200e SH |
62 | static void users_lock(void) |
63 | { | |
64 | lock_mutex(&user_count_mutex); | |
65 | } | |
b731895e | 66 | |
237e200e SH |
67 | static void users_unlock(void) |
68 | { | |
69 | unlock_mutex(&user_count_mutex); | |
70 | } | |
e1068397 | 71 | |
81d17d8f | 72 | static volatile sig_atomic_t need_reload; |
59120f04 | 73 | |
237e200e SH |
74 | /* do_reload - reload the dynamic library. Done under |
75 | * lock and when we know the user_count was 0 */ | |
76 | static void do_reload(void) | |
77 | { | |
9dd49ba5 | 78 | char lxcfs_lib_path[PATH_MAX]; |
b8defc3d CB |
79 | if (dlopen_handle) { |
80 | lxcfs_debug("%s\n", "Closing liblxcfs.so handle."); | |
237e200e | 81 | dlclose(dlopen_handle); |
b8defc3d | 82 | } |
e1068397 | 83 | |
59120f04 | 84 | /* First try loading using ld.so */ |
237e200e | 85 | dlopen_handle = dlopen("liblxcfs.so", RTLD_LAZY); |
b8defc3d CB |
86 | if (dlopen_handle) { |
87 | lxcfs_debug("%s\n", "Successfully called dlopen() on liblxcfs.so."); | |
59120f04 | 88 | goto good; |
b8defc3d | 89 | } |
59120f04 | 90 | |
9dd49ba5 DK |
91 | #ifdef LIBDIR |
92 | /* LIBDIR: autoconf will setup this MACRO. Default value is $PREFIX/lib */ | |
93 | snprintf(lxcfs_lib_path, PATH_MAX, "%s/lxcfs/liblxcfs.so", LIBDIR); | |
94 | #else | |
95 | snprintf(lxcfs_lib_path, PATH_MAX, "/usr/local/lib/lxcfs/liblxcfs.so"); | |
96 | #endif | |
97 | dlopen_handle = dlopen(lxcfs_lib_path, RTLD_LAZY); | |
237e200e | 98 | if (!dlopen_handle) { |
b8defc3d | 99 | lxcfs_error("Failed to open liblxcfs.so: %s.\n", dlerror()); |
237e200e | 100 | _exit(1); |
2dc17609 | 101 | } |
59120f04 SH |
102 | |
103 | good: | |
104 | if (need_reload) | |
b8defc3d | 105 | lxcfs_error("%s\n", "lxcfs: reloaded"); |
237e200e | 106 | need_reload = 0; |
23ce2127 SH |
107 | } |
108 | ||
237e200e | 109 | static void up_users(void) |
23ce2127 | 110 | { |
237e200e SH |
111 | users_lock(); |
112 | if (users_count == 0 && need_reload) | |
113 | do_reload(); | |
114 | users_count++; | |
115 | users_unlock(); | |
116 | } | |
23ce2127 | 117 | |
237e200e SH |
118 | static void down_users(void) |
119 | { | |
120 | users_lock(); | |
121 | users_count--; | |
122 | users_unlock(); | |
23ce2127 SH |
123 | } |
124 | ||
237e200e SH |
125 | static void reload_handler(int sig) |
126 | { | |
237e200e | 127 | need_reload = 1; |
237e200e | 128 | } |
23ce2127 | 129 | |
237e200e SH |
130 | /* Functions to run the library methods */ |
131 | static int do_cg_getattr(const char *path, struct stat *sb) | |
aeb56147 | 132 | { |
237e200e SH |
133 | int (*cg_getattr)(const char *path, struct stat *sb); |
134 | char *error; | |
135 | dlerror(); /* Clear any existing error */ | |
136 | cg_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "cg_getattr"); | |
137 | error = dlerror(); | |
138 | if (error != NULL) { | |
b8defc3d | 139 | lxcfs_error("%s\n", error); |
237e200e SH |
140 | return -1; |
141 | } | |
aeb56147 | 142 | |
237e200e | 143 | return cg_getattr(path, sb); |
aeb56147 SH |
144 | } |
145 | ||
237e200e | 146 | static int do_proc_getattr(const char *path, struct stat *sb) |
23ce2127 | 147 | { |
237e200e SH |
148 | int (*proc_getattr)(const char *path, struct stat *sb); |
149 | char *error; | |
150 | dlerror(); /* Clear any existing error */ | |
151 | proc_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "proc_getattr"); | |
152 | error = dlerror(); | |
153 | if (error != NULL) { | |
b8defc3d | 154 | lxcfs_error("%s\n", error); |
237e200e SH |
155 | return -1; |
156 | } | |
23ce2127 | 157 | |
237e200e | 158 | return proc_getattr(path, sb); |
23ce2127 SH |
159 | } |
160 | ||
237e200e | 161 | static int do_cg_read(const char *path, char *buf, size_t size, off_t offset, |
23ce2127 SH |
162 | struct fuse_file_info *fi) |
163 | { | |
237e200e SH |
164 | int (*cg_read)(const char *path, char *buf, size_t size, off_t offset, |
165 | struct fuse_file_info *fi); | |
166 | char *error; | |
23ce2127 | 167 | |
237e200e SH |
168 | dlerror(); /* Clear any existing error */ |
169 | cg_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_read"); | |
170 | error = dlerror(); | |
171 | if (error != NULL) { | |
b8defc3d | 172 | lxcfs_error("%s\n", error); |
237e200e | 173 | return -1; |
23ce2127 SH |
174 | } |
175 | ||
237e200e | 176 | return cg_read(path, buf, size, offset, fi); |
23ce2127 SH |
177 | } |
178 | ||
237e200e | 179 | static int do_proc_read(const char *path, char *buf, size_t size, off_t offset, |
23ce2127 SH |
180 | struct fuse_file_info *fi) |
181 | { | |
237e200e SH |
182 | int (*proc_read)(const char *path, char *buf, size_t size, off_t offset, |
183 | struct fuse_file_info *fi); | |
184 | char *error; | |
aeb56147 | 185 | |
237e200e SH |
186 | dlerror(); /* Clear any existing error */ |
187 | proc_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_read"); | |
188 | error = dlerror(); | |
189 | if (error != NULL) { | |
b8defc3d | 190 | lxcfs_error("%s\n", error); |
237e200e | 191 | return -1; |
97f1f27b YY |
192 | } |
193 | ||
237e200e SH |
194 | return proc_read(path, buf, size, offset, fi); |
195 | } | |
97f1f27b | 196 | |
237e200e SH |
197 | static int do_cg_write(const char *path, const char *buf, size_t size, off_t offset, |
198 | struct fuse_file_info *fi) | |
199 | { | |
200 | int (*cg_write)(const char *path, const char *buf, size_t size, off_t offset, | |
201 | struct fuse_file_info *fi); | |
202 | char *error; | |
203 | dlerror(); /* Clear any existing error */ | |
204 | cg_write = (int (*)(const char *, const char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_write"); | |
205 | error = dlerror(); | |
206 | if (error != NULL) { | |
b8defc3d | 207 | lxcfs_error("%s\n", error); |
237e200e | 208 | return -1; |
97f1f27b YY |
209 | } |
210 | ||
237e200e | 211 | return cg_write(path, buf, size, offset, fi); |
23ce2127 SH |
212 | } |
213 | ||
237e200e | 214 | static int do_cg_mkdir(const char *path, mode_t mode) |
41bb9357 | 215 | { |
237e200e SH |
216 | int (*cg_mkdir)(const char *path, mode_t mode); |
217 | char *error; | |
218 | dlerror(); /* Clear any existing error */ | |
219 | cg_mkdir = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_mkdir"); | |
220 | error = dlerror(); | |
221 | if (error != NULL) { | |
b8defc3d | 222 | lxcfs_error("%s\n", error); |
237e200e SH |
223 | return -1; |
224 | } | |
41bb9357 | 225 | |
237e200e | 226 | return cg_mkdir(path, mode); |
41bb9357 SH |
227 | } |
228 | ||
237e200e | 229 | static int do_cg_chown(const char *path, uid_t uid, gid_t gid) |
0b6af11b | 230 | { |
237e200e SH |
231 | int (*cg_chown)(const char *path, uid_t uid, gid_t gid); |
232 | char *error; | |
233 | dlerror(); /* Clear any existing error */ | |
234 | cg_chown = (int (*)(const char *, uid_t, gid_t)) dlsym(dlopen_handle, "cg_chown"); | |
235 | error = dlerror(); | |
236 | if (error != NULL) { | |
b8defc3d | 237 | lxcfs_error("%s\n", error); |
237e200e SH |
238 | return -1; |
239 | } | |
0b6af11b | 240 | |
237e200e | 241 | return cg_chown(path, uid, gid); |
41bb9357 SH |
242 | } |
243 | ||
237e200e | 244 | static int do_cg_rmdir(const char *path) |
23ce2127 | 245 | { |
237e200e SH |
246 | int (*cg_rmdir)(const char *path); |
247 | char *error; | |
248 | dlerror(); /* Clear any existing error */ | |
249 | cg_rmdir = (int (*)(const char *path)) dlsym(dlopen_handle, "cg_rmdir"); | |
250 | error = dlerror(); | |
251 | if (error != NULL) { | |
b8defc3d | 252 | lxcfs_error("%s\n", error); |
237e200e | 253 | return -1; |
97f1f27b YY |
254 | } |
255 | ||
237e200e SH |
256 | return cg_rmdir(path); |
257 | } | |
f6c0b279 | 258 | |
237e200e SH |
259 | static int do_cg_chmod(const char *path, mode_t mode) |
260 | { | |
261 | int (*cg_chmod)(const char *path, mode_t mode); | |
262 | char *error; | |
263 | dlerror(); /* Clear any existing error */ | |
264 | cg_chmod = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_chmod"); | |
265 | error = dlerror(); | |
266 | if (error != NULL) { | |
b8defc3d | 267 | lxcfs_error("%s\n", error); |
237e200e | 268 | return -1; |
e1068397 | 269 | } |
cdcdb29b | 270 | |
237e200e | 271 | return cg_chmod(path, mode); |
23ce2127 SH |
272 | } |
273 | ||
237e200e | 274 | static int do_cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, |
49878439 YY |
275 | struct fuse_file_info *fi) |
276 | { | |
237e200e SH |
277 | int (*cg_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, |
278 | struct fuse_file_info *fi); | |
279 | char *error; | |
49878439 | 280 | |
237e200e SH |
281 | dlerror(); /* Clear any existing error */ |
282 | cg_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_readdir"); | |
283 | error = dlerror(); | |
284 | if (error != NULL) { | |
b8defc3d | 285 | lxcfs_error("%s\n", error); |
237e200e | 286 | return -1; |
97f1f27b | 287 | } |
49878439 | 288 | |
237e200e SH |
289 | return cg_readdir(path, buf, filler, offset, fi); |
290 | } | |
49878439 | 291 | |
237e200e SH |
292 | static int do_proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, |
293 | struct fuse_file_info *fi) | |
294 | { | |
295 | int (*proc_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, | |
296 | struct fuse_file_info *fi); | |
297 | char *error; | |
49878439 | 298 | |
237e200e SH |
299 | dlerror(); /* Clear any existing error */ |
300 | proc_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_readdir"); | |
301 | error = dlerror(); | |
302 | if (error != NULL) { | |
b8defc3d | 303 | lxcfs_error("%s\n", error); |
237e200e | 304 | return -1; |
49878439 YY |
305 | } |
306 | ||
237e200e | 307 | return proc_readdir(path, buf, filler, offset, fi); |
49878439 YY |
308 | } |
309 | ||
237e200e | 310 | static int do_cg_open(const char *path, struct fuse_file_info *fi) |
23ce2127 | 311 | { |
237e200e SH |
312 | int (*cg_open)(const char *path, struct fuse_file_info *fi); |
313 | char *error; | |
314 | dlerror(); /* Clear any existing error */ | |
315 | cg_open = (int (*)(const char *, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_open"); | |
316 | error = dlerror(); | |
317 | if (error != NULL) { | |
b8defc3d | 318 | lxcfs_error("%s\n", error); |
237e200e SH |
319 | return -1; |
320 | } | |
23ce2127 | 321 | |
237e200e | 322 | return cg_open(path, fi); |
23ce2127 SH |
323 | } |
324 | ||
bddbb106 SH |
325 | static int do_cg_access(const char *path, int mode) |
326 | { | |
327 | int (*cg_access)(const char *path, int mode); | |
328 | char *error; | |
329 | dlerror(); /* Clear any existing error */ | |
330 | cg_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "cg_access"); | |
331 | error = dlerror(); | |
332 | if (error != NULL) { | |
b8defc3d | 333 | lxcfs_error("%s\n", error); |
bddbb106 SH |
334 | return -1; |
335 | } | |
336 | ||
337 | return cg_access(path, mode); | |
338 | } | |
339 | ||
237e200e | 340 | static int do_proc_open(const char *path, struct fuse_file_info *fi) |
758ad80c | 341 | { |
237e200e SH |
342 | int (*proc_open)(const char *path, struct fuse_file_info *fi); |
343 | char *error; | |
344 | dlerror(); /* Clear any existing error */ | |
345 | proc_open = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "proc_open"); | |
346 | error = dlerror(); | |
347 | if (error != NULL) { | |
b8defc3d | 348 | lxcfs_error("%s\n", error); |
237e200e | 349 | return -1; |
35629743 SH |
350 | } |
351 | ||
237e200e | 352 | return proc_open(path, fi); |
35629743 SH |
353 | } |
354 | ||
bddbb106 SH |
355 | static int do_proc_access(const char *path, int mode) |
356 | { | |
357 | int (*proc_access)(const char *path, int mode); | |
358 | char *error; | |
359 | dlerror(); /* Clear any existing error */ | |
360 | proc_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "proc_access"); | |
361 | error = dlerror(); | |
362 | if (error != NULL) { | |
b8defc3d | 363 | lxcfs_error("%s\n", error); |
bddbb106 SH |
364 | return -1; |
365 | } | |
366 | ||
367 | return proc_access(path, mode); | |
368 | } | |
369 | ||
237e200e | 370 | static int do_cg_release(const char *path, struct fuse_file_info *fi) |
35629743 | 371 | { |
237e200e SH |
372 | int (*cg_release)(const char *path, struct fuse_file_info *fi); |
373 | char *error; | |
374 | dlerror(); /* Clear any existing error */ | |
375 | cg_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_release"); | |
376 | error = dlerror(); | |
377 | if (error != NULL) { | |
b8defc3d | 378 | lxcfs_error("%s\n", error); |
237e200e SH |
379 | return -1; |
380 | } | |
381 | ||
382 | return cg_release(path, fi); | |
758ad80c SH |
383 | } |
384 | ||
237e200e | 385 | static int do_proc_release(const char *path, struct fuse_file_info *fi) |
35629743 | 386 | { |
237e200e SH |
387 | int (*proc_release)(const char *path, struct fuse_file_info *fi); |
388 | char *error; | |
389 | dlerror(); /* Clear any existing error */ | |
390 | proc_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_release"); | |
391 | error = dlerror(); | |
392 | if (error != NULL) { | |
b8defc3d | 393 | lxcfs_error("%s\n", error); |
237e200e SH |
394 | return -1; |
395 | } | |
97f1f27b | 396 | |
237e200e | 397 | return proc_release(path, fi); |
96fc5ee6 SH |
398 | } |
399 | ||
237e200e | 400 | static int do_cg_opendir(const char *path, struct fuse_file_info *fi) |
96fc5ee6 | 401 | { |
237e200e SH |
402 | int (*cg_opendir)(const char *path, struct fuse_file_info *fi); |
403 | char *error; | |
404 | dlerror(); /* Clear any existing error */ | |
405 | cg_opendir = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "cg_opendir"); | |
406 | error = dlerror(); | |
407 | if (error != NULL) { | |
b8defc3d | 408 | lxcfs_error("%s\n", error); |
237e200e SH |
409 | return -1; |
410 | } | |
96fc5ee6 | 411 | |
237e200e | 412 | return cg_opendir(path, fi); |
35629743 SH |
413 | } |
414 | ||
237e200e | 415 | static int do_cg_releasedir(const char *path, struct fuse_file_info *fi) |
35629743 | 416 | { |
237e200e SH |
417 | int (*cg_releasedir)(const char *path, struct fuse_file_info *fi); |
418 | char *error; | |
419 | dlerror(); /* Clear any existing error */ | |
420 | cg_releasedir = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_releasedir"); | |
421 | error = dlerror(); | |
422 | if (error != NULL) { | |
b8defc3d | 423 | lxcfs_error("%s\n", error); |
237e200e | 424 | return -1; |
96fc5ee6 | 425 | } |
237e200e SH |
426 | |
427 | return cg_releasedir(path, fi); | |
35629743 SH |
428 | } |
429 | ||
2ad6d2bd SH |
430 | /* |
431 | * FUSE ops for / | |
432 | * these just delegate to the /proc and /cgroup ops as | |
433 | * needed | |
434 | */ | |
758ad80c SH |
435 | |
436 | static int lxcfs_getattr(const char *path, struct stat *sb) | |
437 | { | |
237e200e | 438 | int ret; |
17e6e1e2 CB |
439 | struct timespec now; |
440 | ||
758ad80c | 441 | if (strcmp(path, "/") == 0) { |
17e6e1e2 CB |
442 | if (clock_gettime(CLOCK_REALTIME, &now) < 0) |
443 | return -EINVAL; | |
444 | sb->st_uid = sb->st_gid = 0; | |
445 | sb->st_atim = sb->st_mtim = sb->st_ctim = now; | |
446 | sb->st_size = 0; | |
758ad80c SH |
447 | sb->st_mode = S_IFDIR | 00755; |
448 | sb->st_nlink = 2; | |
449 | return 0; | |
450 | } | |
17e6e1e2 | 451 | |
758ad80c | 452 | if (strncmp(path, "/cgroup", 7) == 0) { |
237e200e SH |
453 | up_users(); |
454 | ret = do_cg_getattr(path, sb); | |
455 | down_users(); | |
456 | return ret; | |
758ad80c | 457 | } |
35629743 | 458 | if (strncmp(path, "/proc", 5) == 0) { |
237e200e SH |
459 | up_users(); |
460 | ret = do_proc_getattr(path, sb); | |
461 | down_users(); | |
462 | return ret; | |
758ad80c | 463 | } |
f9b24a3e | 464 | return -ENOENT; |
758ad80c SH |
465 | } |
466 | ||
467 | static int lxcfs_opendir(const char *path, struct fuse_file_info *fi) | |
468 | { | |
237e200e | 469 | int ret; |
758ad80c SH |
470 | if (strcmp(path, "/") == 0) |
471 | return 0; | |
472 | ||
473 | if (strncmp(path, "/cgroup", 7) == 0) { | |
237e200e SH |
474 | up_users(); |
475 | ret = do_cg_opendir(path, fi); | |
476 | down_users(); | |
477 | return ret; | |
758ad80c | 478 | } |
35629743 SH |
479 | if (strcmp(path, "/proc") == 0) |
480 | return 0; | |
481 | return -ENOENT; | |
758ad80c SH |
482 | } |
483 | ||
484 | static int lxcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, | |
485 | struct fuse_file_info *fi) | |
486 | { | |
237e200e | 487 | int ret; |
758ad80c | 488 | if (strcmp(path, "/") == 0) { |
d639f863 CB |
489 | if (filler(buf, ".", NULL, 0) != 0 || |
490 | filler(buf, "..", NULL, 0) != 0 || | |
491 | filler(buf, "proc", NULL, 0) != 0 || | |
492 | filler(buf, "cgroup", NULL, 0) != 0) | |
f9b24a3e | 493 | return -ENOMEM; |
758ad80c SH |
494 | return 0; |
495 | } | |
237e200e SH |
496 | if (strncmp(path, "/cgroup", 7) == 0) { |
497 | up_users(); | |
498 | ret = do_cg_readdir(path, buf, filler, offset, fi); | |
499 | down_users(); | |
500 | return ret; | |
501 | } | |
502 | if (strcmp(path, "/proc") == 0) { | |
503 | up_users(); | |
504 | ret = do_proc_readdir(path, buf, filler, offset, fi); | |
505 | down_users(); | |
506 | return ret; | |
507 | } | |
f9b24a3e | 508 | return -ENOENT; |
758ad80c SH |
509 | } |
510 | ||
bddbb106 SH |
511 | static int lxcfs_access(const char *path, int mode) |
512 | { | |
513 | int ret; | |
17e6e1e2 | 514 | |
f9b24a3e | 515 | if (strcmp(path, "/") == 0 && (mode & W_OK) == 0) |
17e6e1e2 CB |
516 | return 0; |
517 | ||
bddbb106 SH |
518 | if (strncmp(path, "/cgroup", 7) == 0) { |
519 | up_users(); | |
520 | ret = do_cg_access(path, mode); | |
521 | down_users(); | |
522 | return ret; | |
523 | } | |
524 | if (strncmp(path, "/proc", 5) == 0) { | |
525 | up_users(); | |
526 | ret = do_proc_access(path, mode); | |
527 | down_users(); | |
528 | return ret; | |
529 | } | |
530 | ||
f9b24a3e | 531 | return -EACCES; |
bddbb106 SH |
532 | } |
533 | ||
758ad80c SH |
534 | static int lxcfs_releasedir(const char *path, struct fuse_file_info *fi) |
535 | { | |
237e200e | 536 | int ret; |
758ad80c SH |
537 | if (strcmp(path, "/") == 0) |
538 | return 0; | |
539 | if (strncmp(path, "/cgroup", 7) == 0) { | |
237e200e SH |
540 | up_users(); |
541 | ret = do_cg_releasedir(path, fi); | |
542 | down_users(); | |
543 | return ret; | |
758ad80c | 544 | } |
35629743 SH |
545 | if (strcmp(path, "/proc") == 0) |
546 | return 0; | |
758ad80c SH |
547 | return -EINVAL; |
548 | } | |
549 | ||
99978832 SH |
550 | static int lxcfs_open(const char *path, struct fuse_file_info *fi) |
551 | { | |
237e200e SH |
552 | int ret; |
553 | if (strncmp(path, "/cgroup", 7) == 0) { | |
554 | up_users(); | |
555 | ret = do_cg_open(path, fi); | |
556 | down_users(); | |
557 | return ret; | |
558 | } | |
559 | if (strncmp(path, "/proc", 5) == 0) { | |
560 | up_users(); | |
561 | ret = do_proc_open(path, fi); | |
562 | down_users(); | |
563 | return ret; | |
564 | } | |
99978832 | 565 | |
f9b24a3e | 566 | return -EACCES; |
99978832 SH |
567 | } |
568 | ||
569 | static int lxcfs_read(const char *path, char *buf, size_t size, off_t offset, | |
570 | struct fuse_file_info *fi) | |
571 | { | |
237e200e SH |
572 | int ret; |
573 | if (strncmp(path, "/cgroup", 7) == 0) { | |
574 | up_users(); | |
575 | ret = do_cg_read(path, buf, size, offset, fi); | |
576 | down_users(); | |
577 | return ret; | |
578 | } | |
579 | if (strncmp(path, "/proc", 5) == 0) { | |
580 | up_users(); | |
581 | ret = do_proc_read(path, buf, size, offset, fi); | |
582 | down_users(); | |
583 | return ret; | |
584 | } | |
99978832 SH |
585 | |
586 | return -EINVAL; | |
587 | } | |
588 | ||
2ad6d2bd SH |
589 | int lxcfs_write(const char *path, const char *buf, size_t size, off_t offset, |
590 | struct fuse_file_info *fi) | |
591 | { | |
237e200e | 592 | int ret; |
2ad6d2bd | 593 | if (strncmp(path, "/cgroup", 7) == 0) { |
237e200e SH |
594 | up_users(); |
595 | ret = do_cg_write(path, buf, size, offset, fi); | |
596 | down_users(); | |
597 | return ret; | |
2ad6d2bd SH |
598 | } |
599 | ||
600 | return -EINVAL; | |
601 | } | |
602 | ||
99978832 SH |
603 | static int lxcfs_flush(const char *path, struct fuse_file_info *fi) |
604 | { | |
605 | return 0; | |
606 | } | |
607 | ||
608 | static int lxcfs_release(const char *path, struct fuse_file_info *fi) | |
758ad80c | 609 | { |
237e200e SH |
610 | int ret; |
611 | if (strncmp(path, "/cgroup", 7) == 0) { | |
612 | up_users(); | |
613 | ret = do_cg_release(path, fi); | |
614 | down_users(); | |
615 | return ret; | |
616 | } | |
617 | if (strncmp(path, "/proc", 5) == 0) { | |
618 | up_users(); | |
619 | ret = do_proc_release(path, fi); | |
620 | down_users(); | |
621 | return ret; | |
622 | } | |
8f6e8f5e SH |
623 | |
624 | return -EINVAL; | |
99978832 SH |
625 | } |
626 | ||
627 | static int lxcfs_fsync(const char *path, int datasync, struct fuse_file_info *fi) | |
628 | { | |
629 | return 0; | |
758ad80c SH |
630 | } |
631 | ||
ab54b798 SH |
632 | int lxcfs_mkdir(const char *path, mode_t mode) |
633 | { | |
237e200e SH |
634 | int ret; |
635 | if (strncmp(path, "/cgroup", 7) == 0) { | |
636 | up_users(); | |
637 | ret = do_cg_mkdir(path, mode); | |
638 | down_users(); | |
639 | return ret; | |
640 | } | |
ab54b798 | 641 | |
f9b24a3e | 642 | return -EPERM; |
ab54b798 SH |
643 | } |
644 | ||
341b21ad SH |
645 | int lxcfs_chown(const char *path, uid_t uid, gid_t gid) |
646 | { | |
237e200e SH |
647 | int ret; |
648 | if (strncmp(path, "/cgroup", 7) == 0) { | |
649 | up_users(); | |
650 | ret = do_cg_chown(path, uid, gid); | |
651 | down_users(); | |
652 | return ret; | |
653 | } | |
341b21ad | 654 | |
f9b24a3e CB |
655 | if (strncmp(path, "/proc", 5) == 0) |
656 | return -EPERM; | |
657 | ||
658 | return -ENOENT; | |
341b21ad SH |
659 | } |
660 | ||
2ad6d2bd SH |
661 | /* |
662 | * cat first does a truncate before doing ops->write. This doesn't | |
663 | * really make sense for cgroups. So just return 0 always but do | |
664 | * nothing. | |
665 | */ | |
666 | int lxcfs_truncate(const char *path, off_t newsize) | |
667 | { | |
668 | if (strncmp(path, "/cgroup", 7) == 0) | |
669 | return 0; | |
f9b24a3e | 670 | return -EPERM; |
2ad6d2bd SH |
671 | } |
672 | ||
50d8d5b5 SH |
673 | int lxcfs_rmdir(const char *path) |
674 | { | |
237e200e SH |
675 | int ret; |
676 | if (strncmp(path, "/cgroup", 7) == 0) { | |
677 | up_users(); | |
678 | ret = do_cg_rmdir(path); | |
679 | down_users(); | |
680 | return ret; | |
681 | } | |
f9b24a3e | 682 | return -EPERM; |
50d8d5b5 SH |
683 | } |
684 | ||
fd2e4e03 SH |
685 | int lxcfs_chmod(const char *path, mode_t mode) |
686 | { | |
237e200e SH |
687 | int ret; |
688 | if (strncmp(path, "/cgroup", 7) == 0) { | |
689 | up_users(); | |
690 | ret = do_cg_chmod(path, mode); | |
691 | down_users(); | |
692 | return ret; | |
693 | } | |
f9b24a3e CB |
694 | |
695 | if (strncmp(path, "/proc", 5) == 0) | |
696 | return -EPERM; | |
697 | ||
698 | return -ENOENT; | |
fd2e4e03 SH |
699 | } |
700 | ||
758ad80c SH |
701 | const struct fuse_operations lxcfs_ops = { |
702 | .getattr = lxcfs_getattr, | |
703 | .readlink = NULL, | |
704 | .getdir = NULL, | |
705 | .mknod = NULL, | |
ab54b798 | 706 | .mkdir = lxcfs_mkdir, |
758ad80c | 707 | .unlink = NULL, |
50d8d5b5 | 708 | .rmdir = lxcfs_rmdir, |
758ad80c SH |
709 | .symlink = NULL, |
710 | .rename = NULL, | |
711 | .link = NULL, | |
fd2e4e03 | 712 | .chmod = lxcfs_chmod, |
341b21ad | 713 | .chown = lxcfs_chown, |
2ad6d2bd | 714 | .truncate = lxcfs_truncate, |
758ad80c | 715 | .utime = NULL, |
99978832 SH |
716 | |
717 | .open = lxcfs_open, | |
718 | .read = lxcfs_read, | |
719 | .release = lxcfs_release, | |
2ad6d2bd | 720 | .write = lxcfs_write, |
99978832 | 721 | |
758ad80c | 722 | .statfs = NULL, |
99978832 SH |
723 | .flush = lxcfs_flush, |
724 | .fsync = lxcfs_fsync, | |
758ad80c SH |
725 | |
726 | .setxattr = NULL, | |
727 | .getxattr = NULL, | |
728 | .listxattr = NULL, | |
729 | .removexattr = NULL, | |
730 | ||
731 | .opendir = lxcfs_opendir, | |
732 | .readdir = lxcfs_readdir, | |
733 | .releasedir = lxcfs_releasedir, | |
734 | ||
735 | .fsyncdir = NULL, | |
736 | .init = NULL, | |
737 | .destroy = NULL, | |
bddbb106 | 738 | .access = lxcfs_access, |
758ad80c SH |
739 | .create = NULL, |
740 | .ftruncate = NULL, | |
741 | .fgetattr = NULL, | |
742 | }; | |
743 | ||
c3fb7d7f | 744 | static void usage() |
758ad80c SH |
745 | { |
746 | fprintf(stderr, "Usage:\n"); | |
747 | fprintf(stderr, "\n"); | |
1de81503 DK |
748 | fprintf(stderr, "lxcfs [-f|-d] [-p pidfile] mountpoint\n"); |
749 | fprintf(stderr, " -f running foreground by default; -d enable debug output \n"); | |
e190ee91 | 750 | fprintf(stderr, " Default pidfile is %s/lxcfs.pid\n", RUNTIME_PATH); |
c3fb7d7f | 751 | fprintf(stderr, "lxcfs -h\n"); |
758ad80c SH |
752 | exit(1); |
753 | } | |
754 | ||
99978832 | 755 | static bool is_help(char *w) |
758ad80c SH |
756 | { |
757 | if (strcmp(w, "-h") == 0 || | |
758 | strcmp(w, "--help") == 0 || | |
759 | strcmp(w, "-help") == 0 || | |
760 | strcmp(w, "help") == 0) | |
761 | return true; | |
762 | return false; | |
763 | } | |
764 | ||
1de81503 | 765 | bool swallow_arg(int *argcp, char *argv[], char *which) |
0b0f73db SH |
766 | { |
767 | int i; | |
768 | ||
769 | for (i = 1; argv[i]; i++) { | |
770 | if (strcmp(argv[i], which) != 0) | |
771 | continue; | |
772 | for (; argv[i]; i++) { | |
773 | argv[i] = argv[i+1]; | |
774 | } | |
775 | (*argcp)--; | |
1de81503 | 776 | return true; |
0b0f73db | 777 | } |
1de81503 | 778 | return false; |
0b0f73db SH |
779 | } |
780 | ||
e190ee91 | 781 | bool swallow_option(int *argcp, char *argv[], char *opt, char **v) |
0b0f73db SH |
782 | { |
783 | int i; | |
784 | ||
785 | for (i = 1; argv[i]; i++) { | |
786 | if (!argv[i+1]) | |
787 | continue; | |
788 | if (strcmp(argv[i], opt) != 0) | |
789 | continue; | |
e190ee91 SH |
790 | do { |
791 | *v = strdup(argv[i+1]); | |
792 | } while (!*v); | |
0b0f73db SH |
793 | for (; argv[i+1]; i++) { |
794 | argv[i] = argv[i+2]; | |
795 | } | |
796 | (*argcp) -= 2; | |
e190ee91 | 797 | return true; |
0b0f73db | 798 | } |
e190ee91 | 799 | return false; |
0b0f73db SH |
800 | } |
801 | ||
e190ee91 SH |
802 | static int set_pidfile(char *pidfile) |
803 | { | |
804 | int fd; | |
805 | char buf[50]; | |
806 | struct flock fl; | |
807 | ||
808 | fl.l_type = F_WRLCK; | |
809 | fl.l_whence = SEEK_SET; | |
810 | fl.l_start = 0; | |
811 | fl.l_len = 0; | |
812 | ||
813 | fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | |
814 | if (fd == -1) { | |
b8defc3d | 815 | fprintf(stderr, "Could not open pidfile %s: %m\n", pidfile); |
e190ee91 SH |
816 | return -1; |
817 | } | |
818 | ||
819 | if (fcntl(fd, F_SETLK, &fl) == -1) { | |
820 | if (errno == EAGAIN || errno == EACCES) { | |
821 | fprintf(stderr, "PID file '%s' is already locked.\n", pidfile); | |
822 | close(fd); | |
823 | return -1; | |
824 | } | |
825 | fprintf(stderr, "Warning; unable to lock PID file, proceeding.\n"); | |
826 | } | |
827 | ||
828 | if (ftruncate(fd, 0) == -1) { | |
829 | fprintf(stderr, "Error truncating PID file '%s': %m", pidfile); | |
830 | close(fd); | |
831 | return -1; | |
832 | } | |
833 | ||
834 | snprintf(buf, 50, "%ld\n", (long) getpid()); | |
835 | if (write(fd, buf, strlen(buf)) != strlen(buf)) { | |
836 | fprintf(stderr, "Error writing to PID file '%s': %m", pidfile); | |
837 | close(fd); | |
838 | return -1; | |
839 | } | |
840 | ||
841 | return fd; | |
842 | } | |
843 | ||
758ad80c SH |
844 | int main(int argc, char *argv[]) |
845 | { | |
47f5266f CB |
846 | int ret = EXIT_FAILURE; |
847 | int pidfd = -1; | |
e190ee91 | 848 | char *pidfile = NULL, *v = NULL; |
29a73c2f | 849 | size_t pidfile_len; |
1de81503 | 850 | bool debug = false; |
0b0f73db SH |
851 | /* |
852 | * what we pass to fuse_main is: | |
1de81503 | 853 | * argv[0] -s [-f|-d] -o allow_other,directio argv[1] NULL |
0b0f73db | 854 | */ |
2c51f8dd SH |
855 | int nargs = 5, cnt = 0; |
856 | char *newargv[6]; | |
758ad80c | 857 | |
0b0f73db SH |
858 | /* accomodate older init scripts */ |
859 | swallow_arg(&argc, argv, "-s"); | |
860 | swallow_arg(&argc, argv, "-f"); | |
1de81503 | 861 | debug = swallow_arg(&argc, argv, "-d"); |
e190ee91 SH |
862 | if (swallow_option(&argc, argv, "-o", &v)) { |
863 | if (strcmp(v, "allow_other") != 0) { | |
864 | fprintf(stderr, "Warning: unexpected fuse option %s\n", v); | |
47f5266f | 865 | exit(EXIT_FAILURE); |
e190ee91 SH |
866 | } |
867 | free(v); | |
868 | v = NULL; | |
869 | } | |
870 | if (swallow_option(&argc, argv, "-p", &v)) | |
871 | pidfile = v; | |
0b0f73db | 872 | |
2e9c0b32 SH |
873 | if (argc == 2 && strcmp(argv[1], "--version") == 0) { |
874 | fprintf(stderr, "%s\n", VERSION); | |
47f5266f | 875 | exit(EXIT_SUCCESS); |
2e9c0b32 | 876 | } |
0b0f73db | 877 | if (argc != 2 || is_help(argv[1])) |
c3fb7d7f | 878 | usage(); |
758ad80c | 879 | |
59120f04 | 880 | do_reload(); |
8fad16cd | 881 | if (signal(SIGUSR1, reload_handler) == SIG_ERR) { |
8dcde249 | 882 | fprintf(stderr, "Error setting USR1 signal handler: %m\n"); |
47f5266f | 883 | goto out; |
8fad16cd | 884 | } |
e190ee91 | 885 | |
38a76a91 | 886 | newargv[cnt++] = argv[0]; |
1de81503 DK |
887 | if (debug) { |
888 | newargv[cnt++] = "-d"; | |
889 | } else { | |
890 | newargv[cnt++] = "-f"; | |
891 | } | |
38a76a91 | 892 | newargv[cnt++] = "-o"; |
f466a31e | 893 | newargv[cnt++] = "allow_other,direct_io,entry_timeout=0.5,attr_timeout=0.5"; |
38a76a91 SH |
894 | newargv[cnt++] = argv[1]; |
895 | newargv[cnt++] = NULL; | |
758ad80c | 896 | |
e190ee91 SH |
897 | if (!pidfile) { |
898 | pidfile_len = strlen(RUNTIME_PATH) + strlen("/lxcfs.pid") + 1; | |
899 | pidfile = alloca(pidfile_len); | |
900 | snprintf(pidfile, pidfile_len, "%s/lxcfs.pid", RUNTIME_PATH); | |
901 | } | |
902 | if ((pidfd = set_pidfile(pidfile)) < 0) | |
903 | goto out; | |
904 | ||
47f5266f CB |
905 | if (!fuse_main(nargs, newargv, &lxcfs_ops, NULL)) |
906 | ret = EXIT_SUCCESS; | |
237e200e | 907 | |
c0adec85 | 908 | out: |
47f5266f CB |
909 | if (dlopen_handle) |
910 | dlclose(dlopen_handle); | |
911 | if (pidfile) | |
912 | unlink(pidfile); | |
913 | if (pidfd > 0) | |
914 | close(pidfd); | |
47f5266f | 915 | exit(ret); |
2183082c | 916 | } |