/*
 * Copyright (C) 1993, 1994 by Ralf Baechle (linux@uni-koblenz.de)
 *
 * This file is part of Minix-Handler, an AmigaDOS Filehandler to access
 * Minix filesystems via AmigaDOS.
 *
 * Minix-Handler is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * Minix-Handler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <exec/execbase.h>
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <dos/filehandler.h>
#include <dos/dosextens.h>
#include <devices/trackdisk.h>

#define ERROR_OK 0

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

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

#include "td.h"
#include "minix.h"

/*
 * This is a link node used both for locks and filehandles.
 */

struct MinixNode
{
	struct MinNode		mn_Node;
	struct FileLock		mn_Lock;
	struct FileHandle	*mn_FileHandle;
	UWORD			Inode;
	APTR			*mn_BufferBlock;
	/*
	 * mn_BufferPos:
	 * 0xffffffff  == no blocks read yet.
	 * other value == position in buffered block.
	 */
	ULONG			mn_BufferPos;
	ULONG			mn_BlockNum;
};

/*
 * Prototypes for this module.
 */

VOID ReadSuper(VOID);
VOID ReadInodes(VOID);
VOID ReadFromInode(struct IOExtTD *Req, UWORD ino, ULONG BlockNum, APTR Buffer);
UWORD FindObjectOnDisk(struct IOExtTD *Req, UBYTE *PathName);
UWORD SearchDir( struct IOExtTD *Req, UWORD di, UBYTE *namebuff);

/*
 * Pointers to volume data
 * Although these structures don't fill a whole block,
 * allways enire blocks are being read!
 */

struct minix_super_block *SuperBlock = NULL;
struct minix_inode       *Inodes = NULL;
ULONG			 InodeSize = 0;

/*
 * IORequest for all trackdisk.device style IO.
 */

struct IOExtTD *TDReq = NULL;

// ##########################################################################

int main( int argc, char *argv[])
{
	ULONG	i;

	if( argc != 2 ) {
		printf("USAGE: %s filename\n", argv[0]);
		exit(20);
		}

	/*
	 * Allocate memory for the Superblock.
	 */
	if(!(SuperBlock=AllocMem(BLOCK_SIZE,MEMF_ANY))) {
		printf("No memory for Superblock.\n");
		goto fail;
		}

	/*
	 * Open trackdisk.device, we are currently limited to this.
	 */
	if(!(TDReq = TD_Open( 0, 0))) {
		printf("Couldn't open trackdisk.device.\n");
		goto fail;
		}

	ReadSuper();
        ReadInodes();

	i = FindObjectOnDisk(TDReq, argv[1] );
	printf("Inode: %d.\n", i);

fail:
	if(TDReq)	TD_Close( TDReq );
	if(SuperBlock)	FreeMem(SuperBlock, BLOCK_SIZE);
}

VOID ReadSuper(VOID)
{
	printf("Reading SuperBlock.\n");
	TD_Read(TDReq, SuperBlock, BLOCK_SIZE, 1024);
	WaitIO(TDReq);

	if(TDReq->iotd_Req.io_Error)
		printf("  Error reading Superblock.\n");
	else
		printf("  Superblock read successful.\n");
	if(SuperBlock->s_magic == MINIX_SUPER_MAGIC)
		printf("  Magic match OK.\n");
	else
		printf("  Magic match failed.\n");

	TD_Motor(TDReq, MOTOR_OFF);
}

VOID ReadInodes(VOID)
{
	printf("Reading Inodes from disk.\n");

	/*
	 * Try to free old inodetable
	 */
	if(Inodes) {
		printf("  Freeing old Inodetable.\n");
		FreeMem( Inodes, InodeSize );
		}

	/*
	 * Calculate size of inode table blocks
	 */
	InodeSize = SuperBlock->s_ninodes*sizeof(struct minix_inode);
	InodeSize = (InodeSize + (BLOCK_SIZE-1)) & ~(BLOCK_SIZE-1);

	/*
	 * Allocate Inodetable.
	 */
	printf("  Allocating core for Inodes.\n");
	if(!(Inodes=AllocMem(InodeSize, MEMF_ANY))) {
		printf("  No core for Inodes.\n");
		return;
		}

	/*
	 * Offset is currently hardwired
	 */
	TD_Read(TDReq, Inodes, InodeSize, 4096);
	WaitIO(TDReq);

	if(TDReq->iotd_Req.io_Error)
		printf("  Error reading Inodes.\n");
	else
		printf("  Inodes read successful.\n");

	TD_Motor(TDReq, MOTOR_OFF);
}

