File:  [Atari MiNT] / MiNT / src / tosfs.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:58:52 2018 UTC (8 years, 1 month ago) by root
Branches: mint, MAIN
CVS tags: mint112, HEAD
MiNT 1.12

/*

Copyright 1991,1992 Eric R. Smith.

Copyright 1992,1993,1994 Atari Corporation.

All rights reserved.

*/



/* a VERY simple tosfs.c 

 * this one is extremely brain-damaged, but will serve OK for a

 * skeleton in which to put a "real" tosfs.c

 */



#include "mint.h"



/* if NEWWAY is defined, tosfs uses the new dup_cookie/release_cookie

 * protocol to keep track of file cookies, instead of the old

 * method of "timing"

 */

/* #define NEWWAY */

#if 0

#define COOKIE_DB(x) DEBUG(x)

#else

#define COOKIE_DB(x)

#endif



/* if RO_FASCISM is defined, the read/write modes are enforced. This is

 * a Good Thing, not fascist at all. Ask Allan Pratt why he chose

 * that name sometime.

 */

#define RO_FASCISM



/* temporary code for debugging Falcon media change bug */

#if 0

#define MEDIA_DB(x) DEBUG(x)

#else

#define MEDIA_DB(x)

#endif



/* search mask for anything OTHER THAN a volume label */

#define FILEORDIR 0x37



char tmpbuf[PATH_MAX+1];



static long	ARGS_ON_STACK tos_root	P_((int drv, fcookie *fc));

static long	ARGS_ON_STACK tos_lookup	P_((fcookie *dir, const char *name, fcookie *fc));

static long	ARGS_ON_STACK tos_getxattr	P_((fcookie *fc, XATTR *xattr));

static long	ARGS_ON_STACK tos_chattr	P_((fcookie *fc, int attrib));

static long	ARGS_ON_STACK tos_chown	P_((fcookie *fc, int uid, int gid));

static long	ARGS_ON_STACK tos_chmode	P_((fcookie *fc, unsigned mode));

static long	ARGS_ON_STACK tos_mkdir	P_((fcookie *dir, const char *name, unsigned mode));

static long	ARGS_ON_STACK tos_rmdir	P_((fcookie *dir, const char *name));

static long	ARGS_ON_STACK tos_remove	P_((fcookie *dir, const char *name));

static long	ARGS_ON_STACK tos_getname	P_((fcookie *root, fcookie *dir,

						    char *pathname, int size));

static long	ARGS_ON_STACK tos_rename	P_((fcookie *olddir, char *oldname,

				    fcookie *newdir, const char *newname));

static long	ARGS_ON_STACK tos_opendir	P_((DIR *dirh, int flags));

static long	ARGS_ON_STACK tos_readdir	P_((DIR *dirh, char *nm, int nmlen, fcookie *));

static long	ARGS_ON_STACK tos_rewinddir	P_((DIR *dirh));

static long	ARGS_ON_STACK tos_closedir	P_((DIR *dirh));

static long	ARGS_ON_STACK tos_pathconf	P_((fcookie *dir, int which));

static long	ARGS_ON_STACK tos_dfree	P_((fcookie *dir, long *buf));

static long	ARGS_ON_STACK tos_writelabel	P_((fcookie *dir, const char *name));

static long	ARGS_ON_STACK tos_readlabel	P_((fcookie *dir, char *name, int namelen));



static long	ARGS_ON_STACK tos_creat	P_((fcookie *dir, const char *name, unsigned mode,

				    int attrib, fcookie *fc));

static DEVDRV *	ARGS_ON_STACK tos_getdev	P_((fcookie *fc, long *devsp));

static long	ARGS_ON_STACK tos_open	P_((FILEPTR *f));

static long	ARGS_ON_STACK tos_write	P_((FILEPTR *f, const char *buf, long bytes));

static long	ARGS_ON_STACK tos_read	P_((FILEPTR *f, char *buf, long bytes));

static long	ARGS_ON_STACK tos_lseek	P_((FILEPTR *f, long where, int whence));

static long	ARGS_ON_STACK tos_ioctl	P_((FILEPTR *f, int mode, void *buf));

static long	ARGS_ON_STACK tos_datime	P_((FILEPTR *f, short *time, int rwflag));

static long	ARGS_ON_STACK tos_close	P_((FILEPTR *f, int pid));

static long	ARGS_ON_STACK tos_dskchng	P_((int drv));



#ifdef NEWWAY

static long	ARGS_ON_STACK tos_release P_((fcookie *fc));

static long	ARGS_ON_STACK tos_dupcookie P_((fcookie *dst, fcookie *src));

#endif



/* some routines from biosfs.c */

extern long	ARGS_ON_STACK null_select	P_((FILEPTR *f, long p, int mode));

extern void	ARGS_ON_STACK null_unselect	P_((FILEPTR *f, long p, int mode));



DEVDRV tos_device = {

	tos_open, tos_write, tos_read, tos_lseek, tos_ioctl, tos_datime,

	tos_close, null_select, null_unselect

};



FILESYS tos_filesys = {

	(FILESYS *)0,

	FS_KNOPARSE | FS_NOXBIT | FS_LONGPATH,

	tos_root,

	tos_lookup, tos_creat, tos_getdev, tos_getxattr,

	tos_chattr, tos_chown, tos_chmode,

	tos_mkdir, tos_rmdir, tos_remove, tos_getname, tos_rename,

	tos_opendir, tos_readdir, tos_rewinddir, tos_closedir,

	tos_pathconf, tos_dfree, tos_writelabel, tos_readlabel,

	nosymlink, noreadlink, nohardlink, nofscntl, tos_dskchng,

#ifdef NEWWAY

	tos_release, tos_dupcookie

#else

	0, 0

#endif

};



/* some utility functions and variables: see end of file */

static DTABUF 	*lastdta;	/* last DTA buffer we asked TOS about */

static DTABUF	foo;

static void do_setdta P_((DTABUF *dta));

static int executable_extension P_((char *));



/* this array keeps track of which drives have been changed */

/* a nonzero entry means that the corresponding drive has been changed,

 * but GEMDOS doesn't know it yet

 */

static char drvchanged[NUM_DRIVES];



/* force TOS to see a media change */

static void force_mediach P_((int drv));

