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

/*

Copyright 1990,1991,1992 Eric R. Smith.

Copyright 1992,1993,1994 Atari Corporation.

All rights reserved.

*/



/* DOS file handling routines */



#include "mint.h"



static long do_dup P_((int,int));

static void unselectme P_((PROC *));



MEMREGION *tofreed;	/* to-be-freed shared text region (set in denyshare) */



/* wait condition for selecting processes which got collisions */

short select_coll;



/*

 * first, some utility routines

 */



FILEPTR *

do_open(name, rwmode, attr, x)

	const char *name;	/* file name */

	int rwmode;	/* file access mode */

	int attr;	/* TOS attributes for created files (if applicable) */

	XATTR *x;	/* filled in with attributes of opened file */

{

	struct tty *tty;

	fcookie dir, fc;

	long devsp;

	FILEPTR *f;

	DEVDRV *dev;

	long r;

	XATTR xattr;

	unsigned perm;

	int creating;

	char temp1[PATH_MAX];

	extern FILESYS proc_filesys;



/* for special BIOS "fake" devices */

	extern DEVDRV fakedev;



	TRACE(("do_open(%s)", name));



/*

 * first step: get a cookie for the directory

 */



	r = path2cookie(name, temp1, &dir);

	if (r) {

		mint_errno = (int)r;

		DEBUG(("do_open(%s): error %ld", name, r));

		return NULL;

	}



/*

 * second step: try to locate the file itself

 */

	r = relpath2cookie(&dir, temp1, follow_links, &fc, 0);



/*

 * file found: this is an error if (O_CREAT|O_EXCL) are set

 */



	if ( (r == 0) && ( (rwmode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) ) ) {

		DEBUG(("do_open(%s): file already exists",name));

		mint_errno = EACCDN;

		release_cookie(&fc);

		release_cookie(&dir);

		return NULL;

	}

/*

 * file not found: maybe we should create it

 * note that if r != 0, the fc cookie is invalid (so we don't need to

 * release it)

 */

	if (r == EFILNF && (rwmode & O_CREAT)) {

	/* check first for write permission in the directory */

		r = (*dir.fs->getxattr)(&dir, &xattr);

		if (r == 0) {

			if (denyaccess(&xattr, S_IWOTH))

				r = EACCDN;

		}

		if (r) {

			DEBUG(("do_open(%s): couldn't get "

			      "write permission on directory",name));

			mint_errno = (int)r;

			release_cookie(&dir);

			return NULL;

		}

		r = (*dir.fs->creat)(&dir, temp1,

			(S_IFREG|DEFAULT_MODE) & (~curproc->umask), attr, &fc);

		if (r) {

			DEBUG(("do_open(%s): error %ld while creating file",

				name, r));

			mint_errno = (int)r;

			release_cookie(&dir);

			return NULL;

		}

		creating = 1;

	} else if (r) {

		DEBUG(("do_open(%s): error %ld while searching for file",

			name, r));

		mint_errno = (int)r;

		release_cookie(&dir);

		return NULL;

	} else {

		creating = 0;

	}



/*

 * check now for permission to actually access the file

 */

	r = (*fc.fs->getxattr)(&fc, &xattr);

	if (r) {

		DEBUG(("do_open(%s): couldn't get file attributes",name));

		mint_errno = (int)r;

		release_cookie(&dir);

		release_cookie(&fc);

		return NULL;

	}

/*

 * we don't do directories

 */

	if ( (xattr.mode & S_IFMT) == S_IFDIR ) {

		DEBUG(("do_open(%s): file is a directory",name));

		release_cookie(&dir);

		release_cookie(&fc);

		mint_errno = EFILNF;

		return NULL;

	}



	switch (rwmode & O_RWMODE) {

	case O_WRONLY:

		perm = S_IWOTH;

		break;

	case O_RDWR:

		perm = S_IROTH|S_IWOTH;

		break;

	case O_EXEC:

		perm = (fc.fs->fsflags & FS_NOXBIT) ? S_IROTH : S_IXOTH;

		break;

	case O_RDONLY:

		perm = S_IROTH;

		break;

	default:

		perm = 0;

		ALERT("do_open: bad file access mode: %x", rwmode);

	}

	if (!creating && denyaccess(&xattr, perm)) {

		DEBUG(("do_open(%s): access to file denied",name));

		release_cookie(&dir);

		release_cookie(&fc);

		mint_errno = EACCDN;

		return NULL;

	}



/*

 * an extra check for write access -- even the superuser shouldn't

 * write to files with the FA_RDONLY attribute bit set (unless,

 * we just created the file, or unless the file is on the proc

 * file system and hence FA_RDONLY has a different meaning)

 */

	if ( !creating && (xattr.attr & FA_RDONLY) && fc.fs != &proc_filesys) {

		if ( (rwmode & O_RWMODE) == O_RDWR ||

		     (rwmode & O_RWMODE) == O_WRONLY ) {

			DEBUG(("do_open(%s): can't write a read-only file",

				name));

			release_cookie(&dir);

			release_cookie(&fc);

			mint_errno = EACCDN;

			return NULL;

		}

	}



/*

 * if writing to a setuid or setgid file, clear those bits

 */

	if ( (perm & S_IWOTH) && (xattr.mode & (S_ISUID|S_ISGID)) ) {

		xattr.mode &= ~(S_ISUID|S_ISGID);

		(*fc.fs->chmode)(&fc, (xattr.mode & ~S_IFMT));

	}

/*

 * If the caller asked for the attributes of the opened file, copy them over.

 */

	if (x) *x = xattr;



/*

 * So far, so good. Let's get the device driver now, and try to

 * actually open the file.

 */

	dev = (*fc.fs->getdev)(&fc, &devsp);

	if (!dev) {

		mint_errno = (int)devsp;

		DEBUG(("do_open(%s): device driver not found",name));

		release_cookie(&dir);

		release_cookie(&fc);

		return NULL;

	}



	if (dev == &fakedev) {		/* fake BIOS devices */

		f = curproc->handle[devsp];

		if (!f || f == (FILEPTR *)1) {

			mint_errno = EIHNDL;

			return 0;

		}

		f->links++;

		release_cookie(&dir);

		release_cookie(&fc);

		return f;

	}

	if (0 == (f = new_fileptr())) {

		release_cookie(&dir);

		release_cookie(&fc);

		mint_errno = ENSMEM;

		return NULL;

	}

	f->links = 1;

	f->flags = rwmode;

	f->pos = 0;

	f->devinfo = devsp;

	f->fc = fc;

	f->dev = dev;

	release_cookie(&dir);



	r = (*dev->open)(f);

	if (r < 0) {

		DEBUG(("do_open(%s): device open failed with error %ld",

			name, r));

		mint_errno = (int)r;

		f->links = 0;

		release_cookie(&fc);

		dispose_fileptr(f);

		return NULL;

	}



	if (tofreed) {

		tofreed->links = 0;

		free_region(tofreed);

		tofreed = 0;

	}



/* special code for opening a tty */

	if (is_terminal(f)) {

		extern struct tty default_tty;	/* in tty.c */



		tty = (struct tty *)f->devinfo;

		tty->use_cnt++;

		while (tty->hup_ospeed && !creating) {

			sleep (IO_Q, (long)&tty->state);

		}

		/* first open for this device (not counting set_auxhandle)? */

		if ((!tty->pgrp && tty->use_cnt-tty->aux_cnt <= 1) ||

		    tty->use_cnt <= 1) {

			short s = tty->state & (TS_BLIND|TS_HOLD|TS_HPCL);

			short u = tty->use_cnt, a = tty->aux_cnt;

			short r = tty->rsel, w = tty->wsel;

			*tty = default_tty;

			if (!creating)

				tty->state = s;

			if ((tty->use_cnt = u) > 1 || !creating) {

				tty->aux_cnt = a;

				tty->rsel = r, tty->wsel = w;

			}

			if (!(f->flags & O_HEAD)) {

				tty_ioctl(f, TIOCSTART, 0);

			}

		}

	}

	return f;

}



