afpfs-ng-mac/fuse/fuse_int.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 </usr/local/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;
}