/* fuse.c, FUSE interfaces for afpfs-ng Copyright (C) 2006 Alex deVries Heavily modifed from the example code provided by: Copyright (C) 2001-2005 Miklos Szeredi 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 #include #include #include #ifdef __linux__ #include #else #include #endif #include #include #include #include #include #include #include #include #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 //(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; }