mirror of
https://github.com/mabam/afpfs-ng-mac.git
synced 2025-01-17 13:29:53 +00:00
683 lines
14 KiB
C
683 lines
14 KiB
C
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include "afpfs-ng/afp.h"
|
|
#include "resource.h"
|
|
#include "lowlevel.h"
|
|
#include "did.h"
|
|
#include "afpfs-ng/midlevel.h"
|
|
#include "lib/forklist.h"
|
|
|
|
#define appledouble ".AppleDouble"
|
|
#define finderinfo_string ".finderinfo"
|
|
#define comment_string ".comment"
|
|
#define servericon_string "/.servericon"
|
|
|
|
#define min(a,b) (((a)<(b)) ? (a) : (b))
|
|
|
|
static unsigned int extra_translate(
|
|
struct afp_volume * volume, const char * path,
|
|
char ** newpath_p)
|
|
{
|
|
|
|
char * p;
|
|
char * newpath;
|
|
*newpath_p=NULL;
|
|
if (~volume->extra_flags & VOLUME_EXTRA_FLAGS_SHOW_APPLEDOUBLE)
|
|
return 0;
|
|
|
|
if (strcmp(path,servericon_string)==0)
|
|
return AFP_META_SERVER_ICON;
|
|
|
|
if (strlen(path)<(strlen(appledouble)+1))
|
|
return 0;
|
|
|
|
if ((p=strrchr(path,'/'))==NULL)
|
|
return 0;
|
|
|
|
p+=1;
|
|
|
|
if (strcmp(p,appledouble)==0) {
|
|
|
|
/* Okay, all we have is /foo/.AppleDouble */
|
|
/* Make a copy, but not the last .AppleDouble */
|
|
newpath = malloc(strlen(path));
|
|
memset(newpath,0,strlen(path));
|
|
if (strlen(path)==strlen(appledouble)+1)
|
|
newpath[0]='/';
|
|
else
|
|
memcpy(newpath,path,strlen(path)-(strlen(appledouble)+1));
|
|
*newpath_p=newpath;
|
|
|
|
return AFP_META_APPLEDOUBLE;
|
|
}
|
|
|
|
if ((p=strstr(path,appledouble))) {
|
|
/* So we have /foo/.AppleDouble/blah. */
|
|
newpath = malloc(strlen(path));
|
|
memset(newpath,0,strlen(path));
|
|
memcpy(newpath,path,p-path);
|
|
p+=strlen(appledouble);
|
|
strcat(newpath,p+1);
|
|
*newpath_p=newpath;
|
|
|
|
/* If the ending is .finderinfo, tag it */
|
|
|
|
|
|
if (strlen(newpath)>strlen(finderinfo_string)) {
|
|
p=newpath+strlen(newpath)-strlen(finderinfo_string);
|
|
if (strcmp(p,finderinfo_string)==0) {
|
|
*p='\0';
|
|
return AFP_META_FINDERINFO;
|
|
}
|
|
}
|
|
|
|
if (strlen(newpath)>strlen(comment_string)) {
|
|
p=newpath+strlen(newpath)-strlen(comment_string);
|
|
if (strcmp(p,comment_string)==0) {
|
|
*p='\0';
|
|
return AFP_META_COMMENT;
|
|
}
|
|
}
|
|
|
|
return AFP_META_RESOURCE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ensure_dt_opened(struct afp_volume * volume)
|
|
{
|
|
|
|
if (volume->dtrefnum>0) return 0;
|
|
|
|
return (afp_opendt(volume,&volume->dtrefnum));
|
|
|
|
}
|
|
|
|
static int get_comment_size(struct afp_volume * volume, const char * basename,
|
|
unsigned int did)
|
|
{
|
|
struct afp_comment comment;
|
|
int ret=0;
|
|
int size=1024;
|
|
|
|
if ((comment.data = malloc(size))==NULL)
|
|
return -1;
|
|
comment.maxsize=size;
|
|
comment.size=0;
|
|
if (volume->dtrefnum==0)
|
|
if (afp_opendt(volume,&volume->dtrefnum) <0) {
|
|
ret=-EIO;
|
|
goto out;
|
|
}
|
|
|
|
ret=(afp_getcomment(volume,did, basename, &comment));
|
|
switch(ret) {
|
|
case kFPAccessDenied:
|
|
ret=-EACCES;
|
|
break;
|
|
case kFPMiscErr:
|
|
case kFPParamErr:
|
|
ret=-EIO;
|
|
break;
|
|
case kFPItemNotFound:
|
|
case kFPObjectNotFound:
|
|
ret=-ENOENT;
|
|
break;
|
|
case kFPNoErr:
|
|
ret=comment.size;
|
|
default:
|
|
break;
|
|
}
|
|
out:
|
|
free(comment.data);
|
|
return ret;
|
|
}
|
|
|
|
int appledouble_creat(struct afp_volume * volume, const char * path, mode_t mode)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
resource = extra_translate(volume, path, &newpath);
|
|
switch(resource) {
|
|
case AFP_META_RESOURCE:
|
|
free(newpath);
|
|
return 1;
|
|
case AFP_META_APPLEDOUBLE:
|
|
free(newpath);
|
|
return -EBADF;
|
|
case AFP_META_SERVER_ICON:
|
|
return -EPERM;
|
|
return 1;
|
|
case AFP_META_FINDERINFO:
|
|
free(newpath);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int appledouble_chmod(struct afp_volume * volume, const char * path, mode_t mode)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* There's no resource you can chmod */
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
}
|
|
|
|
int appledouble_unlink(struct afp_volume * volume, const char *path)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* There's no resource you can unlink */
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
}
|
|
|
|
int appledouble_close(struct afp_volume * volume, struct afp_file_info * fp)
|
|
{
|
|
switch(fp->resource) {
|
|
case AFP_META_RESOURCE:
|
|
switch(afp_closefork(volume,fp->forkid)) {
|
|
case kFPNoErr:
|
|
break;
|
|
default:
|
|
case kFPParamErr:
|
|
case kFPMiscErr:
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
case AFP_META_APPLEDOUBLE:
|
|
return -EBADF;
|
|
case AFP_META_SERVER_ICON:
|
|
return 1;
|
|
case AFP_META_FINDERINFO:
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int appledouble_write(struct afp_volume * volume, struct afp_file_info *fp,
|
|
const char *data, size_t size, off_t offset, size_t *totalwritten)
|
|
{
|
|
int ret;
|
|
int towrite=size;
|
|
struct afp_file_info fp2;
|
|
|
|
switch(fp->resource) {
|
|
case AFP_META_RESOURCE:
|
|
ret=ll_write(volume,data,size,offset,fp,totalwritten);
|
|
return ret;
|
|
case AFP_META_APPLEDOUBLE:
|
|
return -EBADF;
|
|
case AFP_META_FINDERINFO:
|
|
if (offset>=32) return -EINVAL;
|
|
if (towrite>(32-offset)) towrite=32-offset;
|
|
|
|
/* Get the previous finderinfo */
|
|
ret=ll_get_directory_entry(volume,fp->basename,fp->did,
|
|
kFPFinderInfoBit,kFPFinderInfoBit,&fp2);
|
|
if (ret<0) return ret;
|
|
|
|
/* Copy in only the parts we got */
|
|
memcpy(fp->finderinfo+offset,data,towrite);
|
|
|
|
ret=afp_setfiledirparms(volume,
|
|
fp->did,fp->basename,
|
|
kFPFinderInfoBit, fp);
|
|
switch(ret) {
|
|
case kFPNoErr:
|
|
break;
|
|
case kFPAccessDenied:
|
|
return -EACCES;
|
|
case kFPObjectNotFound:
|
|
return -ENOENT;
|
|
case kFPBitmapErr:
|
|
case kFPMiscErr:
|
|
case kFPObjectTypeErr:
|
|
case kFPParamErr:
|
|
default:
|
|
break;
|
|
|
|
}
|
|
*totalwritten=towrite;
|
|
|
|
return 1;
|
|
case AFP_META_COMMENT:
|
|
switch(afp_addcomment(volume, fp->did,fp->basename,
|
|
(char *)data,(uint64_t *) totalwritten)) {
|
|
case kFPAccessDenied:
|
|
return -EACCES;
|
|
case kFPObjectNotFound:
|
|
return -ENOENT;
|
|
case kFPNoErr:
|
|
*totalwritten=size;
|
|
return 1;
|
|
case kFPMiscErr:
|
|
default:
|
|
return -EIO;
|
|
}
|
|
case AFP_META_SERVER_ICON:
|
|
return -EPERM;
|
|
|
|
}
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
int appledouble_read(struct afp_volume * volume, struct afp_file_info *fp,
|
|
char * buf, size_t size, off_t offset, size_t * amount_read,
|
|
int * eof)
|
|
{
|
|
int tocopy;
|
|
int ret;
|
|
struct afp_comment comment;
|
|
*amount_read=0;
|
|
*eof=0;
|
|
|
|
comment.data=malloc(size);
|
|
comment.maxsize=size;
|
|
|
|
switch(fp->resource) {
|
|
case AFP_META_RESOURCE:
|
|
ret=ll_read(volume,buf,size,offset,fp,eof);
|
|
return ret;
|
|
case AFP_META_APPLEDOUBLE:
|
|
return -EBADF;
|
|
case AFP_META_FINDERINFO:
|
|
if (offset>32) return -EFAULT;
|
|
ret=ll_get_directory_entry(volume,fp->basename,fp->did,
|
|
kFPFinderInfoBit,kFPFinderInfoBit,fp);
|
|
if (ret<0) return ret;
|
|
tocopy=min(size,(32-offset));
|
|
memcpy(buf+offset,fp->finderinfo,tocopy);
|
|
if (offset+tocopy==32) *eof=1;
|
|
*amount_read=tocopy;
|
|
case AFP_META_COMMENT:
|
|
if (fp->eof) ret=1; else
|
|
switch(afp_getcomment(volume,fp->did, fp->basename, &comment)) {
|
|
case kFPAccessDenied:
|
|
ret=-EACCES;
|
|
break;
|
|
case kFPMiscErr:
|
|
case kFPParamErr:
|
|
ret=-EIO;
|
|
break;
|
|
case kFPItemNotFound:
|
|
case kFPObjectNotFound:
|
|
ret=-ENOENT;
|
|
break;
|
|
case kFPNoErr:
|
|
memcpy(buf,comment.data,comment.size);
|
|
*amount_read =comment.size;
|
|
ret=1;
|
|
*eof=1;
|
|
fp->eof=1;
|
|
break;
|
|
default:
|
|
ret = -ENOENT;
|
|
break;
|
|
}
|
|
free(comment.data);
|
|
return ret;
|
|
case AFP_META_SERVER_ICON:
|
|
if (offset>256) return -EFAULT;
|
|
tocopy=min(size,(256-offset));
|
|
memcpy(buf+offset,volume->server->icon,tocopy);
|
|
*eof=1;
|
|
fp->eof=1;
|
|
*amount_read=tocopy;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int appledouble_truncate(struct afp_volume * volume, const char * path, int offset)
|
|
{
|
|
|
|
char * newpath;
|
|
int resource = extra_translate(volume, path, &newpath);
|
|
struct afp_file_info fp;
|
|
int ret;
|
|
unsigned int dirid;
|
|
char basename[AFP_MAX_PATH];
|
|
|
|
switch(resource) {
|
|
case AFP_META_RESOURCE:
|
|
|
|
get_dirid(volume,newpath,basename,&dirid);
|
|
|
|
ret=afp_openfork(volume,1,dirid,O_WRONLY,
|
|
basename,&fp);
|
|
|
|
ret=ll_zero_file(volume,fp.forkid,0);
|
|
if (ret<0) {
|
|
afp_closefork(volume,fp.forkid);
|
|
remove_opened_fork(volume,&fp);
|
|
free(newpath);
|
|
return ret;
|
|
}
|
|
afp_closefork(volume,fp.forkid);
|
|
remove_opened_fork(volume,&fp);
|
|
|
|
return 1;
|
|
case AFP_META_APPLEDOUBLE:
|
|
free(newpath);
|
|
return -EISDIR;
|
|
case AFP_META_FINDERINFO:
|
|
free(newpath);
|
|
return 1;
|
|
case AFP_META_COMMENT:
|
|
free(newpath);
|
|
return 1;
|
|
case AFP_META_SERVER_ICON:
|
|
free(newpath);
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int appledouble_open(struct afp_volume * volume, const char * path, int flags,
|
|
struct afp_file_info *fp)
|
|
{
|
|
char * newpath;
|
|
int ret;
|
|
|
|
switch((fp->resource = extra_translate(volume, path, &newpath))) {
|
|
case AFP_META_RESOURCE:
|
|
if (get_dirid(volume,newpath,fp->basename,&fp->did)<0) {
|
|
ret=-ENOENT;
|
|
goto error;
|
|
}
|
|
ret=ll_open(volume,newpath,flags,fp);
|
|
free(newpath);
|
|
if (ret<0) return ret;
|
|
return 1;
|
|
case AFP_META_APPLEDOUBLE:
|
|
free(newpath);
|
|
return -EISDIR;
|
|
case AFP_META_FINDERINFO:
|
|
if (get_dirid(volume,newpath,fp->basename,&fp->did)<0)
|
|
return -ENOENT;
|
|
free(newpath);
|
|
return 1;
|
|
case AFP_META_COMMENT:
|
|
if (get_dirid(volume,newpath,fp->basename,&fp->did)<0) {
|
|
ret=-ENOENT;
|
|
goto error;
|
|
}
|
|
if (volume->dtrefnum==0) {
|
|
switch(afp_opendt(volume,&volume->dtrefnum)) {
|
|
case kFPParamErr:
|
|
case kFPMiscErr:
|
|
free(newpath);
|
|
return -EIO;
|
|
case kFPNoErr:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
free(newpath);
|
|
return 1;
|
|
case AFP_META_SERVER_ICON:
|
|
free(newpath);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
error:
|
|
free(newpath);
|
|
return ret;
|
|
|
|
}
|
|
|
|
int appledouble_getattr(struct afp_volume * volume,
|
|
const char * path, struct stat *stbuf)
|
|
{
|
|
unsigned int resource;
|
|
char * newpath;
|
|
int ret;
|
|
|
|
resource = extra_translate(volume, path, &newpath);
|
|
switch(resource) {
|
|
case AFP_META_RESOURCE:
|
|
ll_getattr(volume,newpath,stbuf,1);
|
|
goto okay;
|
|
case AFP_META_APPLEDOUBLE:
|
|
stbuf->st_mode = 0700 | S_IFDIR;
|
|
goto okay;
|
|
case AFP_META_FINDERINFO:
|
|
ll_getattr(volume,newpath,stbuf,0);
|
|
stbuf->st_mode |= S_IFREG;
|
|
stbuf->st_size=32;
|
|
goto okay;
|
|
case AFP_META_COMMENT: {
|
|
unsigned int did;
|
|
char basename[AFP_MAX_PATH];
|
|
|
|
ret=ll_getattr(volume,newpath,stbuf,0);
|
|
if (ret<0)
|
|
goto error;
|
|
|
|
ret=get_dirid(volume,newpath,basename,&did);
|
|
if (ret<0)
|
|
goto error;
|
|
|
|
ret=get_comment_size(volume,basename,did);
|
|
if (ret<0)
|
|
goto error;
|
|
|
|
stbuf->st_mode |= S_IFREG;
|
|
stbuf->st_size=ret;
|
|
goto okay;
|
|
}
|
|
case AFP_META_SERVER_ICON:
|
|
stbuf->st_mode = S_IFREG | 0444;
|
|
stbuf->st_size=256;
|
|
goto okay;
|
|
}
|
|
return 0;
|
|
okay:
|
|
free(newpath);
|
|
return 1;
|
|
error:
|
|
free(newpath);
|
|
return ret;
|
|
|
|
}
|
|
|
|
static void remove_fp(struct afp_file_info **base,struct afp_file_info * toremove)
|
|
{
|
|
struct afp_file_info *fp, *prev=NULL;
|
|
|
|
for (fp=*base;fp;fp=fp->next)
|
|
if (fp==toremove) {
|
|
if (prev==NULL) {
|
|
*base=fp->next;;
|
|
free(fp);
|
|
prev=NULL;
|
|
} else {
|
|
prev->next=fp->next;
|
|
prev=fp;
|
|
free(fp);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int add_fp(struct afp_file_info **newchain, struct afp_file_info *fp,
|
|
char * suffix, unsigned int size)
|
|
{
|
|
struct afp_file_info * newfp;
|
|
newfp=malloc(sizeof(struct afp_file_info));
|
|
if(!newfp) {
|
|
return 1;
|
|
}
|
|
memcpy(newfp,fp,sizeof(struct afp_file_info));
|
|
strcat(newfp->name,suffix);
|
|
newfp->resourcesize=size;
|
|
newfp->unixprivs.permissions|=S_IFREG;
|
|
newfp->next=*newchain;
|
|
*newchain=newfp;
|
|
return 0;
|
|
}
|
|
|
|
int appledouble_readdir(struct afp_volume * volume,
|
|
const char *path, struct afp_file_info **base)
|
|
{
|
|
unsigned int resource;
|
|
char * newpath;
|
|
|
|
resource = extra_translate(volume, path, &newpath);
|
|
|
|
switch(resource) {
|
|
case 0:
|
|
return 0;
|
|
break;
|
|
case AFP_META_APPLEDOUBLE: {
|
|
struct afp_file_info *fp, *newchain=NULL, *last=NULL;
|
|
ll_readdir(volume, newpath,base,1);
|
|
|
|
/* Add .finderinfo files */
|
|
for (fp=*base;fp;fp=fp->next) {
|
|
add_fp(&newchain,fp,finderinfo_string,32);
|
|
|
|
/* Add comments if it has a size > 0 */
|
|
if (ensure_dt_opened(volume)==0) {
|
|
int size=get_comment_size(volume,
|
|
fp->name,fp->did);
|
|
|
|
if (size>0)
|
|
add_fp(&newchain,fp,comment_string,32);
|
|
}
|
|
|
|
if (fp->unixprivs.permissions & S_IFREG) {
|
|
if (fp->resourcesize==0) {
|
|
remove_fp(base,fp);
|
|
}
|
|
} else {
|
|
remove_fp(base,fp);
|
|
}
|
|
last=fp;
|
|
}
|
|
if ((newchain) && (last)) {
|
|
last->next=newchain;
|
|
}
|
|
|
|
free(newpath);
|
|
return 1;
|
|
}
|
|
break;
|
|
case AFP_META_RESOURCE:
|
|
case AFP_META_SERVER_ICON:
|
|
case AFP_META_COMMENT:
|
|
free(newpath);
|
|
return -ENOTDIR;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int appledouble_mkdir(struct afp_volume * volume, const char * path, mode_t mode)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't mkdir */
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
int appledouble_readlink(struct afp_volume * volume, const char * path,
|
|
char * buf, size_t size)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't readlink*/
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
int appledouble_rmdir(struct afp_volume * volume, const char * path)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't rmdir*/
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
int appledouble_chown(struct afp_volume * volume, const char * path,
|
|
uid_t uid, gid_t gid)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't chown*/
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
int appledouble_utime(struct afp_volume * volume, const char * path,
|
|
struct utimbuf * timebuf)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't utime*/
|
|
resource = extra_translate(volume, path, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
}
|
|
|
|
int appledouble_symlink(struct afp_volume *vol, const char *path1, const char *path2)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't symlink*/
|
|
resource = extra_translate(vol, path1, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
}
|
|
|
|
int appledouble_rename(struct afp_volume * volume, const char * path_from,
|
|
const char * path_to)
|
|
{
|
|
int resource;
|
|
char * newpath;
|
|
|
|
/* You can't rename*/
|
|
resource = extra_translate(volume, path_to, &newpath);
|
|
free(newpath);
|
|
if (resource==0) return 0;
|
|
return -EPERM;
|
|
}
|
|
|