afpfs-ng-mac/lib/resource.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;
}