static long ARGS_ON_STACK Newgetbpb P_((int));

static long ARGS_ON_STACK Newmediach P_((int));

static long ARGS_ON_STACK Newrwabs P_((int, void *, int, int, int, long));



#ifdef NEWWAY

#define NUM_INDICES 64

#else

#define NUM_INDICES 128

#define MIN_AGE 8

#endif



struct tindex {

	char *name;		/* full path name */

	FILEPTR *open;		/* fileptrs for this file; OR

				 * count of number of open directories

				 */

	LOCK *locks;		/* locks on this file */

/* file status */

	long  size;

	short time;

	short date;

	short attr;

	short valid;		/* 1 if the above status is still valid */

#ifdef NEWWAY

	short links;		/* how many times index is in use */

#else

	short stamp;		/* age of this index, for garbage collection */

#endif

} gl_ti[NUM_INDICES];



/* temporary index for files found by readdir */

static struct tindex tmpindex;

static char tmpiname[PATH_MAX];



static struct tindex *tstrindex P_((char *s));

static int tfullpath P_((char *result, struct tindex *base, const char *name));

static struct tindex *garbage_collect P_((void));



#ifndef NEWWAY

static short tclock;		/* #calls to tfullpath since last garbage

				   collection */

#endif





#define ROOTPERMS 32

#ifdef ROOTPERMS

/* jr: save access permissions, owner and group for root directories.

   ROOTPERMS is the number of supported drives; 'mode' has ROOTPERMSET

   if it has been set  */



struct tosrootperm {

	ushort uid, gid, mode;

} root_perms[ROOTPERMS];



#define ROOTPERMSET	0x8000

#endif



/* jr: external table, set up by getbpb in bios.c */

extern unsigned short clsizb[];



#define INODE_PER_CRC



#ifdef INODE_PER_CRC



/* jr: use crc algorithm described in P1003.2, D11.2 with crctab as in

   GNU-cksum. Slightly modified to return 0 for the root (instead of ~0) */



static unsigned long crctab[] = { /* CRC polynomial 0xedb88320 */

0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,

0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,

0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,

0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,

0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,

0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,

0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,

0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,

0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,

0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,

0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,

0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,

0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,

0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,

0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,

0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,

0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,

0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,

0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,

0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,

0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,

0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,

0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,

0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,

0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,

0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,

0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,

0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,

0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,

0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,

0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,

0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL

};



#define UPDC32(octet, crc) (((crc) << 8) ^ crctab[((crc) >> 24) ^ (octet)])



static unsigned long

filename_crc (const char *filename)

{

	unsigned long s = 0;

	unsigned int n = 0;

	

	/* skip x: */

	filename += 2;

	

	while (*filename) {

		s = UPDC32 (*filename++, s);

		n++;

	}



	while (n != 0) {

		s = UPDC32 (n & 0377, s);

		n >>= 8;

	}

	

	return s;

}



#endif





/* some extra flags for the attr field */



/*

 * is a string the name of a file with executable extension?

 */

#define FA_EXEC 0x4000

/*

 * should the file be deleted when it is closed?

 */

#define FA_DELETE 0x2000



/*

 * NOTE: call executable_extension only on a DTA name returned from

 * Fsfirst(), not on an arbitrary path, for two reasons: (1) it

 * expects only upper case, and (2) it looks only for the 1st extension,

 * so a folder with a '.' in its name would confuse it.

 */



static int

executable_extension(s)

	char *s;

{

	while (*s && *s != '.') s++;

	if (!*s) return 0;

	s++;

	if (s[0] == 'T') {

		return (s[1] == 'T' && s[2] == 'P') ||

		       (s[1] == 'O' && s[2] == 'S');

	}

	if (s[0] == 'P')

		return s[1] == 'R' && s[2] == 'G';

	if (s[0] == 'A')

		return s[1] == 'P' && s[2] == 'P';

	if (s[0] == 'G')

		return s[1] == 'T' && s[2] == 'P';

	return 0;

}



/*

 * Look in the table of tos indices to see if an index corresponding

 * to this file name already exists. If so, mark it as being used

 * and return it. If not, find an empty slot and make an index for

 * this string. If no empty slots exist, garbage collect and

 * try again.

 *

 * This routine is pretty dumb; we really should use a hash table

 * of some sort

 */



static struct tindex *tstrindex(s)

	char *s;

{

	int i;

	char *r;

	struct tindex *t, *free = 0;



	assert(s != 0);

	t = gl_ti;

	for (i = 0; i < NUM_INDICES; i++, t++) {

		if (t->name && !stricmp(t->name, s)) {

#ifndef NEWWAY

			t->stamp = tclock;	/* update use time */

#endif

			return t;

		}

		else if (!t->name && !free)

			free = t;

	}

	if (!free) {

		free = garbage_collect();

	}

#ifdef NEWWAY

	if (!free) {

		FORCE("tosfs: all slots in use!!");

		FORCE("Links\tName");

		t = gl_ti;

		for (i = 0; i < NUM_INDICES; i++,t++) {

			FORCE("%d\t%s", t->links, t->name);

		}

		FATAL("tosfs: unable to get a file name index");

	}

#else

	if (!free) {

		FATAL("tosfs: unable to get a file name index");

	}

#endif

	r = kmalloc((long)strlen(s)+1);

	if (!r) {

		FATAL("tosfs: unable to allocate space for a file name");

	}

	strcpy(r, s);

	free->name = r;

#ifdef NEWWAY

	free->links = 0;

#else

	free->stamp = tclock;

#endif

	free->open = 0;

	free->locks = 0;



/* check to see if this file was recently returned by opendir() */

#ifndef NEWWAY

	if (tmpindex.valid && tclock - tmpindex.stamp < MIN_AGE &&

	    !stricmp(free->name, tmpindex.name)) {

		free->size = tmpindex.size;

		free->time = tmpindex.time;

		free->date = tmpindex.date;

		free->attr = tmpindex.attr;

		free->valid = 1;

		tmpindex.valid = 0;

	} else

#endif

	{

		free->valid = 0;

		free->attr = 0;

	}

	return free;

}



/*

 * garbage collection routine: for any TOS index older than MIN_AGE,

 * check through all current processes to see if it's in use. If

 * not, free the corresponding string.

 * Returns: a pointer to a newly freed index, or NULL.

 */



