#include <wait.h>
#include <linux/sched.h>
#include <sys/epoll.h>
-#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/socket.h>
-#include <sys/syscall.h>
#include "bindings.h"
#include "config.h" // for VERSION
-/* Define pivot_root() if missing from the C library */
-#ifndef HAVE_PIVOT_ROOT
-static int pivot_root(const char * new_root, const char * put_old)
-{
-#ifdef __NR_pivot_root
-return syscall(__NR_pivot_root, new_root, put_old);
-#else
-errno = ENOSYS;
-return -1;
-#endif
-}
-#else
-extern int pivot_root(const char * new_root, const char * put_old);
-#endif
-
void *dlopen_handle;
/* Functions to keep track of number of threads using the library */
int ret;
if ((ret = pthread_mutex_lock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
+ lxcfs_error("returned:%d %s\n", ret, strerror(ret));
exit(1);
}
}
int ret;
if ((ret = pthread_mutex_unlock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
+ lxcfs_error("returned:%d %s\n", ret, strerror(ret));
exit(1);
}
}
* lock and when we know the user_count was 0 */
static void do_reload(void)
{
- if (dlopen_handle)
+ if (dlopen_handle) {
+ lxcfs_debug("%s\n", "Closing liblxcfs.so handle.");
dlclose(dlopen_handle);
+ }
/* First try loading using ld.so */
dlopen_handle = dlopen("liblxcfs.so", RTLD_LAZY);
- if (dlopen_handle)
+ if (dlopen_handle) {
+ lxcfs_debug("%s\n", "Successfully called dlopen() on liblxcfs.so.");
goto good;
+ }
dlopen_handle = dlopen("/usr/lib/lxcfs/liblxcfs.so", RTLD_LAZY);
if (!dlopen_handle) {
- fprintf(stderr, "Failed to open liblxcfs\n");
+ lxcfs_error("Failed to open liblxcfs.so: %s.\n", dlerror());
_exit(1);
}
good:
if (need_reload)
- fprintf(stderr, "lxcfs: reloaded\n");
+ lxcfs_error("%s\n", "lxcfs: reloaded");
need_reload = 0;
}
need_reload = 1;
}
-/*
- * Functions and types to ease the use of clone()/__clone2()
- */
-pid_t lxcfs_clone(int (*fn)(void *), void *arg, int flags)
-{
- size_t stack_size = sysconf(_SC_PAGESIZE);
- void *stack = alloca(stack_size);
- pid_t ret;
-
-#ifdef __ia64__
- ret = __clone2(do_clone, stack,
- stack_size, flags | SIGCHLD, &clone_arg);
-#else
- ret = clone(fn, stack + stack_size, flags | SIGCHLD, &arg);
-#endif
- if (ret < 0)
- fprintf(stderr, "failed to clone (%#x): %s", flags, strerror(errno));
-
- return ret;
-}
-
/* Functions to run the library methods */
static int do_cg_getattr(const char *path, struct stat *sb)
{
cg_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "cg_getattr");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_getattr: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "proc_getattr");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_getattr: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_read");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_read: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_read");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_read: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_write = (int (*)(const char *, const char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_write");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_write: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_mkdir = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_mkdir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_mkdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_chown = (int (*)(const char *, uid_t, gid_t)) dlsym(dlopen_handle, "cg_chown");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_chown: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_rmdir = (int (*)(const char *path)) dlsym(dlopen_handle, "cg_rmdir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_rmdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_chmod = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_chmod");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_chmod: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_readdir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_readdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_readdir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_readdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_open = (int (*)(const char *, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_open");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_open: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "cg_access");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_access: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_open = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "proc_open");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_open: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "proc_access");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_access: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_release");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_release: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_release");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_release: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_opendir = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "cg_opendir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_opendir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_releasedir = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_releasedir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_releasedir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
static int lxcfs_getattr(const char *path, struct stat *sb)
{
int ret;
+ struct timespec now;
+
if (strcmp(path, "/") == 0) {
+ if (clock_gettime(CLOCK_REALTIME, &now) < 0)
+ return -EINVAL;
+ sb->st_uid = sb->st_gid = 0;
+ sb->st_atim = sb->st_mtim = sb->st_ctim = now;
+ sb->st_size = 0;
sb->st_mode = S_IFDIR | 00755;
sb->st_nlink = 2;
return 0;
}
+
if (strncmp(path, "/cgroup", 7) == 0) {
up_users();
ret = do_cg_getattr(path, sb);
down_users();
return ret;
}
- return -EINVAL;
+ return -ENOENT;
}
static int lxcfs_opendir(const char *path, struct fuse_file_info *fi)
{
int ret;
if (strcmp(path, "/") == 0) {
- if (filler(buf, "proc", NULL, 0) != 0 ||
- filler(buf, "cgroup", NULL, 0) != 0)
- return -EINVAL;
+ if (filler(buf, ".", NULL, 0) != 0 ||
+ filler(buf, "..", NULL, 0) != 0 ||
+ filler(buf, "proc", NULL, 0) != 0 ||
+ filler(buf, "cgroup", NULL, 0) != 0)
+ return -ENOMEM;
return 0;
}
if (strncmp(path, "/cgroup", 7) == 0) {
down_users();
return ret;
}
- return -EINVAL;
+ return -ENOENT;
}
static int lxcfs_access(const char *path, int mode)
{
int ret;
+
+ if (strcmp(path, "/") == 0 && (mode & W_OK) == 0)
+ return 0;
+
if (strncmp(path, "/cgroup", 7) == 0) {
up_users();
ret = do_cg_access(path, mode);
return ret;
}
- return -EINVAL;
+ return -EACCES;
}
static int lxcfs_releasedir(const char *path, struct fuse_file_info *fi)
return ret;
}
- return -EINVAL;
+ return -EACCES;
}
static int lxcfs_read(const char *path, char *buf, size_t size, off_t offset,
return ret;
}
- return -EINVAL;
+ return -EPERM;
}
int lxcfs_chown(const char *path, uid_t uid, gid_t gid)
return ret;
}
- return -EINVAL;
+ if (strncmp(path, "/proc", 5) == 0)
+ return -EPERM;
+
+ return -ENOENT;
}
/*
{
if (strncmp(path, "/cgroup", 7) == 0)
return 0;
- return -EINVAL;
+ return -EPERM;
}
int lxcfs_rmdir(const char *path)
down_users();
return ret;
}
- return -EINVAL;
+ return -EPERM;
}
int lxcfs_chmod(const char *path, mode_t mode)
down_users();
return ret;
}
- return -EINVAL;
+
+ if (strncmp(path, "/proc", 5) == 0)
+ return -EPERM;
+
+ return -ENOENT;
}
const struct fuse_operations lxcfs_ops = {
.fgetattr = NULL,
};
-static void usage(const char *me)
+static void usage()
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\n");
- fprintf(stderr, "%s [-p pidfile] mountpoint\n", me);
+ fprintf(stderr, "lxcfs [-f|-d] [-p pidfile] mountpoint\n");
+ fprintf(stderr, " -f running foreground by default; -d enable debug output \n");
fprintf(stderr, " Default pidfile is %s/lxcfs.pid\n", RUNTIME_PATH);
- fprintf(stderr, "%s -h\n", me);
+ fprintf(stderr, "lxcfs -h\n");
exit(1);
}
return false;
}
-void swallow_arg(int *argcp, char *argv[], char *which)
+bool swallow_arg(int *argcp, char *argv[], char *which)
{
int i;
argv[i] = argv[i+1];
}
(*argcp)--;
- return;
+ return true;
}
+ return false;
}
bool swallow_option(int *argcp, char *argv[], char *opt, char **v)
return false;
}
-static bool mkdir_p(const char *dir, mode_t mode)
-{
- const char *tmp = dir;
- const char *orig = dir;
- char *makeme;
-
- do {
- dir = tmp + strspn(tmp, "/");
- tmp = dir + strcspn(dir, "/");
- makeme = strndup(orig, dir - orig);
- if (!makeme)
- return false;
- if (mkdir(makeme, mode) && errno != EEXIST) {
- fprintf(stderr, "failed to create directory '%s': %s",
- makeme, strerror(errno));
- free(makeme);
- return false;
- }
- free(makeme);
- } while(tmp != dir);
-
- return true;
-}
-
-static bool umount_if_mounted(void)
-{
- if (umount2(BASEDIR, MNT_DETACH) < 0 && errno != EINVAL) {
- fprintf(stderr, "failed to umount %s: %s\n", BASEDIR,
- strerror(errno));
- return false;
- }
- return true;
-}
-
-static int pivot_enter(void)
-{
- int ret = -1, oldroot = -1, newroot = -1;
-
- oldroot = open("/", O_DIRECTORY | O_RDONLY);
- if (oldroot < 0) {
- fprintf(stderr, "%s: Failed to open old root for fchdir.\n", __func__);
- return ret;
- }
-
- newroot = open(ROOTDIR, O_DIRECTORY | O_RDONLY);
- if (newroot < 0) {
- fprintf(stderr, "%s: Failed to open new root for fchdir.\n", __func__);
- goto err;
- }
-
- /* change into new root fs */
- if (fchdir(newroot) < 0) {
- fprintf(stderr, "%s: Failed to change directory to new rootfs: %s.\n", __func__, ROOTDIR);
- goto err;
- }
-
- /* pivot_root into our new root fs */
- if (pivot_root(".", ".") < 0) {
- fprintf(stderr, "%s: pivot_root() syscall failed: %s.\n", __func__, strerror(errno));
- goto err;
- }
-
- /*
- * At this point the old-root is mounted on top of our new-root.
- * To unmounted it we must not be chdir'd into it, so escape back
- * to the old-root.
- */
- if (fchdir(oldroot) < 0) {
- fprintf(stderr, "%s: Failed to enter old root.\n", __func__);
- goto err;
- }
- if (umount2(".", MNT_DETACH) < 0) {
- fprintf(stderr, "%s: Failed to detach old root.\n", __func__);
- goto err;
- }
-
- if (fchdir(newroot) < 0) {
- fprintf(stderr, "%s: Failed to re-enter new root.\n", __func__);
- goto err;
- }
-
- ret = 0;
-
-err:
- if (oldroot > 0)
- close(oldroot);
- if (newroot > 0)
- close(newroot);
- return ret;
-}
-
-/* Prepare our new clean root. */
-static int pivot_prepare(void)
-{
- if (mkdir(ROOTDIR, 0700) < 0 && errno != EEXIST) {
- fprintf(stderr, "%s: Failed to create directory for new root.\n", __func__);
- return -1;
- }
-
- if (mount("/", ROOTDIR, NULL, MS_BIND, 0) < 0) {
- fprintf(stderr, "%s: Failed to bind-mount / for new root: %s.\n", __func__, strerror(errno));
- return -1;
- }
-
- if (mount(RUNTIME_PATH, ROOTDIR RUNTIME_PATH, NULL, MS_BIND, 0) < 0) {
- fprintf(stderr, "%s: Failed to bind-mount /run into new root: %s.\n", __func__, strerror(errno));
- return -1;
- }
-
- if (mount(BASEDIR, ROOTDIR BASEDIR, NULL, MS_REC | MS_MOVE, 0) < 0) {
- printf("%s: failed to move " BASEDIR " into new root: %s.\n", __func__, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-static bool pivot_new_root(void)
-{
- /* Prepare new root. */
- if (pivot_prepare() < 0)
- return false;
-
- /* Pivot into new root. */
- if (pivot_enter() < 0)
- return false;
-
- return true;
-}
-
-static bool setup_cgfs_dir(void)
-{
- if (!mkdir_p(BASEDIR, 0700)) {
- fprintf(stderr, "Failed to create lxcfs cgdir\n");
- return false;
- }
- if (!umount_if_mounted()) {
- fprintf(stderr, "Failed to clean up old lxcfs cgdir\n");
- return false;
- }
- if (mount("tmpfs", BASEDIR, "tmpfs", 0, "size=100000,mode=700") < 0) {
- fprintf(stderr, "Failed to mount tmpfs for private controllers\n");
- return false;
- }
- return true;
-}
-
-static bool do_mount_cgroups(void)
-{
- char *target;
- size_t clen, len;
- int i, fd, ret;
-
- for (i = 0; i < num_hierarchies; i++) {
- char *controller = hierarchies[i];
- clen = strlen(controller);
- len = strlen(BASEDIR) + clen + 2;
- target = malloc(len);
- if (!target)
- return false;
- ret = snprintf(target, len, "%s/%s", BASEDIR, controller);
- if (ret < 0 || ret >= len) {
- free(target);
- return false;
- }
- if (mkdir(target, 0755) < 0 && errno != EEXIST) {
- free(target);
- return false;
- }
- if (mount(controller, target, "cgroup", 0, controller) < 0) {
- fprintf(stderr, "Failed mounting cgroup %s\n", controller);
- free(target);
- return false;
- }
-
- fd = open(target, O_DIRECTORY);
- if (fd < 0) {
- free(target);
- return false;
- }
-
- fd_hierarchies[i] = fd;
- free(target);
- }
- return true;
-}
-
-static int cgfs_setup_controllers(void *data)
-{
- if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0) < 0) {
- fprintf(stderr, "%s: Failed to re-mount / private: %s.\n", __func__, strerror(errno));
- return -1;
- }
-
- if (!setup_cgfs_dir()) {
- return false;
- }
-
- if (!do_mount_cgroups()) {
- fprintf(stderr, "Failed to set up cgroup mounts\n");
- return false;
- }
-
- if (!pivot_new_root())
- return false;
-
- return true;
-}
-
static int set_pidfile(char *pidfile)
{
int fd;
fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
- fprintf(stderr, "Could not open pidfile %s: %m", pidfile);
+ fprintf(stderr, "Could not open pidfile %s: %m\n", pidfile);
return -1;
}
int main(int argc, char *argv[])
{
- int ret = -1, pidfd;
+ int ret = EXIT_FAILURE;
+ int pidfd = -1;
char *pidfile = NULL, *v = NULL;
size_t pidfile_len;
+ bool debug = false;
/*
* what we pass to fuse_main is:
- * argv[0] -s -f -o allow_other,directio argv[1] NULL
+ * argv[0] -s [-f|-d] -o allow_other,directio argv[1] NULL
*/
int nargs = 5, cnt = 0;
char *newargv[6];
/* accomodate older init scripts */
swallow_arg(&argc, argv, "-s");
swallow_arg(&argc, argv, "-f");
+ debug = swallow_arg(&argc, argv, "-d");
if (swallow_option(&argc, argv, "-o", &v)) {
if (strcmp(v, "allow_other") != 0) {
fprintf(stderr, "Warning: unexpected fuse option %s\n", v);
- exit(1);
+ exit(EXIT_FAILURE);
}
free(v);
v = NULL;
if (argc == 2 && strcmp(argv[1], "--version") == 0) {
fprintf(stderr, "%s\n", VERSION);
- exit(0);
+ exit(EXIT_SUCCESS);
}
if (argc != 2 || is_help(argv[1]))
- usage(argv[0]);
+ usage();
do_reload();
if (signal(SIGUSR1, reload_handler) == SIG_ERR) {
fprintf(stderr, "Error setting USR1 signal handler: %m\n");
- exit(1);
+ goto out;
}
newargv[cnt++] = argv[0];
- newargv[cnt++] = "-f";
+ if (debug) {
+ newargv[cnt++] = "-d";
+ } else {
+ newargv[cnt++] = "-f";
+ }
newargv[cnt++] = "-o";
newargv[cnt++] = "allow_other,direct_io,entry_timeout=0.5,attr_timeout=0.5";
newargv[cnt++] = argv[1];
newargv[cnt++] = NULL;
- /* Share memory with our clone(). */
- fd_hierarchies = mmap(NULL, sizeof(int *) * num_hierarchies, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- if (!fd_hierarchies)
- goto out;
-
- /* Mount cgroups in private mount namespace while sharing open file
- * desciptores between clone() and parent. */
- pid_t pid = lxcfs_clone(cgfs_setup_controllers, NULL, CLONE_NEWNS | CLONE_FILES);
- if (pid < 0) {
- fprintf(stderr, "%s: Error cloning new mount namespace: %s\n", __func__, strerror(errno));
- goto out;
- }
- if (waitpid(pid, NULL, 0) < 0) {
- fprintf(stderr, "%s: Error waiting on cloned child: %s\n", __func__, strerror(errno));
- goto out;
- }
-
if (!pidfile) {
pidfile_len = strlen(RUNTIME_PATH) + strlen("/lxcfs.pid") + 1;
pidfile = alloca(pidfile_len);
if ((pidfd = set_pidfile(pidfile)) < 0)
goto out;
- ret = fuse_main(nargs, newargv, &lxcfs_ops, NULL);
-
- dlclose(dlopen_handle);
- unlink(pidfile);
- close(pidfd);
+ if (!fuse_main(nargs, newargv, &lxcfs_ops, NULL))
+ ret = EXIT_SUCCESS;
out:
- return ret;
+ if (dlopen_handle)
+ dlclose(dlopen_handle);
+ if (pidfile)
+ unlink(pidfile);
+ if (pidfd > 0)
+ close(pidfd);
+ exit(ret);
}