blob: 4caacb98fa8278a747a7ca1c427b9ad621bbfc90 [file] [log] [blame]
#include <fuse.h>
#include <pthread.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#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(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(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;
}