/* 2500 ms after hangup: close device, ready for use again */



static void ARGS_ON_STACK

hangup_done(p, f)

	PROC *p;

	FILEPTR *f;

{

	struct tty *tty = (struct tty *)f->devinfo;



	tty->hup_ospeed = 0;

	tty->state &= ~TS_HPCL;

	tty_ioctl(f, TIOCSTART, 0);

	wake (IO_Q, (long)&tty->state);

	tty->state &= ~TS_HPCL;

	if (--f->links <= 0) {

		if (--tty->use_cnt-tty->aux_cnt <= 0)

			tty->pgrp = 0;

		if (tty->use_cnt <= 0 && tty->xkey) {

			kfree(tty->xkey);

			tty->xkey = 0;

		}

	}



/* hack(?): the closing process may no longer exist, use pid 0 */

	if ((*f->dev->close)(f, 0)) {

		DEBUG(("hangup: device close failed"));

	}

	if (f->links <= 0) {

		release_cookie(&f->fc);

		dispose_fileptr(f);

	}

}



/* 500 ms after hangup: restore DTR */



static void ARGS_ON_STACK

hangup_b1(p, f)

	PROC *p;

	FILEPTR *f;

{

	struct tty *tty = (struct tty *)f->devinfo;

	TIMEOUT *t = addroottimeout(2000L, (void (*)P_((PROC *)))hangup_done, 0);



	if (tty->hup_ospeed > 0)

		(*f->dev->ioctl)(f, TIOCOBAUD, &tty->hup_ospeed);

	if (!t) {

		/* should never happen, but... */

		hangup_done(p, f);

		return;

	}

	t->arg = (long)f;

	tty->hup_ospeed = -1;

}



