Example #1
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;

}
Example #2
0
int ml_utime(struct afp_volume * vol, const char * path, 
	struct utimbuf * timebuf)
{

	int ret=0;
	unsigned int dirid;
	struct afp_file_info fp;
	char basename[AFP_MAX_PATH];
	char converted_path[AFP_MAX_PATH];
	int rc;

	if (volume_is_readonly(vol))
		return -EACCES;

	memset(&fp,0,sizeof(struct afp_file_info));

	fp.modification_date=timebuf->modtime;

	if (invalid_filename(vol->server,path)) 
		return -ENAMETOOLONG;

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH)) {
		return -EINVAL;
	}

	ret=appledouble_utime(vol,path,timebuf);
	if (ret<0) return ret;
	if (ret==1) return 0;

	get_dirid(vol,converted_path,basename,&dirid );

	if (is_dir(vol,dirid,basename)) {
		rc=afp_setdirparms(vol,
			dirid,basename, kFPModDateBit, &fp);
	} else {
		rc=afp_setfileparms(vol,
			dirid,basename, kFPModDateBit, &fp);
	}

	switch(rc) {
	case kFPNoErr:
		break;
	case kFPAccessDenied:
		return -EACCES;
	case kFPObjectNotFound:
		return -ENOENT;
	case kFPBitmapErr:
	case kFPMiscErr:
	case kFPObjectTypeErr:
	case kFPParamErr:
	default:
		break;

	}

	return -ret;
}
Example #3
0
int ml_rmdir(struct afp_volume * vol, const char *path)
{
	int ret,rc;
	unsigned int dirid;
	char basename[AFP_MAX_PATH];
	char converted_path[AFP_MAX_PATH];

	if (invalid_filename(vol->server,path)) 
		return -ENAMETOOLONG;

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH))
		return -EINVAL;

	if (volume_is_readonly(vol))
		return -EACCES;
	
	ret=appledouble_rmdir(vol,path);
	if (ret<0) return ret;
	if (ret==1) return 0;

	get_dirid(vol, converted_path, basename, &dirid);

	if (!is_dir(vol,dirid,basename)) return -ENOTDIR;

	rc=afp_delete(vol,dirid,basename);

	switch(rc) {
	case kFPAccessDenied:
		ret=EACCES;
		break;
	case kFPObjectLocked:
		ret=EBUSY;
		break;
	case kFPObjectNotFound:
		ret=ENOENT;
		break;
	case kFPVolLocked:
		ret=EACCES;
		break;
	case kFPDirNotEmpty:
		ret=ENOTEMPTY;
		break;
	case kFPObjectTypeErr:
	case kFPMiscErr:
	case kFPParamErr:
	case -1:
		ret=EINVAL;
		break;
	default:
		remove_did_entry(vol,converted_path);
		ret=0;
	}
	return -ret;
}
Example #4
0
int ml_mkdir(struct afp_volume * vol, const char * path, mode_t mode) 
{
	int ret,rc;
	unsigned int result_did;
	char basename[AFP_MAX_PATH];
	char converted_path[AFP_MAX_PATH];
	unsigned int dirid;

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH))
		return -EINVAL;

	if (invalid_filename(vol->server,path)) 
		return -ENAMETOOLONG;

	if (volume_is_readonly(vol))
		return -EACCES;

	ret=appledouble_mkdir(vol,path,mode);
	if (ret<0) return ret;
	if (ret==1) return 0;

	get_dirid(vol,converted_path,basename,&dirid);

	rc = afp_createdir(vol,dirid, basename,&result_did);

	switch (rc) {
	case kFPAccessDenied:
		ret = EACCES;
		break;
	case kFPDiskFull:
		ret = ENOSPC;
		break;
	case kFPObjectNotFound:
		ret = ENOENT;
		break;
	case kFPObjectExists:
		ret = EEXIST;
		break;
	case kFPVolLocked:
		ret = EBUSY;
		break;
	case kFPFlatVol:
	case kFPMiscErr:
	case kFPParamErr:
	case -1:
		ret = EFAULT;
		break;
	default:
		ret =0;
	}

	return -ret;
}
Example #5
0
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;

}
Example #6
0
int ml_open(struct afp_volume * volume, const char *path, int flags, 
	struct afp_file_info **newfp)
{

/* FIXME:  doesn't handle create properly */

	struct afp_file_info * fp ;
	int ret;
	unsigned int dirid;
	char converted_path[AFP_MAX_PATH];

