#include #include #include #include #include #include #include #include #include #include #include #include #define MOUNTDIR "/mnt/avfs" static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static struct fuse *um_fuse; static const char *um_dir; #define MAX_USERS 100 static uid_t users[MAX_USERS]; static size_t numusers = 0; void avfs_main(struct fuse *fuse); static void reset_signal_handlers() { struct sigaction sa; sa.sa_handler = SIG_DFL; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; sigaction(SIGCHLD, &sa, NULL); } static void start_avfs(uid_t uid) { int res; char *userdir; struct fuse *user_fuse; struct passwd pw_buf; struct passwd *pw; char buf[1024]; res = getpwuid_r(uid, &pw_buf, buf, sizeof(buf), &pw); if(pw == NULL) return; user_fuse = fuse_new(FUSE_MULTITHREAD, 0); userdir = g_strdup_printf("%s/%010u", MOUNTDIR, uid); mkdir(userdir, 0755); chown(userdir, pw->pw_uid, pw->pw_gid); res = fuse_mount(user_fuse, userdir); g_free(userdir); if(res == -1) return; res = fork(); if(res == 0) { reset_signal_handlers(); initgroups(pw->pw_name, pw->pw_gid); setgid(pw->pw_gid); setuid(pw->pw_uid); avfs_main(user_fuse); exit(0); } fuse_destroy(user_fuse); } static int find_user(const char *userstr, uid_t *uid) { size_t i; char *end; *uid = strtol(userstr, &end, 10); if(*end != '\0') return 0; pthread_mutex_lock(&lock); for(i = 0; i < numusers; i++) { if(users[i] == *uid) { pthread_mutex_unlock(&lock); return 1; } } if(numusers == MAX_USERS) { memmove(users, users + sizeof(users[0]), (MAX_USERS - 1) * sizeof(users[0])); numusers --; } users[numusers++] = *uid; pthread_mutex_unlock(&lock); start_avfs(*uid); return 1; } static void root_attr(struct stat *stbuf) { stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2 + numusers; stbuf->st_size = MAX_USERS; stbuf->st_blksize = 1024; } static int um_getattr(struct fuse_cred *cred, const char *path, struct stat *stbuf) { uid_t uid; memset(stbuf, 0, sizeof(*stbuf)); if(strcmp(path, "/") == 0) { root_attr(stbuf); return 0; } if(!find_user(path+1, &uid)) return -ENOENT; stbuf->st_mode = S_IFLNK | 0777; stbuf->st_nlink = 1; stbuf->st_size = strlen(MOUNTDIR) + 1 + 10; stbuf->st_blksize = 1024; stbuf->st_uid = uid; return 0; } static int um_readlink(struct fuse_cred *cred, const char *path, char *buf, size_t size) { uid_t uid; if(!find_user(path+1, &uid)) return -ENOENT; snprintf(buf, size, "%s/%010u", MOUNTDIR, uid); return 0; } static int um_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h, fuse_dirfil_t filler) { size_t i; if(strcmp(path, "/") != 0) return 0; filler(h, ".", 0); filler(h, "..", 0); pthread_mutex_lock(&lock); for(i = 0; i < numusers; i++) { char buf[32]; sprintf(buf, "%u", users[i]); filler(h, buf, 0); } pthread_mutex_unlock(&lock); return 0; } static void exit_handler() { exit(0); } static void child_handler() { int status; wait(&status); } static void set_signal_handlers() { struct sigaction sa; sa.sa_handler = exit_handler; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) == -1 || sigaction(SIGINT, &sa, NULL) == -1 || sigaction(SIGTERM, &sa, NULL) == -1) { perror("Cannot set exit signal handlers"); exit(1); } sa.sa_handler = SIG_IGN; if(sigaction(SIGPIPE, &sa, NULL) == -1) { perror("Cannot set ignored signals"); exit(1); } sa.sa_handler = child_handler; if(sigaction(SIGCHLD, &sa, NULL) == -1) { perror("Cannot set child signal handler"); exit(1); } } static void cleanup() { fuse_unmount(um_fuse); fuse_destroy(um_fuse); } static struct fuse_operations um_oper = { getattr: um_getattr, getdir: um_getdir, readlink: um_readlink, }; int main(int argc, char *argv[]) { int res; if(argc != 2) { fprintf(stderr, "usage: %s mount_dir\n", argv[0]); exit(1); } um_dir = argv[1]; set_signal_handlers(); atexit(cleanup); um_fuse = fuse_new(FUSE_MULTITHREAD, 0); res = fuse_mount(um_fuse, um_dir); if(res == -1) exit(1); fuse_set_operations(um_fuse, &um_oper); fuse_loop(um_fuse); return 0; }