/*

 * helper function for do_close: this closes the indicated file pointer which

 * is assumed to be associated with process p. The extra parameter is necessary

 * because f_midipipe mucks with file pointers of other processes, so

 * sometimes p != curproc.

 *

 * Note that the function changedrv() in filesys.c can call this routine.

 * in that case, f->dev will be 0 to represent an invalid device, and

 * we cannot call the device close routine.

 */



long

do_pclose(p, f)

	PROC *p;

	FILEPTR *f;

{

	long r = 0;



	if (!f) return EIHNDL;

	if (f == (FILEPTR *)1)

		return 0;



/* if this file is "select'd" by this process, unselect it

 * (this is just in case we were killed by a signal)

 */



/* BUG? Feature? If media change is detected while we're doing the select,

 * we'll never unselect (since f->dev is set to NULL by changedrv())

 */

	if (f->dev) {

		(*f->dev->unselect)(f, (long)p, O_RDONLY);

		(*f->dev->unselect)(f, (long)p, O_WRONLY);

		(*f->dev->unselect)(f, (long)p, O_RDWR);

		wake (SELECT_Q, (long)&select_coll);

	}



	f->links--;



/* TTY manipulation must be done *before* calling the device close routine,

 * since afterwards the TTY structure may no longer exist

 */

	if (is_terminal(f) && f->links <= 0) {

		struct tty *tty = (struct tty *)f->devinfo;

		TIMEOUT *t;

		long ospeed = -1L, z = 0;

/* for HPCL ignore ttys open as /dev/aux, else they would never hang up */

		if (tty->use_cnt-tty->aux_cnt <= 1) {

			if ((tty->state & TS_HPCL) && !tty->hup_ospeed &&

			    !(f->flags & O_HEAD) &&

			    (*f->dev->ioctl)(f, TIOCOBAUD, &ospeed) >= 0 &&

			    (t = addroottimeout(500L, (void (*)P_((PROC *)))hangup_b1, 0))) {

			/* keep device open until hangup complete */

				f->links = 1;

				++tty->use_cnt;

			/* pass f to timeout function */

				t->arg = (long)f;

				(*f->dev->ioctl)(f, TIOCCBRK, 0);

			/* flag: hanging up */

				tty->hup_ospeed = -1;

			/* stop output, flush buffers, drop DTR... */

				tty_ioctl(f, TIOCSTOP, 0);

				tty_ioctl(f, TIOCFLUSH, 0);

				if (ospeed > 0) {

					tty->hup_ospeed = ospeed;

					(*f->dev->ioctl)(f, TIOCOBAUD, &z);

				}

			} else {

				tty->pgrp = 0;

			}

		}

		tty->use_cnt--;

		if (tty->use_cnt <= 0 && tty->xkey) {

			kfree(tty->xkey);

			tty->xkey = 0;

		}

	}



	if (f->dev) {

		r = (*f->dev->close)(f, p->pid);

		if (r) {

			DEBUG(("close: device close failed"));

		}

	}

	if (f->links <= 0) {

		release_cookie(&f->fc);

		dispose_fileptr(f);

	}

	return  r;

}



long

do_close(f)

	FILEPTR *f;

{

	return do_pclose(curproc, f);

}



long ARGS_ON_STACK

f_open(name, mode)

	const char *name;

	int mode;

