/* Minix-FS version 0.60, Copyright S.N. Henson 1991,1992,1993
 * see the file 'copying' for more info.
 *
 * Minix-FS version 0.70 by Frank Naumann
 */

# include <mintbind.h>

# include "main.h"
# include "global.h"
# include "minixfs.h"

# include "inode.h"
# include "zone.h"

# include "version.h"


/*
 * Initialisation routine called first by the kernel
 */

FILESYS *
minix_init (struct kerinfo *k)
{
	kernel = k;
	
	CCONWS ("\033p Minix file system driver for MiNT. \033q\r\n");
	CCONWS ("Version " str(MFS_MAJOR) "." str(MFS_MINOR) "\r\n");
	CCONWS ("Copyright 1991,1992,1993,1994,1995 S.N.Henson\r\n");
# ifdef MFS_PLEV
	CCONWS ("Patch Level " str(MFS_PLEV) "\r\n");
# endif
# ifdef PRE_RELEASE
	CCONWS ("Pre-Release Test Version\r\n");
# endif
	CCONWS ("\033pThis is a modified binary version for FreeMiNT 1.15.\033q\r\n");
	CCONWS ("½ " __DATE__ " by Frank Naumann.\r\n\r\n");
	
	/* check MiNT version */
	if ((k->maj_version < 1) || (k->maj_version == 1 && k->min_version < 15))
	{
		CCONWS ("This version requires at least FreeMiNT 1.15!\r\n");
		CCONWS ("\7Minix-FS not installed.\r\n\r\n");
		return NULL;
	}
	
	/* check buffer cache revision */
	if (bio.version != 3)
	{
		CCONWS ("Wrong MiNT buffer cache version!\r\n");
		CCONWS ("\7Minix-FS not installed.\r\n\r\n");
		return NULL;		
	}
	
	error.data = Kmalloc (BLOCK_SIZE);
	if (!error.data)
	{
		CCONWS ("Can't alloc error unit!\r\n");
		CCONWS ("\7Minix-FS not installed.\r\n\r\n");
		return NULL;
	}
	
	/* safe here? */
	DEBUG ("Minix-FS: loaded and ready (k = %lx).", k);
	
	return &minix_filesys;
}

/* Sanity checker, checks a filesystem is minix and then sets up all internal
 * structures accordingly, e.g. superblock and directory increment. Returns
 * E_OK if the filesystem is minix and valid, negative error number otherwise.
 */

