/*
	FS1541

	packet.c


	This is the packet processor.

	If you are new to this subject and want to learn about it, you *must*
	have the Amiga Guru Book next to you and compare the routines for each
	packet with the specifications in the book -- then you will understand!

*/

#include <string.h>

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

#include "main.h"
#include "packet.h"
#include "support.h"
#include "volume.h"
#include "disk.h"

extern UBYTE diskinfo_image[840];
extern struct BAM empty_bam;

static BOOL getfiledata(UBYTE entry, struct FHArg1 *fharg1);
static BPTR trylockfile(LONG *err2, BSTR name, LONG access, struct FHArg1 *fharg1, BOOL allow_root);

static UBYTE fname1[20];

int inhibited = FALSE;

static LONG TestLock(struct FileLock *fl);
static inline LONG TestLockRef(struct FileLock *fl);

/*-------------------------------------------------------------------------*/

void DoPackets(void)
{
	struct DosPacket *packet;

	while((packet = GetPacket(ourport)))
	{
		LONG error = DOSFALSE;
		LONG err2 = 0;
		BOOL f = TRUE;

		dpsender = packet->dp_Port;

		if(inhibited)
		{
			switch(packet->dp_Type)
			{
				case ACTION_DISK_INFO:
				case ACTION_INHIBIT:
				case ACTION_FORMAT:
					break;

				default:
					f = FALSE;
					err2 = ERROR_NOT_A_DOS_DISK;
					break;
			}
		}

		if(f) switch(packet->dp_Type)
		{
			case ACTION_LOCATE_OBJECT:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);
				struct FHArg1 fharg1;

				if((err2 = TestLock(fl)))
					break;
				else if(!curvolumenode)
				{
					err2 = disk_inserted ? ERROR_NOT_A_DOS_DISK : ERROR_NO_DISK;
					break;
				}

				/* This FileSystem is easy since we don't have to take
				   care of any FileLock path tracking and resolving */

				error = trylockfile(&err2, packet->dp_Arg2, packet->dp_Arg3, &fharg1, TRUE);
				MotorOff();
				break;
			}

			case ACTION_FREE_LOCK:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);

				if(fl)
					freelock(fl);

				MotorOff();
				error = DOSTRUE;
				break;
			}

			case ACTION_COPY_DIR:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);

				if(fl) {
					if(fl->fl_Access == EXCLUSIVE_LOCK) {
						/* Don't duplicate exclusive locks */
						err2 = ERROR_OBJECT_IN_USE;
					}
					else {
						if(fl->fl_Volume == MKBADDR(curdoslist)) {
							/* Try to duplicate it! */
							err2 = ERROR_NO_FREE_STORE;
							error = makelock(fl->fl_Key, SHARED_LOCK);
						}
						else {
							/* The reference disk is currently not inserted. */
							err2 = ERROR_DEVICE_NOT_MOUNTED;
						}
					}
				}
				else {
					if(curvolumenode) {
						/* Return shared lock on root directory */
						err2 = ERROR_NO_FREE_STORE;
						error = makelock(0, SHARED_LOCK);
					}
					else {
						err2 = ERROR_NO_DISK;
					}
				}

				break;
			}

			case ACTION_PARENT:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);

				if(fl) {
					if(!(err2 = TestLockRef(fl))) {
						if(fl->fl_Key != 0) {
							/* Return shared lock on root directory */
							err2 = ERROR_NO_FREE_STORE;
							error = makelock(0, SHARED_LOCK);
						}
						else {
							err2 = 0;
						}
					}
				}
				else {
					err2 = 0;
				}

				break;
			}

			case ACTION_SAME_LOCK:
			{
				struct FileLock *fl1 = BADDR(packet->dp_Arg1);
				struct FileLock *fl2 = BADDR(packet->dp_Arg2);

				err2 = 0;

				if(fl1 == fl2 || ((fl1->fl_Volume == fl2->fl_Volume) && (fl1->fl_Key == fl2->fl_Key)))
					error = DOSTRUE;

				break;
			}

			case ACTION_EXAMINE_OBJECT:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);
				struct FileInfoBlock *fib = BADDR(packet->dp_Arg2);

				if(fl && fl->fl_Key != 0) {
					/* Examine file */
					struct FHArg1 fharg1;
					int entry;
					int special;

					fib->fib_DiskKey = 0;
					fib->fib_DirEntryType = fib->fib_EntryType = ST_FILE;
					fib->fib_Protection = 0;
					fib->fib_Comment[0] = '\0';
					fib->fib_Date.ds_Days = 0;
					fib->fib_Date.ds_Minute = 0;
					fib->fib_Date.ds_Tick = 0;

					if(fl->fl_Volume != MKBADDR(curdoslist)) {
						/* The reference disk is currently not inserted. */
						err2 = ERROR_DEVICE_NOT_MOUNTED;
						break;
					}

					if((special = fl->fl_Key & FLKEY_SPECIALMASK)) {
						/* Examine virtual files */
						static struct { STRPTR s; ULONG l; } tbl[3] = {
							{ VIRTUAL_FILE_D64, D64_SIZE },
							{ VIRTUAL_FILE_DISKINFO, 840 },
							{ VIRTUAL_FILE_OPT, 0 }
						};
						int index = (special>>FLKEY_SPECIALSHIFT)-2;

						switch(special) {
							case FLKEY_DOLLAR:
								strcpy(&fib->fib_FileName[1], VIRTUAL_FILE_DOLLAR);
								fib->fib_FileName[0] = strlen(VIRTUAL_FILE_DOLLAR);
								fib->fib_Size = curvolumenode->dollarlen;
								error = DOSTRUE;
								break;

							case FLKEY_DISKINFO: case FLKEY_D64: case FLKEY_OPT:
								strcpy(&fib->fib_FileName[1], tbl[index].s);
								fib->fib_FileName[0] = strlen(tbl[index].s);
								fib->fib_Size = tbl[index].l;
								error = DOSTRUE;
								break;

							default:
								err2 = ERROR_OBJECT_NOT_FOUND;
								break;
						}
					}
					else {
						for(entry=0;entry<dirsize;entry++)
							if(fl->fl_Key == ((directory[entry].datat<<8)|directory[entry].datas))
								break;
	
						if(entry<dirsize) {
							if(getfiledata(entry, &fharg1)) {
								copy64name(&fib->fib_FileName[1], directory[entry].name, 16);
								fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]);
								fib->fib_Size = fharg1.len;
								error = DOSTRUE;
							}
							else err2 = ERROR_OBJECT_NOT_FOUND;
						}
						else err2 = ERROR_OBJECT_NOT_FOUND;
					}
				}
				else {
					/* Prepare for EXAMINE_NEXT, return volume name in
					   fib_FileName */
					fib->fib_DiskKey = 0;
					fib->fib_DirEntryType = fib->fib_EntryType = ST_ROOT;
					strcpy(&fib->fib_FileName[1], &curvolumenode->name[1]);
					fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]);
					fib->fib_Protection = 0;
					fib->fib_Comment[0] = '\0';
					fib->fib_Date.ds_Days = 0;
					fib->fib_Date.ds_Minute = 0;
					fib->fib_Date.ds_Tick = 0;
					error = DOSTRUE;
				}

				break;
			}

			case ACTION_EXAMINE_NEXT:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);
				struct FileInfoBlock *fib = BADDR(packet->dp_Arg2);

				if((err2 = TestLock(fl)))
					break;

				fib->fib_DirEntryType = fib->fib_EntryType = ST_FILE;

				err2 = ERROR_NO_MORE_ENTRIES;

				while(fib->fib_DiskKey < dirsize) {
					int entry;
					struct FHArg1 fharg1;

					entry = fib->fib_DiskKey++;

					if(getfiledata(entry, &fharg1)) {
						copy64name(&fib->fib_FileName[1], directory[entry].name, 16);
						fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]);
						fib->fib_Size = fharg1.len;
						fib->fib_NumBlocks = fharg1.numblocks;
						error = DOSTRUE;
						break;
					}
				}

				if(error == DOSFALSE)
					MotorOff();

				break;
			}

			case ACTION_FINDINPUT:
			case ACTION_FINDOUTPUT:
			case ACTION_FINDUPDATE:
			{
				struct FileHandle *fh = BADDR(packet->dp_Arg1);
				struct FileLock *fl = BADDR(packet->dp_Arg2);
				struct FHArg1 *fharg1;
				LONG access = packet->dp_Type==ACTION_FINDINPUT ? SHARED_LOCK : EXCLUSIVE_LOCK;

				if((err2 = TestLock(fl)))
					break;

				if((fharg1 = AllocVec(sizeof(struct FHArg1), MEMF_ANY|MEMF_CLEAR))) {
					if(trylockfile(&err2, packet->dp_Arg3, access, fharg1, FALSE)) {
						if(fharg1->number!=NUM_VIRTUAL || access==SHARED_LOCK
						|| (((struct FileLock*)BADDR(fharg1->lock))->fl_Key & FLKEY_SPECIALMASK) == FLKEY_D64) {
							/* Allow virtual files only for reading, exception is $d64 */
							fh->fh_Arg1 = (LONG)fharg1;
							fh->fh_Port = DOSFALSE;
							error = DOSTRUE;
						}
						else {
							/* But new files with virtual file names can be created,
								which will be real then. */
							freelock((struct FileLock*)BADDR(fharg1->lock));
							err2 = ERROR_OBJECT_NOT_FOUND;
						}
					}

					if((error==DOSFALSE && access==EXCLUSIVE_LOCK && err2!=ERROR_INVALID_COMPONENT_NAME) || packet->dp_Type==ACTION_FINDOUTPUT) {
						if(!wprotected) {
							if(error==DOSFALSE) {
								/* Create new file */
								UWORD dblk;
	
								if(dirsize < 144) {
									if((dblk = AllocBlock(18,0))) {
										UBYTE clrblk[256];
	
										memset(&clrblk, 0, sizeof(clrblk));
										clrblk[1] = 1;
	
										if(putblock_ts(dblk>>8, dblk&0xff, &clrblk)) {
											struct DirEntry *de = &directory[dirsize++];
	
											memset(de, 0, sizeof(struct DirEntry));
											de->type = 0x82; /* PRG, closed */
											de->datat = dblk>>8;
											de->datas = dblk&0xff;
											de->lengthl = 1;
											asciito64(de->name, fname1, 16);
											StartUDSTimer();
	
											if(trylockfile(&err2, packet->dp_Arg3, access, fharg1, FALSE)) {
												fh->fh_Arg1 = (LONG)fharg1;
												fh->fh_Port = DOSFALSE;
												error = DOSTRUE;
											}
										}
										else {
											err2 = ABORT_DISK_ERROR;
											FreeBlock(dblk>>8, dblk&0xff);
										}
									}
									else err2 = ERROR_DISK_FULL;
								}
								else err2 = ERROR_DISK_FULL;
							}
							else if(fharg1->number != NUM_VIRTUAL) /* trying to overwrite $d64 */ {
								/* Overwrite existing file */
								struct DataBlock *block;
								UBYTE t,s;
								int i;
	
								for(i=0,t=fharg1->t,s=fharg1->s; t; t=block->t,s=block->s,i++) {
									block = getblock_ts(t,s);
									if(i>0) FreeBlock(t,s);
								}
								fharg1->len = 0;
								fharg1->numblocks = 1;
								block = getblock_ts(fharg1->t, fharg1->s);
								block->t = 0;
								block->s = 1;
								putblock_ts(fharg1->t, fharg1->s, block);
							}
						}
						else err2 = ERROR_DISK_WRITE_PROTECTED;
					}
					else {
						if(error==DOSTRUE) {
							/* Open existing file, already done. */
						}
						/* else err2 still set from first trylockfile() call */
					}

					if(error == DOSFALSE) {
						FreeVec(fharg1);
						MotorOff();
					}
				}
				else err2 = ERROR_NO_FREE_STORE;

				break;
			}

			case ACTION_READ:
			{
				struct FHArg1 *fharg1 = (struct FHArg1 *)packet->dp_Arg1;
				APTR buffer = (APTR)packet->dp_Arg2;
				ULONG togo = packet->dp_Arg3;
				struct DataBlock *block = NULL;

				if((err2 = TestLockRef((struct FileLock*)BADDR(fharg1->lock))))
					break;

				if(fharg1->pos + togo >= fharg1->len)
					togo = fharg1->len - fharg1->pos;

				error = 0;

				if(fharg1->number == NUM_VIRTUAL) {
					/* Handle virtual files */
					int special = ((struct FileLock*)BADDR(fharg1->lock))->fl_Key & FLKEY_SPECIALMASK;

					switch(special) {
						case FLKEY_DOLLAR:
							CopyMem(&curvolumenode->dollarbuf[fharg1->pos], buffer, togo);
							fharg1->pos += togo;
							error += togo;
							break;

						case FLKEY_DISKINFO:
							CopyMem(&diskinfo_image[fharg1->pos], buffer, togo);
							fharg1->pos += togo;
							error += togo;
							break;

						case FLKEY_D64:
							while(togo) {
								ULONG size, offset;

								if(!(block = getputblock((fharg1->pos)>>8, FALSE)))
									break;

								offset = (fharg1->pos) & 255;

								size = 256 - offset;
								if(togo < size)
									size = togo;

								CopyMem(&((UBYTE*)block)[offset], buffer, size);

								buffer += size;
								fharg1->pos += size;
								error += size;
								togo -= size;
							}
							break;

						default:
							break;
					}
				}
				else {
					/* Handle real files */
					while(togo) {
						ULONG size;
	
						if(fharg1->p == 254) {
							if(!block)
								if(!(block = getblock_ts(fharg1->t, fharg1->s)))
									break;
							fharg1->p = 0;
							fharg1->t = block->t;
							fharg1->s = block->s;
						}
	
						size = 254 - fharg1->p;
						if(togo < size)
							size = togo;
	
						if(!(block = getblock_ts(fharg1->t, fharg1->s)))
							break;
	
						CopyMem(&block->data[fharg1->p], buffer, size);
						buffer += size;
						fharg1->p += size;
						fharg1->pos += size;
						error += size;
						togo -= size;
					}
				}

				break;
			}

			case ACTION_WRITE:
			{
				struct FHArg1 *fharg1 = (struct FHArg1 *)packet->dp_Arg1;
				APTR buffer = (APTR)packet->dp_Arg2;
				ULONG togo = packet->dp_Arg3;
				struct DataBlock *block = NULL;
				struct DataBlock newblk;

				if((err2 = TestLockRef((struct FileLock*)BADDR(fharg1->lock))))
					break;

				if(wprotected) {
					error = -1;
					err2 = ERROR_DISK_WRITE_PROTECTED;
					break;
				}

				error = 0;

				if((((struct FileLock*)BADDR(fharg1->lock))->fl_Key & FLKEY_SPECIALMASK) == FLKEY_D64) {
					/* Handle writes to the $d64 file */
					if(fharg1->pos + togo >= D64_SIZE)
						togo = D64_SIZE - fharg1->pos;

					while(togo) {
						ULONG size, offset;

						if(!(block = getputblock((fharg1->pos)>>8, FALSE)))
							break;

						offset = (fharg1->pos) & 255;

						size = 256 - offset;
						if(togo < size)
							size = togo;

						CopyMem(buffer, &((UBYTE*)block)[offset], size);
						getputblock((fharg1->pos)>>8, TRUE);

						buffer += size;
						fharg1->pos += size;
						error += size;
						togo -= size;
					}
					break;
				}

				while(togo) {
					ULONG size;

					if(!block)
						block = getblock_ts(fharg1->t, fharg1->s);

					if(fharg1->p == 254) {
						/* Switch to next block */
						if(!block->t) {
							/* Allocate new block */
							UWORD dblk;

							if(!(dblk = AllocBlock(fharg1->t, fharg1->s))) {
								err2 = ERROR_DISK_FULL;
								break;
							}

							fharg1->p = 0;
							block->t = dblk>>8;
							block->s = dblk&0xff;
							putblock_ts(fharg1->t, fharg1->s, block); /* link to previous block */
							fharg1->t = block->t;
							fharg1->s = block->s;
							fharg1->numblocks++;

							block = &newblk;
							memset(&newblk, 0, sizeof(newblk));
							block->t = 0;
							block->s = 1;
						}
						else {
							/* Jump to existing next block */
							fharg1->p = 0;
							fharg1->t = block->t;
							fharg1->s = block->s;
							block = getblock_ts(fharg1->t, fharg1->s);
						}
					}

					size = 254 - fharg1->p;
					if(togo < size)
						size = togo;

					/* Insert data */
					CopyMem(buffer, &block->data[fharg1->p], size);
					buffer += size;

					/* Advance block pointer and dump it */
					fharg1->p += size;
					if(!block->t)
						block->s = fharg1->p + 1;
					putblock_ts(fharg1->t, fharg1->s, block);

					/* Adjust length field if necessary */
					fharg1->pos += size;
					if(fharg1->pos > fharg1->len)
						fharg1->len = fharg1->pos;

					error += size;
					togo -= size;
				}

				break;
			}

			case ACTION_SEEK:
			{
				struct FHArg1 fharg1bak, *fharg1 = (struct FHArg1 *)packet->dp_Arg1;
				LONG move = packet->dp_Arg2;

				if((err2 = TestLockRef((struct FileLock*)BADDR(fharg1->lock))))
					break;

				error = fharg1->pos;
				err2 = ERROR_SEEK_ERROR;

				switch(packet->dp_Arg3) {
					case OFFSET_BEGINNING:
						move = move - error;
						break;

					case OFFSET_CURRENT:
						break;

					case OFFSET_END:
						move = move + fharg1->len - error;
						break;

					default:
						error = -1;
						break;
				}

				if(error != -1) {
					CopyMem(fharg1, &fharg1bak, sizeof(struct FHArg1));

					if((error+move)<0 || (error+move)>fharg1->len)
						error = -1;
					else {
						if(((struct FileLock*)BADDR(fharg1->lock))->fl_Key & FLKEY_SPECIALMASK) {
							/* Virtual files are easy */
							fharg1->pos += move;
						}
						else {
							if(move<0) {
								/* When moving back, we have to rescan the whole file */
								move = move + error;
								fharg1->p = 0;
								fharg1->pos = 0;
								fharg1->t = fharg1->t0;
								fharg1->s = fharg1->s0;
							}
	
							while(move) {
								ULONG size;
								struct DataBlock *block;
	
								if(fharg1->p == 254) {
									block = getblock_ts(fharg1->t, fharg1->s);

									if(!block) {
										CopyMem(&fharg1bak, fharg1, sizeof(struct FHArg1));
										error = -1;
										err2 = ABORT_DISK_ERROR;
										break;
									}

									fharg1->p = 0;
									fharg1->t = block->t;
									fharg1->s = block->s;
								}
	
								size = 254 - fharg1->p;
			
								if(move < size)
									size = move;
			
								fharg1->p += size;
								fharg1->pos += size;
								move -= size;
							}
						}
					}
				}

				break;
			}

			case ACTION_END:
			{
				struct FHArg1 *fharg1 = (struct FHArg1 *)packet->dp_Arg1;

				if((err2 = TestLockRef((struct FileLock*)BADDR(fharg1->lock))))
					break;

				if(fharg1->len != fharg1->len0) {
					/* File has been modified, update disk structure */
					int i;

					for(i=0;i<dirsize;i++)
						if(directory[i].datat == fharg1->t0
						&& directory[i].datas == fharg1->s0)
						{
							UWORD blks = fharg1->numblocks;
							directory[i].lengthl = blks&0xff;
							directory[i].lengthh = blks>>8;
							directory[i].type |= 0x80;
						}

					StartUDSTimer();
				}

				freelock(BADDR(fharg1->lock));
				FreeVec(fharg1);

				error = DOSTRUE;
				break;
			}

			case ACTION_IS_FILESYSTEM:
				/* Yes, we are. */
				error = DOSTRUE;
				break;

			case ACTION_CURRENT_VOLUME:
			{
				struct FHArg1 *fharg1 = (struct FHArg1 *)packet->dp_Arg1;

				/* The packet name is somewhat misleading... */
				if(fharg1)
					error = ((struct FileLock*)BADDR(fharg1->lock))->fl_Volume;
				else {
					if(curvolumenode)
						error = (LONG)MKBADDR(curdoslist);
					else
						error = NULL;
				}

				break;
			}

			case ACTION_INFO:
			case ACTION_DISK_INFO:
			{
				struct InfoData *id;

				if(packet->dp_Type == ACTION_INFO) {
					struct FileLock *fl = BADDR(packet->dp_Arg1);

					if(fl && fl->fl_Volume != MKBADDR(curdoslist)) {
						/* The reference disk is currently not inserted. */
						err2 = ERROR_DEVICE_NOT_MOUNTED;
						break;
					}

					id = BADDR(packet->dp_Arg2);
				}
				else {
					id = BADDR(packet->dp_Arg1);
				}

				id->id_NumSoftErrors = numsofterrors;
				id->id_NumBlocks = 683;
				id->id_NumBlocksUsed = UsedBlocks();
				id->id_BytesPerBlock = 254;
				id->id_DiskState = hardwprot ? ID_WRITE_PROTECTED : (wprotected ? ID_VALIDATING : ID_VALIDATED);
				id->id_UnitNumber = fssm->fssm_Unit;

				if(curvolumenode) {
					id->id_DiskType = ID_DOS_DISK;
					id->id_VolumeNode = MKBADDR(curdoslist);
					id->id_InUse = curvolumenode->locklist ? DOSTRUE : DOSFALSE;
				}
				else {
					#ifndef ID_BUSY /* My v40 header files don't */
					#define ID_BUSY 0x42555359
					#endif
					id->id_DiskType = inhibited ? ID_BUSY : (disk_inserted ? ID_UNREADABLE_DISK : ID_NO_DISK_PRESENT);
				}

				error = DOSTRUE;

				break;
			}

			case ACTION_INHIBIT:
			{
				/* We could have trouble here if the DosList is locked,
				   but then again, one shouldn't lock it for INHIBIT. */

				if(packet->dp_Arg1 == DOSTRUE) {
					inhibited = TRUE;

					StopUDSTimer();
					MotorOff();
					DoDiskRemove();
				}
				else {
					DoDiskInsert();

					inhibited = FALSE;
				}

				error = DOSTRUE;

				break;
			}

			case ACTION_CREATE_DIR:
				err2 = ERROR_INVALID_COMPONENT_NAME;
				break;

			case ACTION_DELETE_OBJECT:
			{
				struct FileLock *fl = BADDR(packet->dp_Arg1);
				struct FHArg1 fharg1;

				if((err2 = TestLock(fl)))
					break;

				if(!wprotected) {
					if(trylockfile(&err2, packet->dp_Arg2, EXCLUSIVE_LOCK, &fharg1, FALSE)) {
						struct DataBlock *block;
						UBYTE t,s;
	
						freelock((struct FileLock*)BADDR(fharg1.lock));
	
						if(fharg1.number != NUM_VIRTUAL) {
							/* We do this only for non-virtual files. */
							for(t=fharg1.t,s=fharg1.s; t; t=block->t,s=block->s) {
								block = getblock_ts(t,s);
								FreeBlock(t,s);
							}
		
							directory[fharg1.number].type = 0x00;
							StartUDSTimer();
							error = DOSTRUE;
						}
						else err2 = ERROR_DELETE_PROTECTED;
					}
				}
				else err2 = ERROR_DISK_WRITE_PROTECTED;

				MotorOff();
				break;
			}

			case ACTION_FORMAT:
			{
				UBYTE buf[18];
				STRPTR a;
				struct DataBlock blk1;

				ResetDisk();

				if(!hardwprot) {
					if((a = BADDR(packet->dp_Arg1))) {
						int l = a[0]>16 ? 16 : a[0];
						CopyMem(&a[1], buf, l);
						buf[l] = '\0';
					}
					else buf[0] = '\0';

					asciito64(empty_bam.name, buf, 16);
					putblock_ts(18,0,&empty_bam);
					memset(&blk1, 0, sizeof(blk1));
					blk1.s = 0xff;
					putblock_ts(18,1,&blk1);

					error = DOSTRUE;
				}
				else err2 = ERROR_DISK_WRITE_PROTECTED;

				break;
			}

			case ACTION_RENAME_DISK:
			{
				UBYTE buf[18];
				STRPTR a;

				if(curvolumenode) {
					if(!wprotected) {
						if((a = BADDR(packet->dp_Arg1))) {
							int l = a[0]>16 ? 16 : a[0];
							CopyMem(&a[1], buf, l);
							buf[l] = '\0';
						}
						else buf[0] = '\0';

						asciito64(bam->name, buf, 16);

						while(!AttemptLockDosList(LDF_VOLUMES|LDF_WRITE))
							DoPackets();
						copy64name(&curvolumenode->name[1], bam->name, 16);
						curvolumenode->name[0] = strlen(&curvolumenode->name[1]);
						UnLockDosList(LDF_VOLUMES|LDF_WRITE);

						StartUDSTimer();

						error = DOSTRUE;
					}
					else err2 = ERROR_DISK_WRITE_PROTECTED;
				}
				else err2 = disk_inserted ? ERROR_NOT_A_DOS_DISK : ERROR_NO_DISK;

				break;
			}

			case ACTION_RENAME_OBJECT:
			{
				struct FileLock *fl1 = BADDR(packet->dp_Arg1),
				                *fl2 = BADDR(packet->dp_Arg3);
				struct FHArg1 fharg1;

				if(!((err2 = TestLock(fl1)) || (err2 = TestLock(fl2)))) {
					if(!wprotected) {
						if(trylockfile(&err2, packet->dp_Arg2, SHARED_LOCK, &fharg1, FALSE)) {
							freelock((struct FileLock*)BADDR(fharg1.lock));

							if(fharg1.number != NUM_VIRTUAL) {
								STRPTR a;
								UBYTE buf[256];

								if((a = BADDR(packet->dp_Arg4))) {
									CopyMem(&a[1], buf, a[0]);
									buf[a[0]] = '\0';

									if(!strchr(buf, '/')) {
										a = strchr(buf, ':');
										strncpy(fname1, a ? a+1 : buf, 17);
										fname1[16] = '\0';

										asciito64(directory[fharg1.number].name, fname1, 16);
										StartUDSTimer();
										error = DOSTRUE;
									}
									else err2 = ERROR_INVALID_COMPONENT_NAME;
								}
								else err2 = ERROR_INVALID_COMPONENT_NAME;
							}
							else err2 = ERROR_OBJECT_NOT_FOUND;
						}
					}
					else err2 = ERROR_DISK_WRITE_PROTECTED;
				}

				break;
			}

			case ACTION_SET_FILE_SIZE:
				error = -1;
				err2 = ERROR_ACTION_NOT_KNOWN;
				break;

			default:
				err2 = ERROR_ACTION_NOT_KNOWN;
				break;
		}
	
		ReturnPacket(packet, error, err2);
	}
}