/* it's unlikely that the kernel would need to hold onto a file cookie

   for longer than this many calls to tstrindex() without first

   saving the cookie in a directory or file pointer

 */



static struct tindex *

garbage_collect()

{

	struct tindex *free, *t;

	int i;

#ifndef NEWWAY

	fcookie *fc, *gc;

	PROC *p;

	int j;

	int age;

#endif



	free = 0;

	t = gl_ti;

	for (i = 0; i < NUM_INDICES; i++,t++) {

		if (!t->name) continue;

#ifdef NEWWAY

		if (t->links == 0) {

			kfree(t->name);

			t->name = 0;

			if (!free) free = t;

		}

#else

		age = tclock - t->stamp;

		t->stamp = 0;

		assert(age >= 0);

		if (age > MIN_AGE) {

		/* see if any process is using this index */

			if (t->open)

				goto found_index;

			for (p = proclist; p; p = p->gl_next) {

				fc = p->curdir;

				gc = p->root;

				for (j = 0; j < NUM_DRIVES; j++,fc++,gc++) {

					if (( fc->fs == &tos_filesys &&

					      fc->index == (long)t ) ||

					    ( gc->fs == &tos_filesys &&

					      gc->index == (long)t ) )

						goto found_index;

				}

			}

		/* here, we couldn't find the index in use by any proc. */

			kfree(t->name);

			t->name = 0;

			if (!free)

				free = t;

		found_index:

			;

		} else {

	/* make sure that future garbage collections might look at this file */

			t->stamp = -age;

		}

#endif

	}



#ifndef NEWWAY

	tclock = 0;	/* reset the clock */

	tmpindex.valid = 0; /* expire the temporary Fsfirst buffer */

#endif

	return free;

}



#define DIRSEP(c) ((c) == '\\')



static int

tfullpath(result, basei, path)

	char *result;

	struct tindex *basei;

	const char *path;

{

#define TNMTEMP 32

	char *n, name[TNMTEMP+1];

	int namelen, pathlen;

	char *base = basei->name;

	int r = 0;



#ifndef NEWWAY

	basei->stamp = ++tclock;

	if (tclock > 10000) {

	/* garbage collect every so often whether we need it or not */

		(void)garbage_collect();

	}

#endif

	if (!*path) {

		strncpy(result, base, PATH_MAX-1);

		return r;

	}



	strncpy(result, base, PATH_MAX-1);



	pathlen = strlen(result);



/* now path is relative to what's currently in "result" */



	while(*path) {

/* get next name in path */

		n = name; namelen = 0;

		while (*path && !DIRSEP(*path)) {

/* BUG: we really should to the translation to DOS 8.3

 * format *here*, so that really long names are truncated

 * correctly.

 */

			if (namelen < TNMTEMP) {

				*n++ = toupper(*path); path++; namelen++;

			}

			else

				path++;

		}

		*n = 0;

		while (DIRSEP(*path)) path++;

/* check for "." and ".." */

		if (!strcmp(name, ".")) continue;

		if (!strcmp(name, "..")) {

			n = strrchr(result, '\\');

			if (n) {

				*n = 0;

				pathlen = (int)(n - result);

			}

			else r = EMOUNT;

			continue;

		}

		if (pathlen + namelen < PATH_MAX - 1) {

			strcat(result, "\\");

			pathlen++;



	/* make sure the name is restricted to DOS 8.3 format */

			for (base = result; *base; base++)

				;

			n = name;

			namelen = 0;

			while (*n && *n != '.' && namelen++ < 8) {

				*base++ = *n++;

				pathlen++;

			}

			while (*n && *n != '.') n++;

			if (*n == '.' && *(n+1) != 0) {

				*base++ = *n++;

				pathlen++;

				namelen = 0;

				while (*n && namelen++ < 3) {

					*base++ = *n++;

					pathlen++;

				}

			}

			*base = 0;

		}

	}

	return r;

}



static long ARGS_ON_STACK 

tos_root(drv, fc)

	int drv;

	fcookie *fc;

{

	struct tindex *ti;



	ksprintf(tmpbuf, "%c:", drv+'A');

	fc->fs = &tos_filesys;

	fc->dev = drv;

	ti = tstrindex(tmpbuf);

	ti->size = ti->date = ti->time = 0;

	ti->attr = FA_DIR;

	ti->valid = 1;

	fc->index = (long)ti;



/* if the drive has changed, make sure GEMDOS knows it! */

	if (drvchanged[drv]) {

		force_mediach(drv);

	}

#ifdef NEWWAY

	ti->links++;

#endif

	return 0;

}



static long ARGS_ON_STACK 

tos_lookup(dir, name, fc)

	fcookie *dir;

	const char *name;

	fcookie *fc;

{

	long r;

	struct tindex *ti = (struct tindex *)dir->index;



	r = tfullpath(tmpbuf, ti, name);



/* if the name is empty or otherwise trivial, just return the directory */

	if (!strcmp(ti->name, tmpbuf)) {

		*fc = *dir;

#ifdef NEWWAY

		ti->links++;

		COOKIE_DB(("tos_lookup: %s now has %d links", ti->name, ti->links));

#endif 

		return r;

	}



/* is there already an index for this file?? If so, is it up to date?? */

	ti = tstrindex(tmpbuf);

	if (!ti->valid) {

		if (tmpbuf[1] == ':' && tmpbuf[2] == 0) {

			/* a root directory -- lookup always succeeds */

			foo.dta_size = 0;

			foo.dta_date = foo.dta_time = 0;

			foo.dta_attrib = FA_DIR;

			foo.dta_name[0] = 0;

		} else {

			do_setdta(&foo);

			r = Fsfirst(tmpbuf, FILEORDIR);

			if (r) {

DEBUG(("tos_lookup: Fsfirst(%s) returned %ld", tmpbuf, r));

				return r;

			}

		}

		ti->size = foo.dta_size;

		ti->date = foo.dta_date;

		ti->time = foo.dta_time;

		ti->attr = foo.dta_attrib | (ti->attr & FA_DELETE);

		if (executable_extension(foo.dta_name))

			ti->attr |= FA_EXEC;

		ti->valid = 1;

	}

	fc->fs = &tos_filesys;

	fc->index = (long)ti;

	fc->dev = dir->dev;

#ifdef NEWWAY

	ti->links++;

	COOKIE_DB(("tos_lookup: %s now has %d links", ti->name, ti->links));

#endif

	return r;

}