	if (convert_path_to_afp(volume->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH))
		return -EINVAL;

	if (invalid_filename(volume->server,converted_path))
		return -ENAMETOOLONG;

	if (volume_is_readonly(volume) && 
		(flags & (O_WRONLY|O_RDWR|O_TRUNC|O_APPEND|O_CREAT))) 
		return -EACCES;

	if ((fp=malloc(sizeof(*fp)))==NULL) {
		return -1;
	}
	*newfp=fp;

	memset(fp,0,sizeof(*fp));

	ret=appledouble_open(volume,path,flags,fp);
	if (ret<0) return ret;
	if (ret==1) goto out;
	

	if (get_dirid(volume,converted_path,fp->basename,&dirid)<0)
		return -ENOENT;

	fp->did=dirid;

	ret=ll_open(volume,converted_path,flags,fp);

	if (ret<0) goto error;


out:
	return 0;

error:
	free(fp);
	return ret;
}
Example #7
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;
	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;
}
Example #8
0
int ml_chown(struct afp_volume * vol, const char * path, 
	uid_t uid, gid_t gid) 
{
	int ret;
	struct afp_file_info fp;
	int rc;
	unsigned int dirid;
	char basename[AFP_MAX_PATH];
	char converted_path[AFP_MAX_PATH];

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH))
		return -EINVAL;

	if (invalid_filename(vol->server,converted_path)) 
		return -ENAMETOOLONG;

	if (volume_is_readonly(vol))
		return -EACCES;

	ret=appledouble_chown(vol,path,uid,gid);
	if (ret<0) return ret;
	if (ret==1) return 0;

	/* There's no way to do this in AFP < 3.0 */
	if (~ vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX) {

		if (vol->extra_flags & VOLUME_EXTRA_FLAGS_IGNORE_UNIXPRIVS) {
			struct stat stbuf;
			/* See if the file exists */
			ret=ll_getattr(vol,path,&stbuf,0);
			return ret;
		}

		return -ENOSYS;
	};

	get_dirid(vol,converted_path,basename,&dirid );

	if ((rc=get_unixprivs(vol,
		dirid,basename, &fp)))
		return rc;

#if 0
FIXME
	set_uidgid(volume,&fp,uid,gid);
THIS IS the wrong set of returns to check...
#endif
	rc=set_unixprivs(vol, dirid, basename, &fp);

	switch(rc) {
	case -ENOSYS:
		return -ENOSYS;
	case kFPNoErr:
		break;
	case kFPAccessDenied:
		return -EACCES;
	case kFPObjectNotFound:
		return -ENOENT;
	case kFPBitmapErr:
	case kFPMiscErr:
	case kFPObjectTypeErr:
	case kFPParamErr:
	default:
		break;

	}

	return 0;
}
Example #9
0
int ml_readlink(struct afp_volume * vol, const char * path, 
	char *buf, size_t size)
{
	int rc,ret;
	struct afp_file_info fp;
	struct afp_rx_buffer buffer;
	char basename[AFP_MAX_PATH];
	char converted_path[AFP_MAX_PATH];
	unsigned int dirid;
	char link_path[AFP_MAX_PATH];

	memset(buf,0,size);
	memset(link_path,0,AFP_MAX_PATH);

	buffer.data=link_path;
	buffer.maxsize=size;
	buffer.size=0;

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH)) {
		return -EINVAL;
	}

	get_dirid(vol, converted_path, basename, &dirid);

	/* Open the fork */
	rc=afp_openfork(vol,0, dirid, 
		AFP_OPENFORK_ALLOWWRITE|AFP_OPENFORK_ALLOWREAD,
		basename,&fp);
	switch (rc) {
	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 kFPVolLocked:
	case kFPDenyConflict:
	case kFPMiscErr:
	case kFPBitmapErr:
	case 0:
		ret=0;
		break;
	case -1:
	default:
		ret=EFAULT;
		goto error;
	}

	add_opened_fork(vol, &fp);

	/* Read the name of the file from it */
	if (vol->server->using_version->av_number < 30)
		rc=afp_read(vol, fp.forkid,0,size,&buffer);
	else 
		rc=afp_readext(vol, fp.forkid,0,size,&buffer);

	switch(rc) {
	case kFPAccessDenied:
		ret=EACCES;
		goto error;
	case kFPLockErr:
		ret=EBUSY;
		goto error;
	case kFPMiscErr:
	case kFPParamErr:
		ret=EIO;
		goto error;
	case kFPEOFErr:
	case kFPNoErr:
		break;
	}

	switch(afp_closefork(vol,fp.forkid)) {
	case kFPNoErr:
		break;
	default:
	case kFPParamErr:
	case kFPMiscErr:
		ret=EIO;
		goto error;
	}

	remove_opened_fork(vol, &fp);

	/* Convert the name back precomposed UTF8 */
	convert_path_to_unix(vol->server->path_encoding,
		buf,(char *) link_path,AFP_MAX_PATH);

	return 0;
	