static LONG TestLock(struct FileLock *fl)
{
	if(fl) {
		if(fl->fl_Volume != MKBADDR(curdoslist))
			/* The reference disk is currently not inserted. */
			return(ERROR_DEVICE_NOT_MOUNTED);
		if(fl->fl_Key != 0)
			/* An invalid lock has been passed. */
			return(ERROR_OBJECT_WRONG_TYPE);
	}
	return(0);
}

static inline LONG TestLockRef(struct FileLock *fl)
{
	if(fl->fl_Volume != MKBADDR(curdoslist))
		/* The reference disk is currently not inserted. */
		return(ERROR_DEVICE_NOT_MOUNTED);
	return(0);
}

/*-------------------------------------------------------------------------*/

static BOOL getfiledata(UBYTE entry, struct FHArg1 *fharg1)
{
	if(directory[entry].name[0] != 0xa0) {
		UWORD type = (directory[entry].type) & 0x7;

		if(type!=0x00) { // anything but DEL-Files
			struct DataBlock *block;
			int i,t,s,size;

			for(i=0,t=directory[entry].datat,s=directory[entry].datas,size=0;
				i<683; /* a primitive solution to overcome circular links */
				t=block->t,s=block->s,i++)
			{
				if(!(block = getblock_ts(t,s)))
					return(FALSE);

				if(block->t)
					size+=254;
				else {
					size+=block->s-1;
					break;
				}
			}

			if(i<683) {
				fharg1->len = fharg1->len0 = size;
				fharg1->numblocks = i+1;
				fharg1->t = fharg1->t0 = directory[entry].datat;
				fharg1->s = fharg1->s0 = directory[entry].datas;
				fharg1->number = entry;
				return(TRUE);
			}
		}
	}

	return(FALSE);
}