static long ARGS_ON_STACK 

tos_getxattr(fc, xattr)

	fcookie *fc;

	XATTR *xattr;

{

	struct tindex *ti = (struct tindex *)fc->index;

	long r;

#ifndef INODE_PER_CRC

	static long junkindex = 0;

#endif

#ifdef ROOTPERMS

	struct tosrootperm *tp = NULL;

	

	if (fc->dev < ROOTPERMS) tp = &root_perms[fc->dev];

#endif



#ifdef INODE_PER_CRC

	xattr->index = filename_crc (ti->name);

#else

	xattr->index = junkindex++;

#endif



	xattr->dev = fc->dev;

	xattr->rdev = fc->dev;

	xattr->nlink = 1;



	xattr->uid = xattr->gid = 0;



#ifdef ROOTPERMS

	if (tp) {

		xattr->uid = tp->uid;

		xattr->gid = tp->gid;

	}

#endif



#ifndef NEWWAY

	ti->stamp = ++tclock;

#endif



	if (!ti->valid) {

		do_setdta(&foo);

		if (ti->name[2] == 0) {		/* a root directory */

/* actually, this can also happen if a program tries to open a file

 * with an empty name... so we should fail gracefully

 */

			ti->attr = FA_DIR;

			ti->size = 0;

			ti->date = ti->time = 0;

			goto around;

		}

	

		r = Fsfirst(ti->name, FILEORDIR);

		if (r) {

			DEBUG(("tosfs: search error %ld on [%s]", r, ti->name));

			return r;

		}

		ti->size = foo.dta_size;

		ti->date = foo.dta_date;

		ti->time = foo.dta_time;

		ti->attr = foo.dta_attrib | (ti->attr & FA_DELETE);

		if (executable_extension(foo.dta_name))

			ti->attr |= FA_EXEC;

around:

		ti->valid = 1;

	}

	xattr->size = ti->size;



	/* jr: if cluster size unknown, do a getbpb once */

	if (fc->dev < 32 && ! clsizb[fc->dev])

		getbpb (fc->dev);



	xattr->blksize = 1024;

	if (fc->dev < 32 && clsizb[fc->dev])

		xattr->blksize = clsizb[fc->dev];

	

	xattr->nblocks = (xattr->size + xattr->blksize - 1) / xattr->blksize;

	xattr->mdate = xattr->cdate = xattr->adate = ti->date;

	xattr->mtime = xattr->ctime = xattr->atime = ti->time;

	xattr->mode = (ti->attr & FA_DIR) ? (S_IFDIR | DEFAULT_DIRMODE) :

			 (S_IFREG | DEFAULT_MODE);



/* TOS files have permissions rwxrwx--- */

	xattr->mode &= ~(S_IROTH|S_IWOTH|S_IXOTH);



#ifdef ROOTPERMS

	/* when root permissions are set, use them. For regular files,

	unmask x bits */



	if (tp && (tp->mode & ROOTPERMSET)) {

		xattr->mode &= ~DEFAULT_DIRMODE;

		xattr->mode |= (tp->mode & DEFAULT_DIRMODE);



		if (! (ti->attr & FA_DIR))

			xattr->mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);

	}

#endif



	if (ti->attr & FA_RDONLY) {

		xattr->mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);

	}



	if (ti->attr & FA_EXEC) {

		xattr->mode |= (S_IXUSR|S_IXGRP|S_IXOTH);

	}



	xattr->attr = ti->attr & 0xff;

	return 0;

}



static long ARGS_ON_STACK

tos_chattr(fc, attrib)

	fcookie *fc;

	int attrib;

{

	struct tindex *ti = (struct tindex *)fc->index;



	if (ti->attr & FA_DIR) {

		DEBUG(("error: attempt to change attributes of a directory"));

		return EACCDN;

	}

	ti->valid = 0;

	(void)tfullpath(tmpbuf, ti, "");

	return Fattrib(tmpbuf, 1, attrib);

}



static long ARGS_ON_STACK 

tos_chown(dir, uid, gid)

	fcookie *dir;

	int uid, gid;

{

#ifdef ROOTPERMS

	struct tindex *ti = (struct tindex *)dir->index;



	if (dir->dev < ROOTPERMS)

		if (ti->name[2] == '\0')	/* root? */

		{

			root_perms[dir->dev].uid = uid;

			root_perms[dir->dev].gid = gid;

			return 0;	

		}

	

	return EINVFN;

	

#else

	UNUSED(dir); UNUSED(uid); UNUSED(gid);

	return EINVFN;

#endif

}



static long ARGS_ON_STACK 

tos_chmode(fc, mode)

	fcookie *fc;

	unsigned mode;

{

	int oldattr, newattr;

	long r;

	struct tindex *ti = (struct tindex *)fc->index;



#ifdef ROOTPERMS

	if (fc->dev < ROOTPERMS) {

		if (ti->name[2] == '\0') {

			/* root? */

			root_perms[fc->dev].mode = ROOTPERMSET | mode;

			return 0;

		}

	}

#endif



	oldattr = Fattrib(ti->name, 0, 0);

	if (oldattr < 0)

		return oldattr;



	ti->valid = 0;



	if (!(mode & S_IWUSR))

		newattr = oldattr | FA_RDONLY;

	else

		newattr = oldattr & ~FA_RDONLY;

	if (newattr != oldattr)

		r = Fattrib(ti->name, 1, newattr);

	else

		r = 0;

	return (r < 0) ? r : 0;

}



static long ARGS_ON_STACK 

tos_mkdir(dir, name, mode)

	fcookie *dir;

	const char *name;

	unsigned mode;		/* ignored under TOS */

{

	UNUSED(mode);



	(void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);

	tmpindex.valid = 0;



	return Dcreate(tmpbuf);

}



static long ARGS_ON_STACK 

tos_rmdir(dir, name)

	fcookie *dir;

	const char *name;

{

	struct tindex *ti;



	(void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);

	ti = tstrindex(tmpbuf);

	ti->valid = 0;



	return Ddelete(tmpbuf);

}



