/* 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; }