{

	int i;

	FILEPTR *f;

	PROC *proc;



	TRACE(("Fopen(%s, %x)", name, mode));

#if O_GLOBAL

	if (mode & O_GLOBAL) {

	    /* oh, boy! user wants us to open a global handle! */

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	for (i = MIN_OPEN; i < MAX_OPEN; i++) {

		if (!proc->handle[i])

			goto found_for_open;

	}

	DEBUG(("Fopen(%s): process out of handles",name));

	return ENHNDL;		/* no more handles */



found_for_open:

	mode &= O_USER;		/* make sure the mode is legal */



/* note: file mode 3 is reserved for the kernel; for users, transmogrify it

 * into O_RDWR (mode 2)

 */

	if ( (mode & O_RWMODE) == O_EXEC ) {

		mode = (mode & ~O_RWMODE) | O_RDWR;

	}



	proc->handle[i] = (FILEPTR *)1;	/* reserve this handle */

	f = do_open(name, mode, 0, (XATTR *)0);

	proc->handle[i] = (FILEPTR *)0;



	if (!f) {

		return mint_errno;

	}

	proc->handle[i] = f;

/* default is to close non-standard files on exec */

	proc->fdflags[i] = FD_CLOEXEC;



#if O_GLOBAL

	if (proc != curproc) {

	    /* we just opened a global handle */

	    i += 100;

	}

#endif



	TRACE(("Fopen: returning %d", i));

	return i;

}



long ARGS_ON_STACK

f_create(name, attrib)

	const char *name;

	int attrib;

{

	fcookie dir;

	int i;

	FILEPTR *f;

	long r;

	PROC *proc;

	int offset = 0;

	char temp1[PATH_MAX];



	TRACE(("Fcreate(%s, %x)", name, attrib));

#if O_GLOBAL

	if (attrib & O_GLOBAL) {

		proc = rootproc;

		offset = 100;

		attrib &= ~O_GLOBAL;

	}

	else

#endif

		proc = curproc;



	for (i = MIN_OPEN; i < MAX_OPEN; i++) {

		if (!proc->handle[i])

			goto found_for_create;

	}

	DEBUG(("Fcreate(%s): process out of handles",name));

	return ENHNDL;		/* no more handles */



found_for_create:

	if (attrib == FA_LABEL) {

		r = path2cookie(name, temp1, &dir);

		if (r) return r;

		r = (*dir.fs->writelabel)(&dir, temp1);

		release_cookie(&dir);

		if (r) return r;

/*

 * just in case the caller tries to do something with this handle,

 * make it point to u:\dev\null

 */

		f = do_open("u:\\dev\\null", O_RDWR|O_CREAT|O_TRUNC, 0,

			     (XATTR *)0);

		proc->handle[i] = f;

		proc->fdflags[i] = FD_CLOEXEC;

		return i+offset;

	}

	if (attrib & (FA_LABEL|FA_DIR)) {

		DEBUG(("Fcreate(%s,%x): illegal attributes",name,attrib));

		return EACCDN;

	}



	proc->handle[i] = (FILEPTR *)1;		/* reserve this handle */

	f = do_open(name, O_RDWR|O_CREAT|O_TRUNC, attrib, (XATTR *)0);

	proc->handle[i] = (FILEPTR *)0;



	if (!f) {

		DEBUG(("Fcreate(%s) failed, error %d", name, mint_errno));

		return mint_errno;

	}

	proc->handle[i] = f;

	proc->fdflags[i] = FD_CLOEXEC;

	i += offset;

	TRACE(("Fcreate: returning %d", i));

	return i;

}



long ARGS_ON_STACK

f_close(fh)

	int fh;

{

	FILEPTR *f;

	long r;

	PROC *proc;



	TRACE(("Fclose: %d", fh));

#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < 0 || fh >= MAX_OPEN || 0 == (f = proc->handle[fh])) {

		return EIHNDL;

	}

	r = do_pclose(proc, f);



/* standard handles should be restored to default values */

/* do this for TOS domain only! */

	if (proc->domain == DOM_TOS) {

		if (fh == 0 || fh == 1)

			f = proc->handle[-1];

		else if (fh == 2 || fh == 3)

			f = proc->handle[-fh];

		else

			f = 0;

	} else

		f = 0;



	if (f) {

		f->links++;

		proc->fdflags[fh] = 0;

	}

	proc->handle[fh] = f;

	return r;

}



long ARGS_ON_STACK

f_read(fh, count, buf)

	int fh;

	long count;

	char *buf;

{

	FILEPTR *f;



	PROC *proc;



#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN || 0 == (f = proc->handle[fh])) {

		DEBUG(("Fread: invalid handle: %d", fh));

		return EIHNDL;

	}

	if ( (f->flags & O_RWMODE) == O_WRONLY ) {

		DEBUG(("Fread: read on a write-only handle"));

		return EACCDN;

	}

	if (is_terminal(f))

		return tty_read(f, buf, count);



	TRACELOW(("Fread: %ld bytes from handle %d to %lx", count, fh, buf));

	return (*f->dev->read)(f, buf, count);

}



long ARGS_ON_STACK

f_write(fh, count, buf)

	int fh;

	long count;

	const char *buf;

{

	FILEPTR *f;

	PROC *proc;

	long r;



#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN || 0 == (f = proc->handle[fh])) {

		DEBUG(("Fwrite: bad handle: %d", fh));

		return EIHNDL;

	}

	if ( (f->flags & O_RWMODE) == O_RDONLY ) {

		DEBUG(("Fwrite: write on a read-only handle"));

		return EACCDN;

	}

	if (is_terminal(f))

		return tty_write(f, buf, count);



	/* it would be faster to do this in the device driver, but this

	 * way the drivers are easier to write

	 */

	if (f->flags & O_APPEND) {

		r = (*f->dev->lseek)(f, 0L, SEEK_END);

		/* ignore errors from unseekable files (e.g. pipes) */

		if (r == EACCDN)

			r = 0;

	} else

		r = 0;

	if (r >= 0) {

		TRACELOW(("Fwrite: %ld bytes to handle %d", count, fh));

		r = (*f->dev->write)(f, buf, count);

	}

	if (r < 0) {

		DEBUG(("Fwrite: error %ld", r));

	}

	return r;

}



long ARGS_ON_STACK

f_seek(place, fh, how)

	long place;

	int fh;

	int how;

{

	FILEPTR *f;

	PROC *proc;



	TRACE(("Fseek(%ld, %d) on handle %d", place, how, fh));

#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN || 0 == (f = proc->handle[fh])) {

		DEBUG(("Fseek: bad handle: %d", fh));

		return EIHNDL;

	}

	if (is_terminal(f)) {

		return 0;

	}

	return (*f->dev->lseek)(f, place, how);

}



