int ml_symlink(struct afp_volume *vol, const char * path1, const char * path2) { int ret; struct afp_file_info fp; uint64_t written; int rc; unsigned int dirid2; char basename2[AFP_MAX_PATH]; char converted_path1[AFP_MAX_PATH]; char converted_path2[AFP_MAX_PATH]; if (vol->server->using_version->av_number<30) { /* No symlinks for AFP 2.x. */ ret=ENOSYS; goto error; } /* Yes, you can create symlinks for AFP >=30. Tested with 10.3.2 */ if (convert_path_to_afp(vol->server->path_encoding, converted_path1,(char *) path1,AFP_MAX_PATH)) return -EINVAL; if (convert_path_to_afp(vol->server->path_encoding, converted_path2,(char *) path2,AFP_MAX_PATH)) return -EINVAL; if (volume_is_readonly(vol)) return -EACCES; ret=appledouble_symlink(vol,path1,path2); if (ret<0) return ret; if (ret==1) return 0; get_dirid(vol,converted_path2,basename2,&dirid2 ); /* 1. create the file */ rc=afp_createfile(vol,kFPHardCreate,dirid2,basename2); switch (rc) { case kFPAccessDenied: ret=EACCES; goto error; case kFPDiskFull: ret=ENOSPC; goto error; case kFPObjectExists: ret=EEXIST; goto error; case kFPObjectNotFound: ret=ENOENT; goto error; case kFPFileBusy: case kFPVolLocked: ret=EBUSY; goto error; case kFPNoErr: ret=0; break; default: case kFPParamErr: case kFPMiscErr: ret=EIO; goto error; } /* Open the fork */ rc=afp_openfork(vol,0, dirid2, AFP_OPENFORK_ALLOWWRITE|AFP_OPENFORK_ALLOWREAD, basename2,&fp); switch (ret) { case kFPAccessDenied: ret=EACCES; goto error; case kFPObjectNotFound: ret=ENOENT; goto error; case kFPObjectLocked: ret=EROFS; goto error; case kFPObjectTypeErr: ret=EISDIR; goto error; case kFPParamErr: ret=EACCES; goto error; case kFPTooManyFilesOpen: ret=EMFILE; goto error; case 0: ret=0; break; case kFPVolLocked: case kFPDenyConflict: case kFPMiscErr: case kFPBitmapErr: case -1: default: ret=EFAULT; goto error; } add_opened_fork(vol, &fp); /* Write the name of the file to it */ rc=afp_writeext(vol,fp.forkid,0,strlen(converted_path1), converted_path1,&written); switch(afp_closefork(vol,fp.forkid)) { case kFPNoErr: break; default: case kFPParamErr: case kFPMiscErr: ret=EIO; goto error; } remove_opened_fork(vol, &fp); /* And now for the undocumented magic */ memset(&fp.finderinfo,0,32); fp.finderinfo[0]='s'; fp.finderinfo[1]='l'; fp.finderinfo[2]='n'; fp.finderinfo[3]='k'; fp.finderinfo[4]='r'; fp.finderinfo[5]='h'; fp.finderinfo[6]='a'; fp.finderinfo[7]='p'; rc=afp_setfiledirparms(vol,dirid2,basename2, kFPFinderInfoBit, &fp); switch (rc) { case kFPAccessDenied: ret=EPERM; goto error; case kFPBitmapErr: /* This is the case where it isn't supported */ ret=ENOSYS; goto error; case kFPObjectNotFound: ret=ENOENT; goto error; case 0: ret=0; break; case kFPMiscErr: case kFPObjectTypeErr: case kFPParamErr: default: ret=EIO; goto error; } error: return -ret; };
static int set_unixprivs(struct afp_volume * vol, unsigned int dirid, const char * basename, struct afp_file_info * fp) { #define TOCHECK_BITS \ (S_IRUSR |S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \ S_IROTH | S_IWOTH | S_IXOTH ) int ret=0, rc; int rc2; struct afp_file_info fp2; fp->unixprivs.ua_permissions=0; if (!(vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX)) return 0; if (fp->isdir) { rc=afp_setdirparms(vol, dirid,basename, kFPUnixPrivsBit, fp); } else { /* For broken netatalk servers, strip out the extra bits. */ if ((fp->unixprivs.permissions&~(AFP_CHMOD_ALLOWED_BITS_22)) && (vol->server->server_type==AFPFS_SERVER_TYPE_NETATALK) && (vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_CHMOD_KNOWN) && (vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_CHMOD_BROKEN)) fp->unixprivs.permissions&=AFP_CHMOD_ALLOWED_BITS_22; rc=afp_setfiledirparms(vol,dirid,basename, kFPUnixPrivsBit, fp); } switch (rc) { case kFPAccessDenied: ret=EPERM; break; case kFPBitmapErr: ret=ENOSYS; break; case kFPObjectNotFound: ret=ENOENT; break; case 0: ret=0; break; case kFPMiscErr: case kFPObjectTypeErr: case kFPParamErr: default: ret=EIO; break; } /* If it is netatalk, check to see if that worked. If not, * never try this bitset again. */ if ((fp->unixprivs.permissions & ~(AFP_CHMOD_ALLOWED_BITS_22)) && (!(vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_CHMOD_KNOWN)) && (vol->server->server_type==AFPFS_SERVER_TYPE_NETATALK)) { if ((rc2=get_unixprivs(vol, dirid, basename, &fp2))) return rc2; vol->extra_flags|=VOLUME_EXTRA_FLAGS_VOL_CHMOD_KNOWN; if ((fp2.unixprivs.permissions&TOCHECK_BITS)== (fp->unixprivs.permissions&TOCHECK_BITS)) { vol->extra_flags&=~VOLUME_EXTRA_FLAGS_VOL_CHMOD_BROKEN; } else { vol->extra_flags|=VOLUME_EXTRA_FLAGS_VOL_CHMOD_BROKEN; return -EFAULT; } } return -ret; }
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; unsigned int did; struct afp_file_info fp2; switch(fp->resource) { case AFP_META_RESOURCE: ret=ll_write(volume,data,size,offset, fp->forkid,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; }