static long ARGS_ON_STACK 

tos_remove(dir, name)

	fcookie *dir;

	const char *name;

{

	struct tindex *ti;



	(void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);



	ti = tstrindex(tmpbuf);

	if (ti->open) {

		DEBUG(("tos_remove: file is open, will be deleted later"));

		if (ti->attr & FA_RDONLY)

			return EACCDN;

		ti->attr |= FA_DELETE;

		return 0;

	}

	ti->valid = 0;

	return Fdelete(tmpbuf);

}



static long ARGS_ON_STACK 

tos_getname(root, dir, pathname, size)

	fcookie *root, *dir;

	char *pathname;

	int size;

{

	char *rootnam = ((struct tindex *)root->index)->name;

	char *dirnam = ((struct tindex *)dir->index)->name;

	int i;



	i = strlen(rootnam);

	if (strlen(dirnam) < i) {

		DEBUG(("tos_getname: root is longer than path"));

		return EINTRN;

	}

	if (strlen(dirnam+i) < size) {

		strcpy(pathname, dirnam + i);

/*

 * BUG: must be a better way to decide upper/lower case

 */

		if (curproc->domain == DOM_MINT)

			strlwr(pathname);

		return 0;

	} else {

		DEBUG(("tosfs: name too long"));

		return ERANGE;

	}

}



static long ARGS_ON_STACK 

tos_rename(olddir, oldname, newdir, newname)

	fcookie *olddir;

	char *oldname;

	fcookie *newdir;

	const char *newname;

{

	char newbuf[128];

	struct tindex *ti;

	long r;



	(void)tfullpath(tmpbuf, (struct tindex *)olddir->index, oldname);

	(void)tfullpath(newbuf, (struct tindex *)newdir->index, newname);

	r = Frename(0, tmpbuf, newbuf);

	if (r == 0) {

		ti = tstrindex(tmpbuf);

		kfree(ti->name);

		ti->name = kmalloc((long)strlen(newbuf)+1);

		if (!ti->name) {

			FATAL("tosfs: unable to allocate space for a name");

		}

		strcpy(ti->name, newbuf);

		ti->valid = 0;

	}

	return r;

}



#define DIR_FLAG(x)	(x->fsstuff[0])

#define STARTSEARCH	0	/* opendir() was just called */

#define INSEARCH	1	/* readdir() has been called at least once */

#define NMFILE		2	/* no more files to read */



#define DIR_DTA(x)	((DTABUF *)(x->fsstuff + 2))

#define DIR_NAME(x)	(x->fsstuff + 32)



/*

 * The directory functions are a bit tricky. What we do is have

 * opendir() do Fsfirst; the first readdir() picks up this name,

 * subsequent readdir()'s have to do Fsnext

 */



static long ARGS_ON_STACK 

tos_opendir(dirh, flags)

	DIR *dirh;

	int flags;

{

	long r;

	struct tindex *t = (struct tindex *)dirh->fc.index;



	UNUSED(flags);



	(void)tfullpath(tmpbuf, t, "*.*");



	do_setdta(DIR_DTA(dirh));



	r = Fsfirst(tmpbuf, FILEORDIR);



	if (r == 0) {

		t->open++;

		DIR_FLAG(dirh) = STARTSEARCH;

		return 0;

	} else if (r == EFILNF) {

		t->open++;

		DIR_FLAG(dirh) = NMFILE;

		return 0;

	}

 	return r;

}



static long ARGS_ON_STACK 

tos_readdir(dirh, name, namelen, fc)

	DIR *dirh;

	char *name;

	int namelen;

	fcookie *fc;

{

	static long index = 0;

	long ret;

	int giveindex = dirh->flags == 0;

	struct tindex *ti;

	DTABUF *dta = DIR_DTA(dirh);



again:

	if (DIR_FLAG(dirh) == NMFILE)

		return ENMFIL;



	if (DIR_FLAG(dirh) == STARTSEARCH) {

		DIR_FLAG(dirh) = INSEARCH;

	} else {

		assert(DIR_FLAG(dirh) == INSEARCH);

		do_setdta(dta);

		ret = Fsnext();

		if (ret) {

			DIR_FLAG(dirh) = NMFILE;

			return ret;

		}

	}



/* don't return volume labels from readdir */

	if (dta->dta_attrib == FA_LABEL) goto again;



	fc->fs = &tos_filesys;

	fc->dev = dirh->fc.dev;



	(void)tfullpath(tmpiname, (struct tindex *)dirh->fc.index, DIR_NAME(dirh));



	ti = &tmpindex;

	ti->name = tmpiname;

	ti->valid = 1;

	ti->size = dta->dta_size;

	ti->date = dta->dta_date;

	ti->time = dta->dta_time;

	ti->attr = dta->dta_attrib;

#ifndef NEWWAY

	ti->stamp = tclock;

#endif

	if (executable_extension(dta->dta_name))

		ti->attr |= FA_EXEC;

	fc->index = (long)ti;



	if (giveindex) {

		namelen -= (int) sizeof(long);

		if (namelen <= 0) return ERANGE;

		*((long *)name) = index++;

		name += sizeof(long);

	}

	strncpy(name, DIR_NAME(dirh), namelen-1);

	name[namelen-1] = 0;



/* BUG: we really should do the "strlwr" operation only

 * for Dreaddir (i.e. if giveindex == 0) but

 * unfortunately some old programs rely on the behaviour

 * below

 */

	if (curproc->domain == DOM_MINT) {

		strlwr(name);

	}

	if (strlen(DIR_NAME(dirh)) >= namelen)

		return ENAMETOOLONG;

#ifdef NEWWAY

	ti->links++;

	COOKIE_DB(("tos_readdir: %s now has %d links", ti->name, ti->links));

#endif

	return 0;

}



static long ARGS_ON_STACK 

tos_rewinddir(dirh)

	DIR *dirh;

{

	struct tindex *ti = (struct tindex *)dirh->fc.index;

	long r;



	(void)tfullpath(tmpbuf, ti, "*.*");

	do_setdta(DIR_DTA(dirh));

	r = Fsfirst(tmpbuf, FILEORDIR);

	if (r == 0) {

		DIR_FLAG(dirh) = STARTSEARCH;

	} else {

		DIR_FLAG(dirh) = NMFILE;

	}

	return r;

}