/* duplicate file pointer fh; returns a new file pointer >= min, if

   one exists, or ENHNDL if not. called by f_dup and f_cntl

 */



static long do_dup(fh, min)

	int fh, min;

{

	FILEPTR *f;

	int i;

	PROC *proc;



	for (i = min; i < MAX_OPEN; i++) {

		if (!curproc->handle[i])

			goto found;

	}

	return ENHNDL;		/* no more handles */

found:

#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	} else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN || 0 == (f = proc->handle[fh]))

		return EIHNDL;



	curproc->handle[i] = f;



/* set default file descriptor flags */

	if (i >= 0) {

		if (i >= MIN_OPEN)

			curproc->fdflags[i] = FD_CLOEXEC;

		else

			curproc->fdflags[i] = 0;

	}

	f->links++;

	return i;

}



long ARGS_ON_STACK

f_dup(fh)

	int fh;

{

	long r;

	r = do_dup(fh, MIN_OPEN);

	TRACE(("Fdup(%d) -> %ld", fh, r));

	return r;

}



long ARGS_ON_STACK

f_force(newh, oldh)

	int newh;

	int oldh;

{

	FILEPTR *f;

	PROC *proc;



	TRACE(("Fforce(%d, %d)", newh, oldh));



#if O_GLOBAL

	if (oldh >= 100) {

	    oldh -= 100;

	    proc = rootproc;

	} else

#endif

	    proc = curproc;



	if (oldh < MIN_HANDLE || oldh >= MAX_OPEN ||

	    0 == (f = proc->handle[oldh])) {

		DEBUG(("Fforce: old handle invalid"));

		return EIHNDL;

	}



	if (newh < MIN_HANDLE || newh >= MAX_OPEN) {

		DEBUG(("Fforce: new handle out of range"));

		return EIHNDL;

	}



	(void)do_close(curproc->handle[newh]);

	curproc->handle[newh] = f;

	/* set default file descriptor flags */

	if (newh >= 0)

		curproc->fdflags[newh] = (newh >= MIN_OPEN) ? FD_CLOEXEC : 0;

	f->links++;

/*

 * special: for a tty, if this is becoming a control terminal and the

 * tty doesn't have a pgrp yet, make it have the pgrp of the process

 * doing the Fforce

 */

	if (is_terminal(f) && newh == -1 && !(f->flags & O_HEAD)) {

		struct tty *tty = (struct tty *)f->devinfo;



		if (!tty->pgrp) {

			tty->pgrp = curproc->pgrp;



			if (!(f->flags & O_NDELAY) && (tty->state & TS_BLIND))

				(*f->dev->ioctl)(f, TIOCWONLINE, 0);

		}

	}

	return 0;

}



long ARGS_ON_STACK

f_datime(timeptr, fh, rwflag)

	short *timeptr;

	int fh;

	int rwflag;

{

	FILEPTR *f;

	PROC *proc;



	TRACE(("Fdatime(%d)", fh));

#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN || 0 == (f = proc->handle[fh])) {

		DEBUG(("Fdatime: invalid handle"));

		return EIHNDL;

	}



/* some programs use Fdatime to test for TTY devices */

	if (is_terminal(f))

		return EACCDN;



	return (*f->dev->datime)(f, timeptr, rwflag);

}



long ARGS_ON_STACK

f_lock(fh, mode, start, length)

	int fh, mode;

	long start, length;

{

	FILEPTR *f;

	struct flock lock;

	PROC *proc;



#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN || 0 == (f = proc->handle[fh])) {

		DEBUG(("Flock: invalid handle"));

		return EIHNDL;

	}

	TRACE(("Flock(%d,%d,%ld,%ld)", fh, mode, start, length));

	lock.l_whence = SEEK_SET;

	lock.l_start = start;

	lock.l_len = length;



	if (mode == 0)		/* create a lock */

		lock.l_type = F_WRLCK;

	else if (mode == 1)	/* unlock region */

		lock.l_type = F_UNLCK;

	else

		return EINVFN;



	return (*f->dev->ioctl)(f, F_SETLK, &lock);

}



/*

 * extensions to GEMDOS:

 */



/*

 * Fpipe(int *handles): opens a pipe. if successful, returns 0, and

 * sets handles[0] to a file descriptor for the read end of the pipe

 * and handles[1] to one for the write end.

 */



long ARGS_ON_STACK

f_pipe(usrh)

	short *usrh;