int
minix_sanity (int drv)
{
	DI *di;
	super_info *psblk;
	bufr temp;
	
	DEBUG ("Minix-FS (%c): minix_sanity enter (bufr = %lx).", drv+'A', &temp);
	
	psblk = Kmalloc (sizeof (super_info));
	if (!psblk)
	{
		ALERT ("Minix-FS (%c): No memory for super_info structure.", drv+'A');
		return EDRIVE;
	}
	
	di = bio.get_di (drv);
	if (!di)
	{
		DEBUG ("Minix-FS (%c): Cannot access partition.", drv+'A');
		Kfree (psblk);
		return EDRIVE;
	}
	
	if (di->pssize != 512 && di->pssize != 1024)
	{
		DEBUG ("Minix-FS (%c): Cannot access partition with sectorsize > 1024.", drv+'A');
		goto failure;
	}
	
	bio.set_lshift (di, 1024);
	
	super_ptr[drv] = psblk;
	psblk->dev = drv;
	psblk->di = di;
	
	BIO_RWABS (psblk->di, 0, &temp, BLOCK_SIZE, SUPER_BLOCK);
	psblk->sblk = *((super_block *) &temp);
	
	if ((psblk->sblk.s_magic == SUPER_V2) && psblk->sblk.s_ninodes)
	{
		d_inode rip;
		long maps;
		
		if (psblk->sblk.s_log_zsize)
		{
			ALERT ("Minix-FS (%c): Cannot read Drive, Zone-size > Block-size", drv+'A');
			goto failure;
		}
		
		TRACE ("Minix-FS (%c): V2 filesystem", drv+'A');
		psblk->ipb = INODES_PER_BLOCK2;
		psblk->zpind = NR_INDIRECTS2;
		psblk->dzpi = NR_DZONE_NUM2;
		psblk->ndbl = NR_DBL2;
		
		maps = psblk->sblk.s_imap_blks + psblk->sblk.s_zmap_blks;
		psblk->ioff = maps + 2;
		
		read_inode (ROOT_INODE, &rip, drv);
		if (IS_DIR (rip))
		{
			void *p;
			int dot = -1;
			int dotdot = -1;
			int i;
			
			maps *= BLOCK_SIZE;
			p = Kmalloc (maps);
			if (!p)
			{
				ALERT ("Minix-FS (%c): No memory for bitmaps!", drv+'A');
				goto failure;
			}
			psblk->ibitmap = p;
			psblk->zbitmap = p + BLOCK_SIZE * psblk->sblk.s_imap_blks;
			
			BIO_RWABS (psblk->di, 2, p, maps, 2);
			
			psblk->idirty = 0;
			psblk->zdirty = 0;
			psblk->zlast = 0;
			psblk->ilast = 0;
			
			/* Final step , read in the root directory zone 1 and
			 * check the '.' and '..' spacing , The spacing
			 * between the '.' and '..' will be used as an
			 * indicator of the directory entry size. If in doubt
			 * assume a normal minix filesystem.
			 */
			read_zone (rip.i_zone[0], &temp, drv);
			
			for (i = 0; i < min (NR_DIR_ENTRIES, rip.i_size / DIR_ENTRY_SIZE); i++)
			{
				if (temp.bdir[i].d_inum)
				{
					if (!strcmp (temp.bdir[i].d_name, "."))
					{
						if (dot == -1)
						{
							dot = i;
						}
						else
						{
							ALERT ("Minix-FS (%c): multiple \".\" in root dir!", drv+'A');
							dot = -1;
							i = NR_DIR_ENTRIES;
						}
					}

					if (!strcmp (temp.bdir[i].d_name, ".."))
					{
						if (dotdot == -1)
						{
							dotdot = i;
						}
						else
						{
							ALERT ("Minix-FS (%c): multiple \"..\" in root dir!", drv+'A');
							dotdot = -1;
							i = NR_DIR_ENTRIES;
						}
					}
				}
			}
			
			if ((dotdot == -1) || (dot == -1))
			{
				ALERT ("Minix-FS (%c): no '.' or '..' in root dir", drv+'A');
				
				Kfree (psblk->ibitmap);
				goto failure;
			}
			
			psblk->incr = dotdot - dot;
			
			if ((psblk->incr < 1) || NPOW2 (psblk->incr))
			{
				ALERT ("Minix-FS (%c): weird '.' '..' positions", drv+'A');
				
				Kfree (psblk->ibitmap);
				goto failure;
			}

			if (psblk->incr > MAX_INCREMENT)
			{
				ALERT ("Minix-FS (%c): Increment %d", drv+'A', psblk->incr);
				ALERT ("Minix-FS (%c): This minix.xfs binary can only handle %d", drv+'A', MAX_INCREMENT);
				ALERT ("Minix-FS (%c): Recompile with a higher MAX_INCREMENT", drv+'A');
				
				Kfree (psblk->ibitmap);
				goto failure;
			}
			
			DEBUG ("Minix-FS (%c): minix_sanity leave E_OK.", drv+'A');
			return E_OK;
		}
		else
		{
			ALERT ("Minix-FS (%c): root inode is not a directory?", drv+'A');
			
			Kfree (psblk->ibitmap);
			goto failure;
		}
	}
	else
	{
		if ((psblk->sblk.s_magic == SUPER_V1_30) || (psblk->sblk.s_magic == SUPER_MAGIC))
		{
			ALERT ("Minix-FS (%c): V1 filesystem are no longer supported.", drv+'A');
		}
	}
	
failure:
	bio.free_di (di);
	Kfree (psblk);
	super_ptr[drv] = NULL;
	
	DEBUG ("Minix-FS (%c): minix_sanity leave EDRIVE", drv+'A');
	return EDRIVE;
}