/*
 * Currently limited to small files/dirs without extension blocks!
 */
VOID ReadFromInode(struct IOExtTD *Req, UWORD ino, ULONG BlockNum, APTR Buffer)
{
	ULONG	b;

	b = Inodes[ino].i_zone[BlockNum] << BLOCK_SIZE_BITS;
	TD_Read(Req, Buffer, BLOCK_SIZE, b );
	WaitIO(Req);

	if(Req->iotd_Req.io_Error)
		printf("  Error reading Dirblock.\n");
	else
		printf("  Dirblock read successful.\n");

	TD_Motor(Req, MOTOR_OFF);
}

UWORD FindObjectOnDisk(struct IOExtTD *Req, UBYTE *PathName)
{
	UBYTE	*s,*s2,*p1,*p2;
	UBYTE	namebuff[MINIX_NAME_LEN+1];
	WORD	n;
	UWORD	DirInode;

	s2 = PathName;
	printf("Pathname        : %s\n", PathName);
	/*
         * Skip device/volume name
         */
	s = s2;
        while(*s != '\0' && *s != ':') s++;
	if(*s == ':')
		s++;
	else
		s=s2;
	printf("Striped pathname: %s\n", s);

        /*
	 * Special case: do we search for the root directory?
	 */
	if(*s == '\0') return 1;

	/*
         * Get part of filename and examine disk starting
	 * at the root
         */
	DirInode = MINIX_ROOT_INO;
	FOREVER {
		/*
		 * copy up to MINIX_NAME_LEN chars to namebuff.
		 */
		p1 = namebuff;
		p2 = s;
		n = MINIX_NAME_LEN;
	        while((*p1 = *p2) && (*p2 != '/') && n--) {
			p1++;
			p2++;
			}
		*p1 = '\0';

		/*
		 * Skip to next part of path.
		 */
		s2 = s;
	        while(*s != '\0' && *s != '/') s++;
		if(*s == '/')
			s++;
		printf("Pathcomponent   : %s\n", namebuff);
		DirInode = SearchDir( Req, DirInode - 1, namebuff );
		if(!DirInode || !*p2) break;
		}

	/*
         * Return inode number or zero for not found.
         */

	return DirInode;
}

/*
 * Block buffer for directory search.
 */
ULONG BufferBlock[BLOCK_SIZE/sizeof(ULONG)];

/*
 * Search directory di on device Req for entry namebuff
 */
UWORD SearchDir( struct IOExtTD *Req, UWORD di, UBYTE *name)
{
	ULONG			BufferPos;
	ULONG			BlockNum;
	BOOL			Success;
	struct minix_dir_entry	*MinixDir;

	/*
	 * Read first block.
	 */
	BufferPos = 0;
	BlockNum  = 0;
	printf("  Reading first dir block.\n");
	ReadFromInode(TDReq, di, 0, BufferBlock);

        Success = FALSE;
	FOREVER {
		/*
		 * Did we reach the end of dir?
		 */
		if((BlockNum << BLOCK_SIZE_BITS) + BufferPos >= Inodes[di].i_size) {
			printf("  No more dir blocks.\n");
			break;
			}
		/*
		 * Is the buffer empty?
		 * If so, read one block.
		 */
		else if(BufferPos > (BLOCK_SIZE-sizeof(struct minix_inode))) {
			BufferPos = 0;
			BlockNum++;
			ReadFromInode(TDReq, di, BlockNum, BufferBlock);
			}
		/*
		 * Read just some bytes from the buffered block.
		 */
		MinixDir = (struct minix_dir_entry *)(((UBYTE *)BufferBlock) + BufferPos);
		BufferPos += sizeof(struct minix_dir_entry);

		/*
		 * Entry not deleted? Name ok?
		 */
		if( MinixDir->inode && !strncmp( MinixDir->name, name, MINIX_NAME_LEN )) {
			Success = TRUE;
			break;
			}
		}
	if(!Success) {
		printf("  Didn't find entry.\n");
		return 0;
		}
	else {
		printf("  Found wanted entry.\n");
		return MinixDir->inode;
		}
}