{

	FILEPTR *in, *out;

	static int pipeno = 0;

	int i, j;

	char pipename[32]; /* MAGIC: 32 >= strlen "u:\pipe\sys$pipe.000\0" */



	TRACE(("Fpipe"));



/* BUG: more than 999 open pipes hangs the system */

	do {

		ksprintf(pipename, "u:\\pipe\\sys$pipe.%03d", pipeno);

		pipeno++; if (pipeno > 999) pipeno = 0;

		out = do_open(pipename, O_WRONLY|O_CREAT|O_EXCL, FA_RDONLY|FA_HIDDEN|FA_CHANGED,

				 (XATTR *)0);

			/* read-only attribute means unidirectional fifo */

			/* hidden attribute means check for broken pipes */

			/* changed attribute means act like Unix fifos */

	} while (out == 0 && mint_errno == EACCDN);



	if (!out) {

		DEBUG(("Fpipe: error %d", mint_errno));

		return mint_errno;

	}



	in = do_open(pipename, O_RDONLY, 0, (XATTR *)0);

	if (!in) {

		DEBUG(("Fpipe: in side of pipe not opened (error %d)",

			mint_errno));

		(void)do_close(out);

		return mint_errno;

	}



	for (i = MIN_OPEN; i < MAX_OPEN; i++) {

		if (curproc->handle[i] == 0)

			break;

	}



	for (j = i+1; j < MAX_OPEN; j++) {

		if (curproc->handle[j] == 0)

			break;

	}



	if (j >= MAX_OPEN) {

		DEBUG(("Fpipe: not enough handles left"));

		(void) do_close(in);

		(void) do_close(out);

		return ENHNDL;

	}

	curproc->handle[i] = in; curproc->handle[j] = out;

/* leave pipes open across Pexec */

	curproc->fdflags[i] = 0;

	curproc->fdflags[j] = 0;



	usrh[0] = i;

	usrh[1] = j;

	TRACE(("Fpipe: returning 0: input %d output %d",i,j));

	return 0;

}



/*

 * f_cntl: a combination "ioctl" and "fcntl". Some functions are

 * handled here, if they apply to the file descriptors directly

 * (e.g. F_DUPFD) or if they're easily translated into file system

 * functions (e.g. FSTAT). Others are passed on to the device driver

 * via dev->ioctl.

 */



long ARGS_ON_STACK

f_cntl(fh, arg, cmd)

	int fh;

	long arg;

	int cmd;

{

	FILEPTR	*f;

	PROC *proc;

	struct flock *fl;

	long r;



	TRACE(("Fcntl(%d, cmd=0x%x)", fh, cmd));

#if O_GLOBAL

	if (fh >= 100) {

	    fh -= 100;

	    proc = rootproc;

	}

	else

#endif

	    proc = curproc;



	if (fh < MIN_HANDLE || fh >= MAX_OPEN) {

		DEBUG(("Fcntl: bad file handle"));

		return EIHNDL;

	}



	if (cmd == F_DUPFD) {

#if O_GLOBAL

		if (proc != curproc) fh += 100;

#endif

  		return do_dup(fh, (int)arg);

	}



	f = proc->handle[fh];

	if (!f) return EIHNDL;



	switch(cmd) {

	case F_GETFD:

		TRACE(("Fcntl F_GETFD"));

		if (fh < 0) return EIHNDL;

		return proc->fdflags[fh];

	case F_SETFD:

		TRACE(("Fcntl F_SETFD"));

		if (fh < 0) return EIHNDL;

		proc->fdflags[fh] = arg;

		return 0;

	case F_GETFL:

		TRACE(("Fcntl F_GETFL"));

		return f->flags & O_USER;

	case F_SETFL:

		TRACE(("Fcntl F_SETFL"));

		arg &= O_USER;		/* make sure only user bits set */

#if 0

	/* COMPATIBILITY WITH OLD VERSIONS ONLY */

	/* THIS CODE WILL GO AWAY. REALLY! */

		if (arg & 4) {

			arg |= O_NDELAY;

			arg &= ~4;

		}

#endif



	/* make sure the file access and sharing modes are not changed */

		arg &= ~(O_RWMODE|O_SHMODE);

		arg |= f->flags & (O_RWMODE|O_SHMODE);

		f->flags &= ~O_USER;	/* set user bits to arg */

		f->flags |= arg;

		return 0;

	case FSTAT:

		return (*f->fc.fs->getxattr)(&f->fc, (XATTR *)arg);

	case F_SETLK:

	case F_SETLKW:

	/* make sure that the file was opened with appropriate permissions */

		fl = (struct flock *)arg;

		if (fl->l_type == F_RDLCK) {

			if ( (f->flags & O_RWMODE) == O_WRONLY )

				return EACCDN;

		} else {

			if ( (f->flags & O_RWMODE) == O_RDONLY )

				return EACCDN;

		}

		/* fall through to device ioctl */

	default:

		TRACE(("Fcntl mode %x: calling ioctl",cmd));

		if (is_terminal(f)) {

			/* tty in the middle of a hangup? */

			while (((struct tty *)f->devinfo)->hup_ospeed) {

				sleep (IO_Q, (long)&((struct tty *)f->devinfo)->state);

			}

			if (cmd == FIONREAD || cmd == FIONWRITE ||

			    cmd == TIOCSTART || cmd == TIOCSTOP ||

			    cmd == TIOCSBRK || cmd == TIOCFLUSH) {

				r = tty_ioctl(f, cmd, (void *)arg);

			} else {

				r = (*f->dev->ioctl)(f, cmd, (void *)arg);

				if (r == EINVFN) {

					r = tty_ioctl(f, cmd, (void *)arg);

				}

			}

		} else {

			r = (*f->dev->ioctl)(f, cmd, (void *)arg);

		}

		return r;

	}

}



