mirror of
https://github.com/mabam/afpfs-ng-mac.git
synced 2025-04-08 18:37:11 +00:00
693 lines
15 KiB
C
693 lines
15 KiB
C
/*
|
|
* commands.c
|
|
*
|
|
* Copyright (C) 2006 Alex deVries
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <utime.h>
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <stdarg.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
|
|
#include "afpfs-ng/afp.h"
|
|
#include "afpfs-ng/dsi.h"
|
|
#include "afp_server.h"
|
|
#include "afpfs-ng/utils.h"
|
|
#include "daemon.h"
|
|
#include "afpfs-ng/uams_def.h"
|
|
#include "afpfs-ng/codepage.h"
|
|
#include "afpfs-ng/libafpclient.h"
|
|
#include "afpfs-ng/map_def.h"
|
|
#include "fuse_int.h"
|
|
#include "fuse_error.h"
|
|
#include "fuse_internal.h"
|
|
|
|
#ifdef __linux
|
|
#define FUSE_DEVICE "/dev/fuse"
|
|
#else
|
|
#define FUSE_DEVICE "/dev/fuse0"
|
|
#endif
|
|
|
|
|
|
static int fuse_log_method=LOG_METHOD_SYSLOG;
|
|
|
|
void trigger_exit(void);
|
|
|
|
static struct fuse_client * client_base = NULL;
|
|
|
|
struct afp_volume * global_volume;
|
|
|
|
static int volopen(struct fuse_client * c, struct afp_volume * volume);
|
|
static int process_command(struct fuse_client * c);
|
|
static struct afp_volume * mount_volume(struct fuse_client * c,
|
|
struct afp_server * server, char * volname, char * volpassword) ;
|
|
|
|
void fuse_set_log_method(int new_method)
|
|
{
|
|
fuse_log_method=new_method;
|
|
}
|
|
|
|
|
|
static int remove_client(struct fuse_client * toremove)
|
|
{
|
|
struct fuse_client * c, * prev=NULL;
|
|
|
|
for (c=client_base;c;c=c->next) {
|
|
if (c==toremove) {
|
|
if (!prev) client_base=NULL;
|
|
else prev->next=toremove->next;
|
|
free(toremove);
|
|
toremove=NULL;
|
|
return 0;
|
|
}
|
|
prev=c;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int fuse_add_client(int fd)
|
|
{
|
|
struct fuse_client * c, *newc;
|
|
|
|
if ((newc=malloc(sizeof(*newc)))==NULL) goto error;
|
|
|
|
|
|
memset(newc,0,sizeof(*newc));
|
|
newc->fd=fd;
|
|
newc->next=NULL;
|
|
if (client_base==NULL) client_base=newc;
|
|
else {
|
|
for (c=client_base;c->next;c=c->next);
|
|
c->next=newc;
|
|
|
|
}
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
static int fuse_process_client_fds(fd_set * set, int max_fd)
|
|
{
|
|
|
|
struct fuse_client * c;
|
|
|
|
for (c=client_base;c;c=c->next) {
|
|
if (FD_ISSET(c->fd,set)) {
|
|
if (process_command(c)<0) return -1;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int fuse_scan_extra_fds(int command_fd, fd_set *set, int * max_fd)
|
|
{
|
|
|
|
struct sockaddr_un new_addr;
|
|
socklen_t new_len = sizeof(struct sockaddr_un);
|
|
int new_fd;
|
|
|
|
|
|
|
|
if (FD_ISSET(command_fd,set)) {
|
|
new_fd=accept(command_fd,(struct sockaddr *) &new_addr,&new_len);
|
|
if (new_fd>=0) {
|
|
fuse_add_client(new_fd);
|
|
FD_SET(new_fd,set);
|
|
if ((new_fd+1) > *max_fd) *max_fd=new_fd+1;
|
|
}
|
|
}
|
|
|
|
switch (fuse_process_client_fds(set,*max_fd)) {
|
|
case -1:
|
|
{
|
|
int i;
|
|
FD_CLR(new_fd,set);
|
|
for (i=*max_fd;i>=0;i--)
|
|
if (FD_ISSET(i,set)) {
|
|
*max_fd=i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
(*max_fd)++;
|
|
close(new_fd);
|
|
goto out;
|
|
case 1:
|
|
goto out;
|
|
}
|
|
/* unknown fd */
|
|
sleep(10);
|
|
|
|
return 0;
|
|
|
|
out:
|
|
return 1;
|
|
}
|
|
|
|
static void fuse_log_for_client(void * priv,
|
|
enum loglevels loglevel, int logtype, const char *message) {
|
|
int len = 0;
|
|
struct fuse_client * c = priv;
|
|
|
|
if (c) {
|
|
len = strlen(c->client_string);
|
|
snprintf(c->client_string+len,
|
|
MAX_CLIENT_RESPONSE-len,
|
|
"%s", message);
|
|
} else {
|
|
|
|
if (fuse_log_method & LOG_METHOD_SYSLOG)
|
|
syslog(LOG_INFO, "%s", message);
|
|
if (fuse_log_method & LOG_METHOD_STDOUT)
|
|
printf("%s",message);
|
|
}
|
|
|
|
}
|
|
|
|
struct start_fuse_thread_arg {
|
|
struct afp_volume * volume;
|
|
struct fuse_client * client;
|
|
int wait;
|
|
int fuse_result;
|
|
int fuse_errno;
|
|
int changeuid;
|
|
};
|
|
|
|
static void * start_fuse_thread(void * other)
|
|
{
|
|
int fuseargc=0;
|
|
const char *fuseargv[200];
|
|
#define mountstring_len (AFP_SERVER_NAME_LEN+1+AFP_VOLUME_NAME_LEN+1)
|
|
char mountstring[mountstring_len];
|
|
struct start_fuse_thread_arg * arg = other;
|
|
struct afp_volume * volume = arg->volume;
|
|
struct fuse_client * c = arg->client;
|
|
struct afp_server * server = volume->server;
|
|
|
|
/* Check to see if we have permissions to access the mountpoint */
|
|
|
|
snprintf(mountstring,mountstring_len,"%s:%s",
|
|
server->server_name_printable,
|
|
volume->volume_name_printable);
|
|
fuseargc=0;
|
|
fuseargv[0]=mountstring;
|
|
fuseargc++;
|
|
fuseargv[1]=volume->mountpoint;
|
|
fuseargc++;
|
|
if (get_debug_mode()) {
|
|
fuseargv[fuseargc]="-d";
|
|
fuseargc++;
|
|
} else {
|
|
fuseargv[fuseargc]="-f";
|
|
fuseargc++;
|
|
}
|
|
|
|
if (arg->changeuid) {
|
|
fuseargv[fuseargc]="-o";
|
|
fuseargc++;
|
|
fuseargv[fuseargc]="allow_other";
|
|
fuseargc++;
|
|
}
|
|
|
|
|
|
/* #ifdef USE_SINGLE_THREAD */
|
|
fuseargv[fuseargc]="-s";
|
|
fuseargc++;
|
|
/*
|
|
#endif
|
|
*/
|
|
global_volume=volume;
|
|
|
|
arg->fuse_result=
|
|
afp_register_fuse(fuseargc, (char **) fuseargv,volume);
|
|
|
|
arg->fuse_errno=errno;
|
|
|
|
arg->wait=0;
|
|
pthread_cond_signal(&volume->startup_condition_cond);
|
|
|
|
log_for_client((void *) c,AFPFSD,LOG_WARNING,
|
|
"Unmounting volume %s from %s\n",
|
|
volume->volume_name_printable,
|
|
volume->mountpoint);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int volopen(struct fuse_client * c, struct afp_volume * volume)
|
|
{
|
|
char mesg[1024];
|
|
unsigned int l = 0;
|
|
memset(mesg,0,1024);
|
|
int rc=afp_connect_volume(volume,volume->server,mesg,&l,1024);
|
|
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,mesg);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
static unsigned char process_suspend(struct fuse_client * c)
|
|
{
|
|
struct afp_server_suspend_request * req =(void *)c->incoming_string+1;
|
|
struct afp_server * s;
|
|
|
|
/* Find the server */
|
|
if ((s=find_server_by_name(req->server_name))==NULL) {
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,
|
|
"%s is an unknown server\n",req->server_name);
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
}
|
|
|
|
if (afp_zzzzz(s))
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
|
|
loop_disconnect(s);
|
|
|
|
s->connect_state=SERVER_STATE_DISCONNECTED;
|
|
log_for_client((void *) c,AFPFSD,LOG_NOTICE,
|
|
"Disconnected from %s\n",req->server_name);
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
}
|
|
|
|
|
|
static int afp_server_reconnect_loud(struct fuse_client * c, struct afp_server * s)
|
|
{
|
|
char mesg[1024];
|
|
unsigned int l = 2040;
|
|
int rc;
|
|
|
|
rc=afp_server_reconnect(s,mesg,&l,l);
|
|
|
|
if (rc)
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,
|
|
"%s",mesg);
|
|
return rc;
|
|
|
|
|
|
}
|
|
|
|
|
|
static unsigned char process_resume(struct fuse_client * c)
|
|
{
|
|
struct afp_server_resume_request * req =(void *) c->incoming_string+1;
|
|
struct afp_server * s;
|
|
|
|
/* Find the server */
|
|
if ((s=find_server_by_name(req->server_name))==NULL) {
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,
|
|
"%s is an unknown server\n",req->server_name);
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
}
|
|
|
|
if (afp_server_reconnect_loud(c,s))
|
|
{
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,
|
|
"Unable to reconnect to %s\n",req->server_name);
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
}
|
|
log_for_client((void *) c,AFPFSD,LOG_NOTICE,
|
|
"Resumed connection to %s\n",req->server_name);
|
|
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
|
|
}
|
|
|
|
static unsigned char process_unmount(struct fuse_client * c)
|
|
{
|
|
struct afp_server_unmount_request * req;
|
|
struct afp_server * s;
|
|
struct afp_volume * v;
|
|
int j=0;
|
|
|
|
req=(void *) c->incoming_string+1;
|
|
|
|
for (s=get_server_base();s;s=s->next) {
|
|
for (j=0;j<s->num_volumes;j++) {
|
|
v=&s->volumes[j];
|
|
if (strcmp(v->mountpoint,req->mountpoint)==0) {
|
|
goto found;
|
|
}
|
|
|
|
}
|
|
}
|
|
goto notfound;
|
|
found:
|
|
if (v->mounted != AFP_VOLUME_MOUNTED ) {
|
|
log_for_client((void *) c,AFPFSD,LOG_NOTICE,
|
|
"%s was not mounted\n",v->mountpoint);
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
}
|
|
|
|
afp_unmount_volume(v);
|
|
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
notfound:
|
|
log_for_client((void *)c,AFPFSD,LOG_WARNING,
|
|
"afpfs-ng doesn't have anything mounted on %s.\n",req->mountpoint);
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
|
|
|
|
}
|
|
|
|
static unsigned char process_ping(struct fuse_client * c)
|
|
{
|
|
log_for_client((void *)c,AFPFSD,LOG_INFO,
|
|
"Ping!\n");
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
}
|
|
|
|
static unsigned char process_exit(struct fuse_client * c)
|
|
{
|
|
log_for_client((void *)c,AFPFSD,LOG_INFO,
|
|
"Exiting\n");
|
|
trigger_exit();
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
}
|
|
|
|
static unsigned char process_status(struct fuse_client * c)
|
|
{
|
|
struct afp_server * s;
|
|
|
|
char text[40960];
|
|
int len=40960;
|
|
|
|
if ((c->incoming_size + 1)< sizeof(struct afp_server_status_request))
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
|
|
afp_status_header(text,&len);
|
|
|
|
log_for_client((void *)c,AFPFSD,LOG_INFO,text);
|
|
|
|
s=get_server_base();
|
|
|
|
for (s=get_server_base();s;s=s->next) {
|
|
afp_status_server(s,text,&len);
|
|
log_for_client((void *)c,AFPFSD,LOG_DEBUG,text);
|
|
}
|
|
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
|
|
}
|
|
|
|
static int process_mount(struct fuse_client * c)
|
|
{
|
|
struct afp_server_mount_request * req;
|
|
struct afp_server * s=NULL;
|
|
struct afp_volume * volume;
|
|
struct afp_connection_request conn_req;
|
|
int ret;
|
|
struct stat lstat;
|
|
|
|
if ((c->incoming_size-1) < sizeof(struct afp_server_mount_request))
|
|
goto error;
|
|
|
|
req=(void *) c->incoming_string+1;
|
|
|
|
/* Todo should check the existance and perms of the mount point */
|
|
|
|
if ((ret=access(req->mountpoint,X_OK))!=0) {
|
|
log_for_client((void *)c,AFPFSD,LOG_DEBUG,
|
|
"Incorrect permissions on mountpoint %s: %s\n",
|
|
req->mountpoint, strerror(errno));
|
|
|
|
goto error;
|
|
}
|
|
|
|
if (stat(FUSE_DEVICE,&lstat)) {
|
|
printf("Could not find %s\n",FUSE_DEVICE);
|
|
goto error;
|
|
}
|
|
|
|
if (access(FUSE_DEVICE,R_OK | W_OK )!=0) {
|
|
log_for_client((void *)c, AFPFSD,LOG_NOTICE,
|
|
"Incorrect permissions on %s, mode of device"
|
|
" is %o, uid/gid is %d/%d. But your effective "
|
|
"uid/gid is %d/%d\n",
|
|
FUSE_DEVICE,lstat.st_mode, lstat.st_uid,
|
|
lstat.st_gid,
|
|
geteuid(),getegid());
|
|
goto error;
|
|
}
|
|
|
|
log_for_client((void *)c,AFPFSD,LOG_NOTICE,
|
|
"Mounting %s from %s on %s\n",
|
|
(char *) req->url.volumename,
|
|
(char *) req->url.servername,
|
|
req->mountpoint);
|
|
|
|
memset(&conn_req,0,sizeof(conn_req));
|
|
|
|
conn_req.url=req->url;
|
|
conn_req.uam_mask=req->uam_mask;
|
|
|
|
if ((s=afp_server_full_connect(c,&conn_req))==NULL) {
|
|
signal_main_thread();
|
|
goto error;
|
|
}
|
|
|
|
if ((volume=mount_volume(c,s,req->url.volumename,
|
|
req->url.volpassword))==NULL) {
|
|
goto error;
|
|
}
|
|
|
|
volume->extra_flags|=req->volume_options;
|
|
|
|
volume->mapping=req->map;
|
|
afp_detect_mapping(volume);
|
|
|
|
snprintf(volume->mountpoint,255, "%s", req->mountpoint);
|
|
|
|
/* Create the new thread and block until we get an answer back */
|
|
{
|
|
pthread_mutex_t mutex;
|
|
struct timespec ts;
|
|
struct timeval tv;
|
|
int ret;
|
|
struct start_fuse_thread_arg arg;
|
|
memset(&arg,0,sizeof(arg));
|
|
arg.client = c;
|
|
arg.volume = volume;
|
|
arg.wait = 1;
|
|
arg.changeuid=req->changeuid;
|
|
|
|
gettimeofday(&tv,NULL);
|
|
ts.tv_sec=tv.tv_sec;
|
|
ts.tv_sec+=5;
|
|
ts.tv_nsec=tv.tv_usec*1000;
|
|
pthread_mutex_init(&mutex,NULL);
|
|
pthread_cond_init(&volume->startup_condition_cond,NULL);
|
|
|
|
/* Kickoff a thread to see how quickly it exits. If
|
|
* it exits quickly, we have an error and it failed. */
|
|
|
|
pthread_create(&volume->thread,NULL,start_fuse_thread,&arg);
|
|
|
|
if (arg.wait) ret = pthread_cond_timedwait(
|
|
&volume->startup_condition_cond,&mutex,&ts);
|
|
|
|
report_fuse_errors(c);
|
|
|
|
switch (arg.fuse_result) {
|
|
case 0:
|
|
if (volume->mounted==AFP_VOLUME_UNMOUNTED) {
|
|
/* Try and discover why */
|
|
switch(arg.fuse_errno) {
|
|
case ENOENT:
|
|
log_for_client((void *)c,AFPFSD,LOG_ERR,
|
|
"Permission denied, maybe a problem with the fuse device or mountpoint?\n");
|
|
break;
|
|
default:
|
|
log_for_client((void *)c,AFPFSD,LOG_ERR,
|
|
"Mounting of volume %s from server %s failed.\n",
|
|
volume->volume_name_printable,
|
|
volume->server->server_name_printable);
|
|
}
|
|
goto error;
|
|
} else {
|
|
log_for_client((void *)c,AFPFSD,LOG_NOTICE,
|
|
"Mounting of volume %s from server %s succeeded.\n",
|
|
volume->volume_name_printable,
|
|
volume->server->server_name_printable);
|
|
return 0;
|
|
}
|
|
break;
|
|
case ETIMEDOUT:
|
|
log_for_client((void *)c,AFPFSD,LOG_NOTICE,
|
|
"Still trying.\n");
|
|
return 0;
|
|
break;
|
|
default:
|
|
volume->mounted=AFP_VOLUME_UNMOUNTED;
|
|
log_for_client((void *)c,AFPFSD,LOG_NOTICE,
|
|
"Unknown error %d, %d.\n",
|
|
arg.fuse_result,arg.fuse_errno);
|
|
goto error;
|
|
}
|
|
|
|
}
|
|
return AFP_SERVER_RESULT_OKAY;
|
|
error:
|
|
if ((s) && (!something_is_mounted(s))) {
|
|
afp_server_remove(s);
|
|
}
|
|
signal_main_thread();
|
|
return AFP_SERVER_RESULT_ERROR;
|
|
}
|
|
|
|
|
|
static void * process_command_thread(void * other)
|
|
{
|
|
|
|
struct fuse_client * c = other;
|
|
int ret=0;
|
|
char tosend[sizeof(struct afp_server_response) + MAX_CLIENT_RESPONSE];
|
|
struct afp_server_response response;
|
|
|
|
|
|
switch(c->incoming_string[0]) {
|
|
case AFP_SERVER_COMMAND_MOUNT:
|
|
ret=process_mount(c);
|
|
break;
|
|
case AFP_SERVER_COMMAND_STATUS:
|
|
ret=process_status(c);
|
|
break;
|
|
case AFP_SERVER_COMMAND_UNMOUNT:
|
|
ret=process_unmount(c);
|
|
break;
|
|
case AFP_SERVER_COMMAND_SUSPEND:
|
|
ret=process_suspend(c);
|
|
break;
|
|
case AFP_SERVER_COMMAND_RESUME:
|
|
ret=process_resume(c);
|
|
break;
|
|
case AFP_SERVER_COMMAND_PING:
|
|
ret=process_ping(c);
|
|
break;
|
|
case AFP_SERVER_COMMAND_EXIT:
|
|
ret=process_exit(c);
|
|
break;
|
|
default:
|
|
log_for_client((void *)c,AFPFSD,LOG_ERR,"Unknown command\n");
|
|
}
|
|
/* Send response */
|
|
response.result=ret;
|
|
response.len=strlen(c->client_string);
|
|
|
|
bcopy(&response,tosend,sizeof(response));
|
|
bcopy(c->client_string,tosend+sizeof(response),response.len);
|
|
ret=write(c->fd,tosend,sizeof(response)+response.len);
|
|
if (ret<0) {
|
|
perror("Writing");
|
|
}
|
|
|
|
if ((!c) || (c->fd==0)) return NULL;
|
|
rm_fd_and_signal(c->fd);
|
|
close(c->fd);
|
|
remove_client(c);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
static int process_command(struct fuse_client * c)
|
|
{
|
|
int ret;
|
|
int fd;
|
|
|
|
ret=read(c->fd,&c->incoming_string,AFP_CLIENT_INCOMING_BUF);
|
|
|
|
if (ret<=0) {
|
|
perror("reading");
|
|
goto out;
|
|
}
|
|
c->incoming_size=ret;
|
|
|
|
pthread_t thread;
|
|
pthread_create(&thread,NULL,process_command_thread,c);
|
|
return 0;
|
|
out:
|
|
fd=c->fd;
|
|
c->fd=0;
|
|
remove_client(c);
|
|
close(fd);
|
|
rm_fd_and_signal(fd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct afp_volume * mount_volume(struct fuse_client * c,
|
|
struct afp_server * server, char * volname, char * volpassword)
|
|
{
|
|
struct afp_volume * using_volume;
|
|
|
|
using_volume = find_volume_by_name(server,volname);
|
|
|
|
if (!using_volume) {
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,
|
|
"Volume %s does not exist on server %s.\n",volname,
|
|
server->server_name_printable);
|
|
if (server->num_volumes) {
|
|
char names[1024];
|
|
afp_list_volnames(server,names,1024);
|
|
log_for_client((void *)c,AFPFSD,LOG_ERR,
|
|
"Choose from: %s\n",names);
|
|
}
|
|
goto error;
|
|
}
|
|
|
|
if (using_volume->mounted==AFP_VOLUME_MOUNTED) {
|
|
log_for_client((void *)c,AFPFSD,LOG_ERR,
|
|
"Volume %s is already mounted on %s\n",volname,
|
|
using_volume->mountpoint);
|
|
goto error;
|
|
}
|
|
|
|
if (using_volume->flags & HasPassword) {
|
|
bcopy(volpassword,using_volume->volpassword,AFP_VOLPASS_LEN);
|
|
if (strlen(volpassword)<1) {
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,"Volume password needed\n");
|
|
goto error;
|
|
}
|
|
} else memset(using_volume->volpassword,0,AFP_VOLPASS_LEN);
|
|
|
|
if (volopen(c,using_volume)) {
|
|
log_for_client((void *) c,AFPFSD,LOG_ERR,"Could not mount volume %s\n",volname);
|
|
goto error;
|
|
}
|
|
|
|
using_volume->server=server;
|
|
|
|
return using_volume;
|
|
error:
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct libafpclient client = {
|
|
.unmount_volume = fuse_unmount_volume,
|
|
.log_for_client = fuse_log_for_client,
|
|
.forced_ending_hook =fuse_forced_ending_hook,
|
|
.scan_extra_fds = fuse_scan_extra_fds};
|
|
|
|
int fuse_register_afpclient(void)
|
|
{
|
|
libafpclient_register(&client);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|