error:
	return -ret;
}
Example #10
0
int ml_chmod(struct afp_volume * vol, const char * path, mode_t mode) 
{
/*
chmod has an interesting story to it.  

It is known to work with Darwin 10.3.9 (AFP 3.1), 10.4.2 and 10.5.x (AFP 3.2).

chmod will not work properly in the following situations:

- AFP 2.2, this needs some more verification but I don't see how it is possible

- netatalk 2.0.3 and probably earlier:

  . netatalk will only enable it at all if you have "options=upriv" 
    set for that volume.

  . netatalk will never be able to chmod the execute bit and some others on 
    files; this is hard coded in unix.c's setfilemode() in 2.0.3.  It's like
    it has 2.2 behaviour even though it is trying to speak 3.1.

  . The only bits allowed are
        S_IRUSR |S_IWUSR | S_IRGRP | S_IWGRP |S_IROTH | S_IWOTH;
    There's probably a reason for this, I don't know what it is.

  . afpfs-ng's behaviour's the same as the Darwin client.

The right way to see if a volume supports chmod is to check the attributes
found with getvolparm or volopen, then to test chmod the first time.

*/

	int ret=0,rc;
	struct afp_file_info fp;
	unsigned int dirid;
	char basename[AFP_MAX_PATH];
	char converted_path[AFP_MAX_PATH];
	uid_t uid; gid_t gid;

	if (invalid_filename(vol->server,path)) 
		return -ENAMETOOLONG;

	if (volume_is_readonly(vol))
		return -EACCES;


	/* There's no way to do this in AFP < 3.0 */
	if (~ vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX) {

		if (vol->extra_flags & VOLUME_EXTRA_FLAGS_IGNORE_UNIXPRIVS) {
			struct stat stbuf;
			/* See if the file exists */
			ret=ll_getattr(vol,path,&stbuf,0);
			return ret;
		}

		return -ENOSYS;
	};

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH)) {
		return -EINVAL;
	}

	ret=appledouble_chmod(vol,path,mode);
	if (ret<0) return ret;
	if (ret==1) return 0;

	get_dirid(vol,converted_path,basename,&dirid );

	if ((rc=get_unixprivs(vol,
		dirid,basename, &fp))) 
		return rc;

	mode&=(~S_IFDIR);

	/* Don't bother updating it if it's already the same */
	if ((fp.unixprivs.permissions&(~S_IFDIR))==mode)
		return 0;

	/* Check to make sure that we can; some servers (at least netatalk)
	   don't report an error when you try to setfileparm when you don't
	   own the file.  */

	/* Try to guess if the operation is possible */

	uid=fp.unixprivs.uid;
	gid=fp.unixprivs.gid;
	if (translate_uidgid_to_client(vol, &uid,&gid))
		return -EIO;

	if ((gid!=getgid()) && (uid!=geteuid())) {
		return -EPERM;
	}
	
	fp.unixprivs.permissions=mode;

	rc=set_unixprivs(vol, dirid,basename, &fp);
	if (rc==-ENOSYS) {
		return -ENOSYS;
	}



	return -ret;
}
Example #11
0
int ml_creat(struct afp_volume * volume, const char *path, mode_t mode)
{
	int ret=0;
	char basename[AFP_MAX_PATH];
	unsigned int dirid;
	struct afp_file_info fp;
	int rc;
	char converted_path[AFP_MAX_PATH];

	if (convert_path_to_afp(volume->server->path_encoding,
		converted_path,(char *) path,AFP_MAX_PATH))
		return -EINVAL;

	if (volume_is_readonly(volume))
		return -EACCES;

	ret=appledouble_creat(volume,path,mode);
	if (ret<0) return ret;
	if (ret==1) return 0;
 
	if (invalid_filename(volume->server,converted_path)) 
		return -ENAMETOOLONG;

	get_dirid(volume, converted_path, basename, &dirid);

	rc=afp_createfile(volume,kFPSoftCreate, dirid,basename);
	switch(rc) {
	case kFPAccessDenied:
		ret=EACCES;
		break;
	case kFPDiskFull:
		ret=ENOSPC;
		break;
	case kFPObjectExists:
		ret=EEXIST;
		break;
	case kFPObjectNotFound:
		ret=ENOENT;
		break;
	case kFPFileBusy:
	case kFPVolLocked:
		ret=EBUSY;
		break;
	case kFPNoErr:
		ret=0;
		break;
	default:
	case kFPParamErr:
	case kFPMiscErr:
		ret=EIO;
	}

	if (ret) return -ret;

	/* If we don't support unixprivs, just exit */
	if (~ volume->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX) 
		return 0;

	/* Figure out the privs of the file we just created */
	if ((ret=get_unixprivs(volume,
		dirid,basename, &fp)))
		return rc;

	if (ret) return -ret;

	if (fp.unixprivs.permissions==mode)
		return 0;


	fp.unixprivs.ua_permissions=0;
	fp.unixprivs.permissions=mode;
	fp.isdir=0;  /* Anything you make with mknod is a file */
	/* note that we're not monkeying with the ownership here */
	
	rc=set_unixprivs(volume, dirid, basename, &fp);

	switch(rc) {
	case kFPAccessDenied:
		ret=EPERM;
		goto error;
	case kFPObjectNotFound:
		ret=ENOENT;
		goto error;
	case 0:
		ret=0;
		break;
	case kFPBitmapErr:
	case kFPMiscErr:
	case kFPObjectTypeErr:
	case kFPParamErr:
	default:
		ret=EIO;
		goto error;
	}

error:
	return -ret;
}
Example #12
0
int ml_rename(struct afp_volume * vol,
	const char * path_from, const char * path_to) 
{
	int ret,rc;
	char basename_from[AFP_MAX_PATH];
	char basename_to[AFP_MAX_PATH];
	char converted_path_from[AFP_MAX_PATH];
	char converted_path_to[AFP_MAX_PATH];
	unsigned int dirid_from,dirid_to;

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path_from,(char *) path_from,AFP_MAX_PATH))
		return -EINVAL;

	if (convert_path_to_afp(vol->server->path_encoding,
		converted_path_to,(char *) path_to,AFP_MAX_PATH))
		return -EINVAL;

	if (volume_is_readonly(vol)) 
		return -EACCES;

	get_dirid(vol, converted_path_from, basename_from, &dirid_from);
	get_dirid(vol, converted_path_to, basename_to, &dirid_to);

	if (is_dir(vol,dirid_to,converted_path_to)) {
		rc=afp_moveandrename(vol,
			dirid_from,dirid_to,
			basename_from,basename_to,basename_from);
	} else {
		rc=afp_moveandrename(vol,
			dirid_from,dirid_to,
			basename_from,NULL,basename_to);
	}
	switch(rc) {
	case kFPObjectLocked:
	case kFPAccessDenied:
		ret=EACCES;
		break;
	case kFPCantRename:
		ret=EROFS;
		break;
	case kFPObjectExists:
		/* First, remove the old file. */
		switch(afp_delete(vol,dirid_to,basename_to)) {

		case kFPAccessDenied:
			ret=EACCES;
			break;
		case kFPObjectLocked:
			ret=EBUSY;
			break;
		case kFPObjectNotFound:
			ret=ENOENT;
			break;
		case kFPVolLocked:
			ret=EACCES;
			break;
		case kFPDirNotEmpty:
			ret=ENOTEMPTY;
			break;
		case kFPObjectTypeErr:
		case kFPMiscErr:
		case kFPParamErr:
		case -1:
			ret=EINVAL;
			break;
		}
		/* Then, do the move again */
		switch(afp_moveandrename(vol,
			dirid_from,dirid_to,
			basename_from,NULL,basename_to)) {
		case kFPObjectLocked:
		case kFPAccessDenied:
			ret=EACCES;
			break;
		case kFPCantRename:
			ret=EROFS;
			break;
		case kFPObjectExists:
		case kFPObjectNotFound:
			ret=ENOENT;
			break;
		case kFPParamErr:
		case kFPMiscErr:
			ret=EIO;
		default:	
		case kFPNoErr:
			ret=0;
			break;
		}
		break;
	case kFPObjectNotFound:
		ret=ENOENT;
	case kFPNoErr:
		ret=0;
		break;
	default:	
	case kFPParamErr:
	case kFPMiscErr:
		ret=EIO;
	}
	return -ret;
}
Example #13
0
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;
};