static long ARGS_ON_STACK 

tos_closedir(dirh)

	DIR *dirh;

{

	struct tindex *t = (struct tindex *)dirh->fc.index;



	if (t->open == 0) {

		FATAL("t->open == 0: directory == %s", t->name);

	}

	--t->open;

	DIR_FLAG(dirh) = NMFILE;

	return 0;

}



static long ARGS_ON_STACK 

tos_pathconf(dir, which)

	fcookie *dir;

	int which;

{

	UNUSED(dir);



	switch(which) {

	case -1:

		return DP_MAXREQ;

	case DP_IOPEN:

		return 60;	/* we can only keep about this many open */

	case DP_MAXLINKS:

		 return 1;	/* no hard links */

	case DP_PATHMAX:

		return PATH_MAX;

	case DP_NAMEMAX:

		return 8+3+1;

	case DP_ATOMIC:

		return 512;	/* we can write at least a sector atomically */

	case DP_TRUNC:

		return DP_DOSTRUNC;	/* DOS style file names */

	case DP_CASE:

		return DP_CASECONV;	/* names converted to upper case */

	case DP_MODEATTR:

		return FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_LABEL|FA_CHANGED|

#ifdef ROOTPERMS

				0666L << 8|

#endif

				DP_FT_DIR|DP_FT_REG;

	case DP_XATTRFIELDS:

		return DP_DEV|DP_NLINK|DP_BLKSIZE|DP_SIZE|DP_NBLOCKS|DP_MTIME;

	default:

		return EINVFN;

	}

}



long ARGS_ON_STACK 

tos_dfree(dir, buf)

	fcookie *dir;

	long *buf;

{

	return Dfree(buf, (dir->dev)+1);

}



/*

 * writelabel: creates a volume label

 * readlabel: reads a volume label

 * both of these are only guaranteed to work in the root directory

 */



/*

 * BUG: this should first delete any old labels, so that it will

 * work with TOS <1.4

 */



long ARGS_ON_STACK 

tos_writelabel(dir, name)

	fcookie *dir;

	const char *name;

{

	long r;

	struct tindex *ti;



	(void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);

	r = Fcreate(tmpbuf, FA_LABEL);

	if (r < 0) return r;

	(void)Fclose((int)r);

	ti = tstrindex(tmpbuf);

	ti->valid = 0;

	return 0;

}



long ARGS_ON_STACK 

tos_readlabel(dir, name, namelen)

	fcookie *dir;

	char *name;

	int namelen;

{

	long r;

	struct tindex *ti = (struct tindex *)dir->index;



	if (ti->name[2] != 0)		/* not a root directory? */

		return EFILNF;



	(void)tfullpath(tmpbuf, ti, "*.*");

	do_setdta(&foo);

	r = Fsfirst(tmpbuf, FA_LABEL);

	if (r)

		return r;

	strncpy(name, foo.dta_name, namelen-1);

	return (strlen(foo.dta_name) < namelen) ? 0 : ENAMETOOLONG;

}



/*

 * TOS creat: this doesn't actually create the file, rather it

 * sets up a (fake) index for the file that will be used by

 * the later tos_open call.

 */



static long ARGS_ON_STACK 

tos_creat(dir, name, mode, attrib, fc)

	fcookie *dir;

	const char *name;

	unsigned mode;

	int attrib;

	fcookie *fc;

{

	struct tindex *ti;

	UNUSED(mode);



	(void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);



	ti = tstrindex(tmpbuf);

	ti->size = 0;

	ti->date = datestamp;

	ti->time = timestamp;

	ti->attr = attrib;

	ti->valid = 1;



	fc->fs = &tos_filesys;

	fc->index = (long)ti;

	fc->dev = dir->dev;

#ifdef NEWWAY

	ti->links++;

	COOKIE_DB(("tos_creat: %s now has %d links", ti->name, ti->links));

#endif

	return 0;

}

	

/*

 * TOS device driver

 */



static DEVDRV * ARGS_ON_STACK 

tos_getdev(fc, devsp)

	fcookie *fc;

	long *devsp;

{

	UNUSED(fc); UNUSED(devsp);

	return &tos_device;

}



static long ARGS_ON_STACK 

tos_open(f)

	FILEPTR *f;

{

	struct tindex *ti;

	int mode = f->flags;

	int tosmode;

	long r;

	extern int flk;		/* in main.c, set if _FLK cookie already present */



	ti = (struct tindex *)(f->fc.index);

	assert(ti != 0);



#ifndef NEWWAY

	ti->stamp = ++tclock;

#endif

	ti->valid = 0;



#ifndef RO_FASCISM

/* TEMPORARY HACK: change all modes to O_RDWR for files opened in

 * compatibility sharing mode. This is silly, but

 * allows broken TOS programs that write to read-only handles to continue

 * to work (it also helps file sharing, by making the realistic assumption

 * that any open TOS file can be written to). Eventually,

 * this should be tuneable by the user somehow.

 * ALSO: change O_COMPAT opens into O_DENYNONE; again, this may be temporary.

 */

	if ( (mode & O_SHMODE) == O_COMPAT ) {

		f->flags = (mode & ~(O_RWMODE|O_SHMODE)) | O_RDWR | O_DENYNONE;

	}

#endif



/* check to see that nobody has opened this file already in an

 * incompatible mode

 */

	if (denyshare(ti->open, f)) {

		TRACE(("tos_open: file sharing denied"));

		return EACCDN;

	}



/*

 * now open the file; if O_TRUNC was specified, actually

 * create the file anew.

 * BUG: O_TRUNC without O_CREAT doesn't work right. The kernel doesn't

 * use this mode, anyways

 */



	if (mode & O_TRUNC) {

		if (ti->open) {

			DEBUG(("tos_open: attempt to truncate an open file"));

			return EACCDN;

		}

		r = Fcreate(ti->name, ti->attr);

	} else {

		if (flk)

			tosmode = mode & (O_RWMODE|O_SHMODE);

		else

			tosmode = (mode & O_RWMODE);

		if (tosmode == O_EXEC) tosmode = O_RDONLY;



		r = Fopen(ti->name, tosmode );

		if (r == EFILNF && (mode & O_CREAT))

			r = Fcreate(ti->name, ti->attr);

	}



	if (r < 0) {

/* get rid of the index for the file, since it doesn't exist */

		kfree(ti->name);

		ti->name = 0;

		ti->valid = 0;

		return r;

	}



	f->devinfo = r;



	f->next = ti->open;

	ti->open = f;

	return 0;

}



