/* Copyright 1991,1992 Eric R. Smith. 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" /* search mask for anything OTHER THAN a volume label */ #define FILEORDIR 0x37 #ifndef Flock /* this may need to be adjusted for your compiler/library */ extern long gemdos(); #define Flock(handle, mode, start, len) gemdos(0x5c, handle, mode, start, len) #endif char tmpbuf[PATH_MAX+1]; static long tos_root P_((int drv, fcookie *fc)); static long tos_lookup P_((fcookie *dir, const char *name, fcookie *fc)); static long tos_getxattr P_((fcookie *fc, XATTR *xattr)); static long tos_chattr P_((fcookie *fc, int attrib)); static long tos_chown P_((fcookie *fc, int uid, int gid)); static long tos_chmode P_((fcookie *fc, unsigned mode)); static long tos_mkdir P_((fcookie *dir, const char *name, unsigned mode)); static long tos_rmdir P_((fcookie *dir, const char *name)); static long tos_remove P_((fcookie *dir, const char *name)); static long tos_getname P_((fcookie *root, fcookie *dir, char *pathname)); static long tos_rename P_((fcookie *olddir, char *oldname, fcookie *newdir, const char *newname)); static long tos_opendir P_((DIR *dirh, int flags)); static long tos_readdir P_((DIR *dirh, char *nm, int nmlen, fcookie *)); static long tos_rewinddir P_((DIR *dirh)); static long tos_closedir P_((DIR *dirh)); static long tos_pathconf P_((fcookie *dir, int which)); static long tos_dfree P_((fcookie *dir, long *buf)); static long tos_writelabel P_((fcookie *dir, const char *name)); static long tos_readlabel P_((fcookie *dir, char *name, int namelen)); static long tos_creat P_((fcookie *dir, const char *name, unsigned mode, int attrib, fcookie *fc)); static DEVDRV * tos_getdev P_((fcookie *fc, long *devsp)); static long tos_open P_((FILEPTR *f)); static long tos_write P_((FILEPTR *f, const char *buf, long bytes)); static long tos_read P_((FILEPTR *f, char *buf, long bytes)); static long tos_lseek P_((FILEPTR *f, long where, int whence)); static long tos_ioctl P_((FILEPTR *f, int mode, void *buf)); static long tos_datime P_((FILEPTR *f, short *time, int rwflag)); static long tos_close P_((FILEPTR *f, int pid)); static long tos_dskchng P_((int drv)); /* some routines from biosfs.c */ extern long null_select P_((FILEPTR *f, long p, int mode)); extern void 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, 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 }; /* 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 Newgetbpb P_((int)); static long Newmediach P_((int)); static long Newrwabs P_((int, void *, int, int, int, long)); #define NUM_INDICES 128 #define MIN_AGE 8 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 */ short stamp; /* age of this index, for garbage collection */ } 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)); static short tclock; /* #calls to tfullpath since last garbage collection */ /* 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 */ 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)) { t->stamp = tclock; /* update use time */ return t; } else if (!t->name && !free) free = t; } if (!free) { free = garbage_collect(); } if (!free) { FATAL("tosfs: unable to get a file name index"); } r = kmalloc((long)strlen(s)+1); if (!r) { FATAL("tosfs: unable to allocate space for a file name"); } strcpy(r, s); free->name = r; free->stamp = tclock; free->open = 0; free->locks = 0; /* check to see if this file was recently returned by opendir() */ 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 free->valid = 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; fcookie *fc, *gc; PROC *p; int i, j; int age; free = 0; t = gl_ti; for (i = 0; i < NUM_INDICES; i++,t++) { if (!t->name) continue; 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; } } tclock = 0; /* reset the clock */ tmpindex.valid = 0; /* expire the temporary Fsfirst buffer */ 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 dom; int namelen, pathlen; char *base = basei->name; int r = 0; basei->stamp = ++tclock; if (tclock > 10000) { /* garbage collect every so often whether we need it or not */ (void)garbage_collect(); } if (!*path) { strncpy(result, base, PATH_MAX-1); return r; } dom = curproc->domain; 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 = 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 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); } return 0; } static long 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; 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; 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; return r; } static long tos_getxattr(fc, xattr) fcookie *fc; XATTR *xattr; { struct tindex *ti = (struct tindex *)fc->index; long r; static long junkindex = 0; xattr->index = junkindex++; xattr->dev = fc->dev; xattr->nlink = 1; xattr->uid = xattr->gid = 0; ti->stamp = ++tclock; 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 */ TRACE("tosfs: a root directory became invalid??"); goto around; } r = Fsfirst(ti->name, FILEORDIR); if (r < 0) FATAL("tosfs: search error on [%s]", ti->name); ti->size = foo.dta_size; ti->date = foo.dta_date; ti->time = foo.dta_time; ti->attr = foo.dta_attrib; if (executable_extension(foo.dta_name)) ti->attr |= FA_EXEC; around: ti->valid = 1; } xattr->size = ti->size; /* BUG: blksize isn't accurate if the sector size is not 512 */ xattr->blksize = 1024; xattr->nblocks = (xattr->size + 1023) / 1024; 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); 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 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 tos_chown(dir, uid, gid) fcookie *dir; int uid, gid; { return EINVFN; } static long tos_chmode(fc, mode) fcookie *fc; unsigned mode; { int oldattr, newattr; long r; struct tindex *ti = (struct tindex *)fc->index; 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 tos_mkdir(dir, name, mode) fcookie *dir; const char *name; unsigned mode; /* ignored under TOS */ { (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name); tmpindex.valid = 0; return Dcreate(tmpbuf); } static long 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 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 tos_getname(root, dir, pathname) fcookie *root, *dir; char *pathname; { ksprintf(pathname, "%s", ((struct tindex *)dir->index)->name + strlen(((struct tindex*)root->index)->name) ); if (curproc->domain == DOM_MINT) strlwr(pathname); return 0; } static long 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 tos_opendir(dirh, flags) DIR *dirh; int flags; { long r; struct tindex *t = (struct tindex *)dirh->fc.index; (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 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; ti->stamp = tclock; if (executable_extension(dta->dta_name)) ti->attr |= FA_EXEC; fc->index = (long)ti; if (giveindex) { namelen -= sizeof(long); if (namelen <= 0) return ERANGE; *((long *)name) = index++; name += sizeof(long); } strncpy(name, DIR_NAME(dirh), namelen-1); name[namelen-1] = 0; if (curproc->domain == DOM_MINT) { strlwr(name); } if (strlen(DIR_NAME(dirh)) >= namelen) return ENAMETOOLONG; else return 0; } static long 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 tos_closedir(dirh) DIR *dirh; { struct tindex *t = (struct tindex *)dirh->fc.index; assert(t->open); --t->open; DIR_FLAG(dirh) = NMFILE; return 0; } static long tos_pathconf(dir, which) fcookie *dir; int which; { 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 */ default: return EINVFN; } } long 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 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(r); ti = tstrindex(tmpbuf); ti->valid = 0; return 0; } long 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 tos_creat(dir, name, mode, attrib, fc) fcookie *dir; const char *name; unsigned mode; int attrib; fcookie *fc; { struct tindex *ti; (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; return 0; } /* * TOS device driver */ static DEVDRV * tos_getdev(fc, devsp) fcookie *fc; long *devsp; { return &tos_device; } static long 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); ti->stamp = ++tclock; ti->valid = 0; /* 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; } /* 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 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 tos_read(f, buf, bytes) FILEPTR *f; char *buf; long bytes; { return Fread((int)f->devinfo, bytes, buf); } static long tos_lseek(f, where, whence) FILEPTR *f; long where; int whence; { long r; r = Fseek(where, (int)f->devinfo, whence); return r; } static long 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 */ if (mode == FIONREAD || mode == FIONWRITE) { *((long *)buf) = 1; return 0; } else if (mode == F_SETLK || mode == 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); kfree(lck); if (flk) (void)Flock((int)f->devinfo, 1, t.l.l_start, t.l.l_len); 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); /* see if there's a conflicting lock */ lck = denylock(ti->locks, &t); if (lck) { DEBUG("tosfs: lock conflicts with one held by %d", lck->l.l_pid); 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); return r; } } 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 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 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); 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 tos_dskchng(drv) int drv; { char dlet; int i; struct tindex *ti; dlet = 'A' + drv; ti = gl_ti; for (i = 0; i < NUM_INDICES; i++, ti++) { if (ti->name[0] == dlet) { kfree(ti->name); ti->name = 0; } } /* * OK, make sure that GEMDOS knows to look for a change if we * ever use this drive again. */ drvchanged[drv] = 1; return 1; } /* * 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 (*Oldgetbpb) P_((int)); static long (*Oldmediach) P_((int)); static long (*Oldrwabs) P_((int, void *, int, int, int, long)); static long Newgetbpb(d) int d; { if (d == chdrv) { *((Func *)0x472L) = Oldgetbpb; *((Func *)0x476L) = Oldrwabs; *((Func *)0x47eL) = Oldmediach; } return (*Oldgetbpb)(d); } static long Newmediach(d) int d; { if (d == chdrv) return 2; return (*Oldmediach)(d); } static long Newrwabs(d, buf, a, b, c, l) int d; void *buf; int a, b, c; long l; { if (d == chdrv) return E_CHNG; return (*Oldrwabs)(d, buf, a, b, c, l); } static void force_mediach(d) int d; { long r; static char fname[] = "X:\\.INF"; TRACE("tosfs: disk change drive %c", d+'A'); chdrv = d; Oldrwabs = *((Func *)0x476L); if (Oldrwabs == Newrwabs) { ALERT("tosfs: error in media change code"); } else { *((Func *)0x476L) = Newrwabs; Oldmediach = *((Func *)0x47eL); *((Func *)0x47eL) = Newmediach; Oldgetbpb = *((Func *)0x472L); *((Func *)0x472L) = Newgetbpb; } fname[0] = d + 'A'; r = Fopen(fname, 0); if (r >= 0) (void)Fclose(r); drvchanged[d] = 0; }