/*

 * fselect(timeout, rfd, wfd, xfd)

 * timeout is an (unsigned) 16 bit integer giving the maximum number

 * of milliseconds to wait; rfd, wfd, and xfd are pointers to 32 bit

 * integers containing bitmasks that describe which file descriptors

 * we're interested in. These masks are changed to represent which

 * file descriptors actually have data waiting (rfd), are ready to

 * output (wfd), or have exceptional conditions (xfd). If timeout is 0,

 * fselect blocks until some file descriptor is ready; otherwise, it

 * waits only "timeout" milliseconds. Return value: number of file

 * descriptors that are available for reading/writing; or a negative

 * error number.

 */



/* helper function for time outs */

static void

unselectme(p)

	PROC *p;

{

	wakeselect((long)p);

}



long ARGS_ON_STACK

f_select(timeout, rfdp, wfdp, xfdp)

	unsigned timeout;

	long *rfdp, *wfdp, *xfdp;

{

	long rfd, wfd, xfd, col_rfd, col_wfd, col_xfd;

	long mask, bytes;

	int i, count;

	FILEPTR *f;

	PROC *p;

	TIMEOUT *t;

	int rsel;

	long wait_cond;

	short sr;



	if (rfdp) {

		col_rfd = rfd = *rfdp;

	}

	else

		col_rfd = rfd = 0;



	if (wfdp) {

		col_wfd = wfd = *wfdp;

	}

	else

		col_wfd = wfd = 0;

	if (xfdp) {

		col_xfd = xfd = *xfdp;

	} else {

		col_xfd = xfd = 0;

	}



	/* watch out for aliasing */

	if (rfdp) *rfdp = 0;

	if (wfdp) *wfdp = 0;

	if (xfdp) *xfdp = 0;



	t = 0;



	TRACE(("Fselect(%u, %lx, %lx, %lx)", timeout, rfd, wfd, xfd));

	p = curproc;			/* help the optimizer out */



	/* first, validate the masks */

	mask = 1L;

	for (i = 0; i < MAX_OPEN; i++) {

		if ( ((rfd & mask) || (wfd & mask) || (xfd & mask)) && !(p->handle[i]) ) {

			DEBUG(("Fselect: invalid handle: %d", i));

			return EIHNDL;

		}

		mask = mask << 1L;

	}



/* now, loop through the file descriptors, setting up the select process */

/* NOTE: wakeselect will set p->wait_cond to 0 if data arrives during the

 * selection

 * Also note: because of the validation above, we may assume that the

 * file handles are valid here. However, this assumption may no longer

 * be true after we've gone to sleep, since a signal handler may have

 * closed one of the handles.

 */



	curproc->wait_cond = (long)wakeselect;		/* flag */



 

retry_after_collision:

	mask = 1L;

	wait_cond = (long)wakeselect;

	count = 0;

	

	for (i = 0; i < MAX_OPEN; i++) {

		if (col_rfd & mask) {

			f = p->handle[i];

			if (is_terminal(f))

				rsel = (int) tty_select(f, (long)p, O_RDONLY);

			else

				rsel = (int) (*f->dev->select)(f, (long)p, O_RDONLY);

			switch(rsel) {

			case 0:

				col_rfd &= ~mask;

				break;

			case 1:

				count++;

				*rfdp |= mask;

				break;

			case 2:

				wait_cond = (long)&select_coll;

				break;

			}

		}

		if (col_wfd & mask) {

			f = p->handle[i];

			if (is_terminal(f))

				rsel = (int) tty_select(f, (long)p, O_WRONLY);

			else

				rsel = (int) (*f->dev->select)(f, (long)p, O_WRONLY);

			switch(rsel) {

			case 0:

				col_wfd &= ~mask;

				break;

			case 1:

				count++;

				*wfdp |= mask;

				break;

			case 2:

				wait_cond = (long)&select_coll;

				break;

			}

		}

		if (col_xfd & mask) {

			f = p->handle[i];

/* tesche: anybody worried about using O_RDWR for exceptional data? ;) */

			rsel = (*f->dev->select)(f, (long)p, O_RDWR);

/*  tesche: for old device drivers, which don't understand this

 * call, this will never be true and therefore won't disturb us here.

 */

			switch (rsel) {

			case 0:

				col_xfd &= ~mask;

				break;

			case 1:

				count++;

				*xfdp |= mask;

				break;

			case 2:

				wait_cond = (long)&select_coll;

				break;

			}

		}

		mask = mask << 1L;

	}



	if (count == 0) {	/* no data is ready yet */

		if (timeout && !t) {

			t = addtimeout((long)timeout, unselectme);

			timeout = 0;

		}



	/* curproc->wait_cond changes when data arrives or the timeout happens */

		sr = spl7();

		while (curproc->wait_cond == (long)wakeselect) {

			curproc->wait_cond = wait_cond;

			spl(sr);

			/*

			 * The 0x100 tells sleep() to return without sleeping

			 * when curproc->wait_cond changes. This way we don't

			 * need spl7 (avoiding endless serial overruns).

			 * Also fixes a deadlock with checkkeys/checkbttys.

			 * They are called from sleep and may wakeselect()

			 * curproc. But sleep used to reset curproc->wait_cond

			 * to wakeselect causing curproc to sleep forever.

			 */

			if (sleep(SELECT_Q|0x100, wait_cond))

				curproc->wait_cond = 0;

			sr = spl7();

		}

		if (curproc->wait_cond == (long)&select_coll) {

			curproc->wait_cond = (long)wakeselect;

			spl(sr);

			goto retry_after_collision;

		}

		spl(sr);



	/* we can cancel the time out now (if it hasn't already happened) */

		if (t) canceltimeout(t);



	/* OK, let's see what data arrived (if any) */

		mask = 1L;

		for (i = 0; i < MAX_OPEN; i++) {

			if (rfd & mask) {

				f = p->handle[i];

				if (f) {

				    bytes = 1L;

				    if (is_terminal(f))

					(void)tty_ioctl(f, FIONREAD, &bytes);

				    else

					(void)(*f->dev->ioctl)(f, FIONREAD,&bytes);

				    if (bytes > 0) {

					*rfdp |= mask;

					count++;

				    }

				}

			}

			if (wfd & mask) {

				f = p->handle[i];

				if (f) {

				    bytes = 1L;

				    if (is_terminal(f))

					(void)tty_ioctl(f, FIONWRITE, &bytes);

				    else

				        (void)(*f->dev->ioctl)(f, FIONWRITE,&bytes);

				    if (bytes > 0) {

					*wfdp |= mask;

					count++;

				    }

				}

			}

			if (xfd & mask) {

				f = p->handle[i];

				if (f) {

/*  tesche: since old device drivers won't understand this call,

 * we set up `no exceptional condition' as default.

 */

				    bytes = 0L;

				    (void)(*f->dev->ioctl)(f, FIOEXCEPT,&bytes);

				    if (bytes > 0) {

					*xfdp |= mask;

					count++;

				    }

				}

			}

			mask = mask << 1L;

		}

	} else if (t) {

		/* in case data arrived after a collsion, there

		 * could be a timeout pending even if count > 0

		 */

		canceltimeout(t);

	}



	/* at this point, we either have data or a time out */

	/* cancel all the selects */

	mask = 1L;



	for (i = 0; i < MAX_OPEN; i++) {

		if (rfd & mask) {

			f = p->handle[i];

			if (f)

				(*f->dev->unselect)(f, (long)p, O_RDONLY);

		}

		if (wfd & mask) {

			f = p->handle[i];

			if (f)

				(*f->dev->unselect)(f, (long)p, O_WRONLY);

		}

		if (xfd & mask) {

			f = p->handle[i];

			if (f)

				(*f->dev->unselect)(f, (long)p, O_RDWR);

		}

		mask = mask << 1L;

	}



	/* wake other processes which got a collision */

	if (rfd || wfd || xfd)

		wake(SELECT_Q, (long)&select_coll);



	TRACE(("Fselect: returning %d", count));

	return count;

}





