mirror of
https://github.com/mabam/afpfs-ng-mac.git
synced 2025-01-06 01:32:09 +00:00
512 lines
11 KiB
C
512 lines
11 KiB
C
|
|
|
|
/*
|
|
|
|
fuse.c, FUSE interfaces for afpfs-ng
|
|
|
|
Copyright (C) 2006 Alex deVries <alexthepuffin@gmail.com>
|
|
|
|
Heavily modifed from the example code provided by:
|
|
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
|
|
|
|
This program can be distributed under the terms of the GNU GPL.
|
|
See the file COPYING.
|
|
*/
|
|
|
|
#define HAVE_ARCH_STRUCT_FLOCK
|
|
|
|
#define FUSE_USE_VERSION 25
|
|
|
|
|
|
#include "afpfs-ng/afp.h"
|
|
|
|
#include <fuse.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#ifdef __linux__
|
|
#include <asm/fcntl.h>
|
|
#else
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#include <utime.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "afpfs-ng/dsi.h"
|
|
#include "afpfs-ng/afp_protocol.h"
|
|
#include "afpfs-ng/codepage.h"
|
|
#include "afpfs-ng/midlevel.h"
|
|
#include "fuse_error.h"
|
|
|
|
/* Uncomment the following line to enable full debugging: */
|
|
/*
|
|
#define LOG_FUSE_EVENTS
|
|
*/
|
|
|
|
void log_fuse_event(enum loglevels loglevel, int logtype,
|
|
char *format, ...) {
|
|
#ifdef LOG_FUSE_EVENTS
|
|
va_list ap;
|
|
|
|
va_start(ap,format);
|
|
vprintf(format,ap);
|
|
va_end(ap);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
static int fuse_readlink(const char * path, char *buf, size_t size)
|
|
{
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** readlink of %s\n",path);
|
|
|
|
ret=ml_readlink(volume,path,buf,size);
|
|
|
|
if (ret==-EFAULT) {
|
|
log_for_client(NULL,AFPFSD,LOG_WARNING,
|
|
"Got some sort of internal error in afp_open for readlink\n");
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int fuse_rmdir(const char *path)
|
|
{
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** rmdir of %s\n",path);
|
|
|
|
ret=ml_rmdir(volume,path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_unlink(const char *path)
|
|
{
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** unlink of %s\n",path);
|
|
|
|
ret=ml_unlink(volume,path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|
off_t offset, struct fuse_file_info *fi)
|
|
{
|
|
(void) offset;
|
|
(void) fi;
|
|
struct afp_file_info * filebase = NULL, * p;
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** readdir of %s\n",path);
|
|
|
|
filler(buf, ".", NULL, 0);
|
|
filler(buf, "..", NULL, 0);
|
|
|
|
ret=ml_readdir(volume,path,&filebase);
|
|
|
|
if (ret) goto error;
|
|
|
|
for (p=filebase;p;p=p->next) {
|
|
filler(buf,p->name,NULL,0);
|
|
}
|
|
|
|
afp_ml_filebase_free(&filebase);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_mknod(const char *path, mode_t mode, dev_t dev)
|
|
{
|
|
int ret=0;
|
|
struct fuse_context * context = fuse_get_context();
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *) context->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** mknod of %s\n",path);
|
|
|
|
ret=ml_creat(volume,path,mode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int fuse_release(const char * path, struct fuse_file_info * fi)
|
|
{
|
|
|
|
struct afp_file_info * fp = (void *) fi->fh;
|
|
int ret=0;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** release of %s\n",path);
|
|
|
|
ret=ml_close(volume,path,fp);
|
|
|
|
if (ret<0) goto error;
|
|
|
|
return ret;
|
|
error:
|
|
free((void *) fi->fh);
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_open(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
|
|
struct afp_file_info * fp ;
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
unsigned char flags = AFP_OPENFORK_ALLOWREAD;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,
|
|
"*** Opening path %s\n",path);
|
|
|
|
ret = ml_open(volume,path,flags,&fp);
|
|
|
|
if (ret==0)
|
|
fi->fh=(unsigned long) fp;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int fuse_write(const char * path, const char *data,
|
|
size_t size, off_t offset,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
|
|
struct afp_file_info *fp = (struct afp_file_info *) fi->fh;
|
|
int ret;
|
|
struct fuse_context * context = fuse_get_context();
|
|
struct afp_volume * volume=(void *) context->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,
|
|
"*** write of from %llu for %llu\n",
|
|
(unsigned long long) offset,(unsigned long long) size);
|
|
|
|
ret=ml_write(volume,path,data,size,offset,fp,
|
|
context->uid, context->gid);
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
static int fuse_mkdir(const char * path, mode_t mode)
|
|
{
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** mkdir of %s\n",path);
|
|
|
|
ret=ml_mkdir(volume,path,mode);
|
|
|
|
return ret;
|
|
|
|
}
|
|
static int fuse_read(const char *path, char *buf, size_t size, off_t offset,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct afp_file_info * fp;
|
|
int ret=0;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
int eof;
|
|
size_t amount_read=0;
|
|
|
|
if (!fi || !fi->fh)
|
|
return -EBADF;
|
|
fp=(void *) fi->fh;
|
|
|
|
while (1) {
|
|
ret = ml_read(volume,path,buf+amount_read,size,offset,fp,&eof);
|
|
if (ret<0) goto error;
|
|
amount_read+=ret;
|
|
if (eof) goto out;
|
|
size-=ret;
|
|
if (size==0) goto out;
|
|
offset+=ret;
|
|
}
|
|
out:
|
|
return amount_read;
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_chown(const char * path, uid_t uid, gid_t gid)
|
|
{
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"** chown\n");
|
|
|
|
ret=ml_chown(volume,path,uid,gid);
|
|
|
|
if (ret==-ENOSYS) {
|
|
log_for_client(NULL,AFPFSD,LOG_WARNING,"chown unsupported\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_truncate(const char * path, off_t offset)
|
|
{
|
|
int ret=0;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,
|
|
"** truncate\n");
|
|
|
|
ret=ml_truncate(volume,path,offset);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int fuse_chmod(const char * path, mode_t mode)
|
|
{
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
int ret;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,
|
|
"** chmod %s\n",path);
|
|
ret=ml_chmod(volume,path,mode);
|
|
|
|
switch (ret) {
|
|
|
|
case -EPERM:
|
|
log_for_client(NULL,AFPFSD,LOG_DEBUG,
|
|
"You're not the owner of this file.\n");
|
|
break;
|
|
|
|
case -ENOSYS:
|
|
log_for_client(NULL,AFPFSD,LOG_WARNING,"chmod unsupported or this mode is not possible with this server\n");
|
|
break;
|
|
case -EFAULT:
|
|
log_for_client(NULL,AFPFSD,LOG_ERR,
|
|
"You're mounting from a netatalk server, and I was trying to change "
|
|
"permissions but you're setting some mode bits that aren't supported "
|
|
"by the server. This is because this netatalk server is broken. \n"
|
|
"This is because :\n"
|
|
" - you haven't set -options=unix_priv in AppleVolumes.default\n"
|
|
" - you haven't applied a patch which fixes chmod() to netatalk, or are using an \n"
|
|
" old version. See afpfs-ng docs.\n"
|
|
" - maybe both\n"
|
|
"It sucks, but I'm marking this volume as broken for 'extended' chmod modes.\n"
|
|
"Allowed bits are: %o\n", AFP_CHMOD_ALLOWED_BITS_22);
|
|
|
|
ret=0; /* Return anyway */
|
|
break;
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_utime(const char * path, struct utimbuf * timebuf)
|
|
{
|
|
|
|
int ret=0;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,
|
|
"** utime\n");
|
|
|
|
|
|
ret=ml_utime(volume,path,timebuf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void afp_destroy(void * ignore)
|
|
{
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
if (volume->mounted==AFP_VOLUME_UNMOUNTED) {
|
|
log_for_client(NULL,AFPFSD,LOG_WARNING,"Skipping unmounting of the volume %s\n",volume->volume_name_printable);
|
|
return;
|
|
}
|
|
if ((!volume) || (!volume->server)) return;
|
|
|
|
/* We're just ignoring the results since there's nothing we could
|
|
do with them anyway. */
|
|
afp_unmount_volume(volume);
|
|
|
|
}
|
|
|
|
static int fuse_symlink(const char * path1, const char * path2)
|
|
{
|
|
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
int ret;
|
|
|
|
ret=ml_symlink(volume,path1,path2);
|
|
if ((ret==-EFAULT) || (ret==-ENOSYS)) {
|
|
log_for_client(NULL,AFPFSD,LOG_WARNING,
|
|
"Got some sort of internal error in when creating symlink\n");
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
static int fuse_rename(const char * path_from, const char * path_to)
|
|
{
|
|
int ret;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
|
|
ret=ml_rename(volume,path_from, path_to);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int fuse_statfs(const char *path, struct statvfs *stat)
|
|
{
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
int ret;
|
|
|
|
ret=ml_statfs(volume,path,stat);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int fuse_getattr(const char *path, struct stat *stbuf)
|
|
{
|
|
char * c;
|
|
struct afp_volume * volume=
|
|
(struct afp_volume *)
|
|
((struct fuse_context *)(fuse_get_context()))->private_data;
|
|
int ret;
|
|
|
|
log_fuse_event(AFPFSD,LOG_DEBUG,"*** getattr of \"%s\"\n",path);
|
|
|
|
/* Oddly, we sometimes get <dir1>/<dir2>/(null) for the path */
|
|
|
|
if (!path) return -EIO;
|
|
|
|
if ((c=strstr(path,"(null)"))) {
|
|
/* We should fix this to make sure it is at the end */
|
|
if (c>path) *(c-1)='\0';
|
|
}
|
|
|
|
ret=ml_getattr(volume,path,stbuf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static struct afp_volume * global_volume;
|
|
|
|
#if FUSE_USE_VERSION < 26
|
|
static void *afp_init(void) {
|
|
#else
|
|
static void *afp_init(void * o) {
|
|
#endif
|
|
struct afp_volume * vol = global_volume;
|
|
|
|
vol->priv=(void *)((struct fuse_context *)(fuse_get_context()))->fuse;
|
|
/* Trigger the daemon that we've started */
|
|
if (vol->priv) vol->mounted=1;
|
|
pthread_cond_signal(&vol->startup_condition_cond);
|
|
return (void *) vol;
|
|
}
|
|
|
|
|
|
|
|
static struct fuse_operations afp_oper = {
|
|
.getattr =fuse_getattr,
|
|
.open = fuse_open,
|
|
.read = fuse_read,
|
|
.readdir = fuse_readdir,
|
|
.mkdir = fuse_mkdir,
|
|
.readlink = fuse_readlink,
|
|
.rmdir = fuse_rmdir,
|
|
.unlink = fuse_unlink,
|
|
.mknod = fuse_mknod,
|
|
.write = fuse_write,
|
|
.release= fuse_release,
|
|
.chmod=fuse_chmod,
|
|
.symlink=fuse_symlink,
|
|
.chown=fuse_chown,
|
|
.truncate=fuse_truncate,
|
|
.rename=fuse_rename,
|
|
.utime=fuse_utime,
|
|
.destroy=afp_destroy,
|
|
.init=afp_init,
|
|
.statfs=fuse_statfs,
|
|
};
|
|
|
|
|
|
int afp_register_fuse(int fuseargc, char *fuseargv[],struct afp_volume * vol)
|
|
{
|
|
int ret;
|
|
global_volume=vol;
|
|
|
|
fuse_capture_stderr_start();
|
|
|
|
#if FUSE_USE_VERSION < 26
|
|
ret=fuse_main(fuseargc, fuseargv, &afp_oper);
|
|
#else
|
|
ret=fuse_main(fuseargc, fuseargv, &afp_oper,(void *) vol);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|