static long ARGS_ON_STACK 

tos_write(f, buf, bytes)

	FILEPTR *f; const char *buf; long bytes;

{

	struct tindex *ti = (struct tindex *)f->fc.index;



	ti->valid = 0;

	return Fwrite((int)f->devinfo, bytes, buf);

}



static long ARGS_ON_STACK 

tos_read(f, buf, bytes)

	FILEPTR *f; char *buf; long bytes;

{

	return Fread((int)f->devinfo, bytes, buf);

}



static long ARGS_ON_STACK 

tos_lseek(f, where, whence)

	FILEPTR *f; long where; int whence;

{

	long r;



	r = Fseek(where, (int)f->devinfo, whence);

	return r;

}



static long ARGS_ON_STACK 

tos_ioctl(f, mode, buf)

	FILEPTR *f; int mode; void *buf;

{

	LOCK t, *lck, **old;

	struct flock *fl;

	long r;

	struct tindex *ti;

	extern int flk;		/* set in main.c if _FLK already installed */



	switch(mode) {

	case FIONREAD:

		r = Fseek (0L, (int) f->devinfo, 1);

		if (r < 0) return r;

		*(long *) buf = Fseek (0L, (int) f->devinfo, 2) - r;

		(void) Fseek (r, (int) f->devinfo, 0);

		return 0;

	case FIONWRITE:

		*((long *)buf) = 1;

		return 0;

	case FIOEXCEPT:

		*((long *)buf) = 0;

		return 0;

	case F_SETLK:

	case F_SETLKW:

	case F_GETLK:

		fl = ((struct flock *)buf);

		t.l = *fl;

		switch(t.l.l_whence) {

		case 0:

			break;

		case 1:		/* SEEK_CUR */

			r = Fseek(0L, (int)f->devinfo, 1);

			t.l.l_start += r;

			break;

		case 2:

			r = Fseek(0L, (int)f->devinfo, 1);

			t.l.l_start = Fseek(t.l.l_start, (int)f->devinfo, 2);

			(void)Fseek(r, (int)f->devinfo, 0);

			break;

		default:

			DEBUG(("Invalid value for l_whence"));

			return EINVFN;

		}

/* BUG: can't lock a file starting at >2gigabytes from the beginning */

		if (t.l.l_start < 0) t.l.l_start = 0;

		t.l.l_whence = 0;

		ti = (struct tindex *)f->fc.index;



		if (mode == F_GETLK) {

			lck = denylock(ti->locks, &t);

			if (lck)

				*fl = lck->l;

			else

				fl->l_type = F_UNLCK;

			return 0;

		}



		if (t.l.l_type == F_UNLCK) {

		/* try to find the lock */

			old = &ti->locks;

			lck = *old;

			while (lck) {

				if (lck->l.l_pid == curproc->pid &&

				    lck->l.l_start == t.l.l_start &&

				    lck->l.l_len == t.l.l_len) {

		/* found it -- remove the lock */

					*old = lck->next;

					TRACE(("tosfs: unlocked %s: %ld + %ld",

						ti->name, t.l.l_start, t.l.l_len));

					if (flk)

					    (void)Flock((int)f->devinfo, 1,

							t.l.l_start, t.l.l_len);

				/* wake up anyone waiting on the lock */

					wake(IO_Q, (long)lck);

					kfree(lck);

					break;

				}

				old = &lck->next;

				lck = lck->next;

			}

			return lck ?  0 : ENSLOCK;

		}

		TRACE(("tosfs: lock %s: %ld + %ld", ti->name,

			t.l.l_start, t.l.l_len));

		do {

		/* see if there's a conflicting lock */

			while ((lck = denylock(ti->locks, &t)) != 0) {

				DEBUG(("tosfs: lock conflicts with one held by %d",

					lck->l.l_pid));

				if (mode == F_SETLKW) {

					sleep(IO_Q, (long)lck);		/* sleep a while */

				}

				else

					return ELOCKED;

			}

		/* if not, add this lock to the list */

			lck = kmalloc(SIZEOF(LOCK));

			if (!lck) return ENSMEM;

		/* see if other _FLK code might object */

			if (flk) {

				r = Flock((int)f->devinfo, 0, t.l.l_start, t.l.l_len);

				if (r) {

					kfree(lck);

					if (mode == F_SETLKW && r == ELOCKED) {

						yield();

						lck = NULL;

					}

					else

						return r;

				}

			}

		} while (!lck);

		lck->l = t.l;

		lck->l.l_pid = curproc->pid;

		lck->next = ti->locks;

		ti->locks = lck;

	/* mark the file as being locked */

		f->flags |= O_LOCK;

		return 0;

	}

	return EINVFN;

}



static long ARGS_ON_STACK 

tos_datime(f, timeptr, rwflag)

	FILEPTR *f;

	short *timeptr;

	int rwflag;

{

	if (rwflag) {

		struct tindex *ti = (struct tindex *)f->fc.index;

		ti->valid = 0;

	}

	return Fdatime(timeptr, (int)f->devinfo, rwflag);

}



static long ARGS_ON_STACK 

tos_close(f, pid)

	FILEPTR *f;

	int pid;

{

	LOCK *lck, **oldl;

	struct tindex *t;

	FILEPTR **old, *p;

	long r = 0;

	extern int flk;		/* set in main.c */



	t = (struct tindex *)(f->fc.index);

/* if this handle was locked, remove any locks held by the process

 */

	if (f->flags & O_LOCK) {

		TRACE(("tos_close: releasing locks (file mode: %x)", f->flags));

		oldl = &t->locks;

		lck = *oldl;

		while (lck) {

			if (lck->l.l_pid == pid) {

				*oldl = lck->next;

				if (flk)

					(void)Flock((int)f->devinfo, 1,

						lck->l.l_start, lck->l.l_len);

				wake(IO_Q, (long)lck);

				kfree(lck);

			} else {

				oldl = &lck->next;

			}

			lck = *oldl;

		}

	}



	if (f->links <= 0) {

/* remove f from the list of open file pointers on this index */

		t->valid = 0;

		old = &t->open;

		p = t->open;

		while (p && p != f) {

			old = &p->next;

			p = p->next;

		}

		assert(p);

		*old = f->next;

		f->next = 0;

		r = Fclose((int)f->devinfo);



/* if the file was marked for deletion, delete it */

		if (!t->open) {

			if (t->attr & FA_DELETE) {

				(void)Fdelete(t->name);

				t->name = 0;

			}

		}

	}

	return r;

}