/*

 * GEMDOS extension: Fmidipipe

 * Fmidipipe(pid, in, out) manipulates the MIDI file handles (handles -4 and -5)

 * of process "pid" so that they now point to the files with handles "in" and

 * "out" in the calling process

 */



long ARGS_ON_STACK

f_midipipe(pid, in, out)

	int pid, in, out;

{

	PROC *p;

	FILEPTR *fin, *fout;



/* first, find the process */



	if (pid == 0)

		p = curproc;

	else {

		p = pid2proc(pid);

		if (!p)

			return EFILNF;

	}

 

/* next, validate the input and output file handles */

	if (in < MIN_HANDLE || in >= MAX_OPEN || (0==(fin = curproc->handle[in])))

		return EIHNDL;

	if ( (fin->flags & O_RWMODE) == O_WRONLY ) {

		DEBUG(("Fmidipipe: input side is write only"));

		return EACCDN;

	}

	if (out < MIN_HANDLE || out >= MAX_OPEN || (0==(fout = curproc->handle[out])))

		return EIHNDL;

	if ( (fout->flags & O_RWMODE) == O_RDONLY ) {

		DEBUG(("Fmidipipe: output side is read only"));

		return EACCDN;

	}



/* OK, duplicate the handles and put them in the new process */

	fin->links++; fout->links++;

	(void)do_pclose(p, p->midiin);

	(void)do_pclose(p, p->midiout);

	p->midiin = fin; p->midiout = fout;

	return 0;

}


unix.superglobalmegacorp.com

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