/*-------------------------------------------------------------------------*/

/* May only be called if curvolumenode is valid! */
static BPTR trylockfile(LONG *err2, BSTR name, LONG access, struct FHArg1 *fharg1, BOOL allow_root)
{
	UBYTE buf[256];
	STRPTR a;
	LONG error;

	error = DOSFALSE;

	/* Convert BSTR argument into C string */
	if((a = BADDR(name))) {
		CopyMem(&a[1], buf, a[0]);
		buf[a[0]] = '\0';

		if(strchr(buf, '/')) {
			/* User tries to access subdirectories */
			*err2 = ERROR_INVALID_COMPONENT_NAME;
			return(error);
		}

		a = strchr(buf, ':');
		strncpy(fname1, a ? a+1 : buf, 17);
		fname1[16] = '\0';
	}
	else
		fname1[0] = '\0';

	if(fname1[0] == '\0') {
		if(allow_root) {
			/* Return lock on root directory */
			if(access == EXCLUSIVE_LOCK) {
				*err2 = ERROR_OBJECT_IN_USE;
			}
			else {
				*err2 = ERROR_NO_FREE_STORE;
				error = makelock(0, access);
			}
		}
		else *err2 = ERROR_OBJECT_WRONG_TYPE;
	}
	else {
		int entry;

		*err2 = ERROR_OBJECT_NOT_FOUND;

		/* Look up file in directory and lock it */
		for(entry=0;entry<dirsize;entry++) {
			copy64name(buf, directory[entry].name, 16);
			if(!Stricmp(buf, fname1))
				break;
		}

		if(entry == dirsize) {
			/* Perhaps we can provide a virtual file */
			fharg1->number = NUM_VIRTUAL;

			if(!Stricmp(VIRTUAL_FILE_DISKINFO,fname1)) {
				*err2 = ERROR_NO_FREE_STORE;
				error = fharg1->lock = makelock(FLKEY_DISKINFO, access);
				fharg1->len = fharg1->len0 = 840;
			}
			else if(fname1[0] == '$') {
				if(!Stricmp(VIRTUAL_FILE_DOLLAR,fname1)) {
					*err2 = ERROR_NO_FREE_STORE;
					error = fharg1->lock = makelock(FLKEY_DOLLAR, access);
					fharg1->len = fharg1->len0 = curvolumenode->dollarlen;
				}
				else if(!Stricmp(VIRTUAL_FILE_D64,fname1)) {
					*err2 = ERROR_NO_FREE_STORE;
					error = fharg1->lock = makelock(FLKEY_D64, access);
					fharg1->len = fharg1->len0 = D64_SIZE;
				}
				else if(!Stricmp(VIRTUAL_FILE_OPT,fname1)) {
					if(!wprotected) {
						*err2 = ERROR_NO_FREE_STORE;
						error = fharg1->lock = makelock(FLKEY_OPT, access);
						fharg1->len = fharg1->len0 = 0;
						OptimizeDirectory();
						StartUDSTimer();
					}
					else *err2 = ERROR_DISK_WRITE_PROTECTED;
				}
			}
		}
		else {
			if(getfiledata(entry, fharg1)) {
				if(lockable(fharg1->t, fharg1->s, access)) {
					*err2 = ERROR_NO_FREE_STORE;
					error = fharg1->lock = makelock(((fharg1->t)<<8)|fharg1->s, access);
				}
				else *err2 = ERROR_OBJECT_IN_USE;
			}
		}
	}

	return(error);
}