/*

 * check for disk change: called by the kernel if Mediach returns a

 * non-zero value

 */



long ARGS_ON_STACK 

tos_dskchng(drv)

	int drv;

{

	char dlet;

	int i;

	struct tindex *ti;

	FILEPTR *f, *nextf;



	dlet = 'A' + drv;

MEDIA_DB(("tos_dskchng(%c)", dlet));

	ti = gl_ti;

	for (i = 0; i < NUM_INDICES; i++, ti++) {

		if (ti->name && ti->name[0] == dlet) {

#ifdef NEWWAY

	/* only free the name if this index not used by any cookie */

			if (ti->links != 0)

				ti->valid = 0;

			else

#endif /* NEWWAY */

			{

				kfree(ti->name);

				ti->name = 0;

			}

			if (!(ti->attr & FA_DIR)) {

				nextf = ti->open;

				while ( (f = nextf) != 0 ) {

					nextf = f->next;

					f->next = 0;

				}

			}

			ti->open = 0;

	/* if there are any cookies pointing at this name, they're not

	 * valid any more, so we will *want* to get an error if they're

	 * used.

	 */

		}

	}

	tmpindex.valid = 0;

/*

 * OK, make sure that GEMDOS knows to look for a change if we

 * ever use this drive again.

 */

	drvchanged[drv] = 1;

	return 1;

}



#ifdef NEWWAY

/* release/copy file cookies; these functions exist to keep

 * track of whether or not the kernel is still using a file

 */

long

tos_release(fc)

	fcookie *fc;

{

	struct tindex *ti = (struct tindex *)fc->index;



	if (ti->links <= 0) {

		FATAL("tos_release: link count of `%s' is %d", ti->name, ti->links);

	}

	ti->links--;

	COOKIE_DB(("tos_release: %s now has %d links", ti->name, ti->links));

	return 0;

}



long

tos_dupcookie(dest, src)

	fcookie *dest, *src;

{

	struct tindex *ti = (struct tindex *)src->index;



	if (ti->links <= 0) {

		FATAL("tos_dupcookie: link count of %s is %d", ti->name, ti->links);

	}

	ti->links++;

	COOKIE_DB(("tos_dupcookie: %s now has %d links", ti->name, ti->links));

	*dest = *src;

	return 0;

}

#endif



/*

 * utility function: sets the TOS DTA, and also records what directory

 * this was in. This is just to save us a call into the kernel if the

 * correct DTA has already been set.

 */



static void

do_setdta(dta)

	DTABUF *dta;

{

	if (dta != lastdta) {

		Fsetdta(dta);

		lastdta = dta;

	}

}



/*

 * routines for forcing a media change on drive "drv"

 */



static int chdrv;



/* new Getbpb function: when this is called, all the other

 * vectors can be un-installed

 */



static long ARGS_ON_STACK (*Oldgetbpb) P_((int));

static long ARGS_ON_STACK (*Oldmediach) P_((int));

static long ARGS_ON_STACK (*Oldrwabs) P_((int, void *, int, int, int, long));



static long  ARGS_ON_STACK

Newgetbpb(d)

	int d;

{

	if (d == chdrv) {

MEDIA_DB(("Newgetbpb(%c)", d+'A'));

if (Oldgetbpb == Newgetbpb) {

MEDIA_DB(("AARGH!!! BAD BPBs"));

}

		*((Func *)0x472L) = Oldgetbpb;

		*((Func *)0x476L) = Oldrwabs;

		*((Func *)0x47eL) = Oldmediach;

	}

	return (*Oldgetbpb)(d);

}



static long ARGS_ON_STACK

Newmediach(d)

	int d;

{

	if (d == chdrv) {

MEDIA_DB(("Newmediach(%c)", d+'A'));

		return 2;

	}

	return (*Oldmediach)(d);

}



static long ARGS_ON_STACK

Newrwabs(mode, buf, num, recno, dev, l)

	int mode;

	void *buf;

	int num, recno, dev;

	long l;

{

	if (dev == chdrv) {

MEDIA_DB(("Newrwabs"));

		return E_CHNG;

	}

	return (*Oldrwabs)(mode, buf, num, recno, dev, l);

}



static void

force_mediach(d)

	int d;

{

#ifdef FSFIRST_MEDIACH

	static char fname[] = "X:\\*.*";

#else

	long r;

	static char fname[] = "X:\\X";

#endif

	TRACE(("tosfs: disk change drive %c", d+'A'));

MEDIA_DB(("forcing media change on %c", d+'A'));



	chdrv = d;

	Oldrwabs = *((Func *)0x476L);

	Oldgetbpb = *((Func *)0x472L);

	Oldmediach = *((Func *)0x47eL);



	if (Oldrwabs == Newrwabs || Oldgetbpb == Newgetbpb ||

	    Oldmediach == Newmediach) {

		FORCE("tosfs: error in media change code");

	} else {

		*((Func *)0x476L) = Newrwabs;

		*((Func *)0x472L) = Newgetbpb;

		*((Func *)0x47eL) = Newmediach;

	}



	fname[0] = d + 'A';

MEDIA_DB(("calling GEMDOS"));

#ifdef FSFIRST_MEDIACH

	(void)Fsfirst(fname, 8);

#else	

	r = Fopen(fname, 0);

	if (r >= 0) Fclose((int)r);

#endif

MEDIA_DB(("done calling GEMDOS"));

	drvchanged[d] = 0;

	if ( *((Func *)0x476L) == Newrwabs ) {

		DEBUG(("WARNING: media change not performed correctly"));

		*((Func *)0x472L) = Oldgetbpb;

		*((Func *)0x476L) = Oldrwabs;

		*((Func *)0x47eL) = Oldmediach;

	}

}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.