home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / mint / mint095s / tosfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-03  |  28.8 KB  |  1,290 lines

  1. /*
  2. Copyright 1991,1992 Eric R. Smith. All rights reserved.
  3. */
  4.  
  5. /* a VERY simple tosfs.c 
  6.  * this one is extremely brain-damaged, but will serve OK for a
  7.  * skeleton in which to put a "real" tosfs.c
  8.  */
  9.  
  10. #include "mint.h"
  11.  
  12. /* search mask for anything OTHER THAN a volume label */
  13. #define FILEORDIR 0x37
  14.  
  15. #ifndef Flock
  16. /* this may need to be adjusted for your compiler/library */
  17. extern long gemdos();
  18. #define Flock(handle, mode, start, len) gemdos(0x5c, handle, mode, start, len)
  19. #endif
  20.  
  21. char tmpbuf[PATH_MAX+1];
  22.  
  23. static long    tos_root    P_((int drv, fcookie *fc));
  24. static long    tos_lookup    P_((fcookie *dir, const char *name, fcookie *fc));
  25. static long    tos_getxattr    P_((fcookie *fc, XATTR *xattr));
  26. static long    tos_chattr    P_((fcookie *fc, int attrib));
  27. static long    tos_chown    P_((fcookie *fc, int uid, int gid));
  28. static long    tos_chmode    P_((fcookie *fc, unsigned mode));
  29. static long    tos_mkdir    P_((fcookie *dir, const char *name, unsigned mode));
  30. static long    tos_rmdir    P_((fcookie *dir, const char *name));
  31. static long    tos_remove    P_((fcookie *dir, const char *name));
  32. static long    tos_getname    P_((fcookie *root, fcookie *dir, char *pathname));
  33. static long    tos_rename    P_((fcookie *olddir, char *oldname,
  34.                     fcookie *newdir, const char *newname));
  35. static long    tos_opendir    P_((DIR *dirh, int flags));
  36. static long    tos_readdir    P_((DIR *dirh, char *nm, int nmlen, fcookie *));
  37. static long    tos_rewinddir    P_((DIR *dirh));
  38. static long    tos_closedir    P_((DIR *dirh));
  39. static long    tos_pathconf    P_((fcookie *dir, int which));
  40. static long    tos_dfree    P_((fcookie *dir, long *buf));
  41. static long    tos_writelabel    P_((fcookie *dir, const char *name));
  42. static long    tos_readlabel    P_((fcookie *dir, char *name, int namelen));
  43.  
  44. static long    tos_creat    P_((fcookie *dir, const char *name, unsigned mode,
  45.                     int attrib, fcookie *fc));
  46. static DEVDRV *    tos_getdev    P_((fcookie *fc, long *devsp));
  47. static long    tos_open    P_((FILEPTR *f));
  48. static long    tos_write    P_((FILEPTR *f, const char *buf, long bytes));
  49. static long    tos_read    P_((FILEPTR *f, char *buf, long bytes));
  50. static long    tos_lseek    P_((FILEPTR *f, long where, int whence));
  51. static long    tos_ioctl    P_((FILEPTR *f, int mode, void *buf));
  52. static long    tos_datime    P_((FILEPTR *f, short *time, int rwflag));
  53. static long    tos_close    P_((FILEPTR *f, int pid));
  54. static long    tos_dskchng    P_((int drv));
  55.  
  56. /* some routines from biosfs.c */
  57. extern long    null_select    P_((FILEPTR *f, long p, int mode));
  58. extern void    null_unselect    P_((FILEPTR *f, long p, int mode));
  59.  
  60. DEVDRV tos_device = {
  61.     tos_open, tos_write, tos_read, tos_lseek, tos_ioctl, tos_datime,
  62.     tos_close, null_select, null_unselect
  63. };
  64.  
  65. FILESYS tos_filesys = {
  66.     (FILESYS *)0,
  67.     FS_KNOPARSE | FS_NOXBIT,
  68.     tos_root,
  69.     tos_lookup, tos_creat, tos_getdev, tos_getxattr,
  70.     tos_chattr, tos_chown, tos_chmode,
  71.     tos_mkdir, tos_rmdir, tos_remove, tos_getname, tos_rename,
  72.     tos_opendir, tos_readdir, tos_rewinddir, tos_closedir,
  73.     tos_pathconf, tos_dfree, tos_writelabel, tos_readlabel,
  74.     nosymlink, noreadlink, nohardlink, nofscntl, tos_dskchng
  75. };
  76.  
  77. /* some utility functions and variables: see end of file */
  78. static DTABUF     *lastdta;    /* last DTA buffer we asked TOS about */
  79. static DTABUF    foo;
  80. static void do_setdta P_((DTABUF *dta));
  81. static int executable_extension P_((char *));
  82.  
  83. /* this array keeps track of which drives have been changed */
  84. /* a nonzero entry means that the corresponding drive has been changed,
  85.  * but GEMDOS doesn't know it yet
  86.  */
  87. static char drvchanged[NUM_DRIVES];
  88.  
  89. /* force TOS to see a media change */
  90. static void force_mediach P_((int drv));
  91. static long Newgetbpb P_((int));
  92. static long Newmediach P_((int));
  93. static long Newrwabs P_((int, void *, int, int, int, long));
  94.  
  95. #define NUM_INDICES 128
  96. #define MIN_AGE 8
  97.  
  98. struct tindex {
  99.     char *name;        /* full path name */
  100.     FILEPTR *open;        /* fileptrs for this file; OR
  101.                  * count of number of open directories
  102.                  */
  103.     LOCK *locks;        /* locks on this file */
  104. /* file status */
  105.     long  size;
  106.     short time;
  107.     short date;
  108.     short attr;
  109.     short valid;        /* 1 if the above status is still valid */
  110.     short stamp;        /* age of this index, for garbage collection */
  111. } gl_ti[NUM_INDICES];
  112.  
  113. /* temporary index for files found by readdir */
  114. static struct tindex tmpindex;
  115. static char tmpiname[PATH_MAX];
  116.  
  117. static struct tindex *tstrindex P_((char *s));
  118. static int tfullpath P_((char *result, struct tindex *base, const char *name));
  119. static struct tindex *garbage_collect P_((void));
  120.  
  121. static short tclock;        /* #calls to tfullpath since last garbage
  122.                    collection */
  123.  
  124. /* some extra flags for the attr field */
  125.  
  126. /*
  127.  * is a string the name of a file with executable extension?
  128.  */
  129. #define FA_EXEC 0x4000
  130. /*
  131.  * should the file be deleted when it is closed?
  132.  */
  133. #define FA_DELETE 0x2000
  134.  
  135. /*
  136.  * NOTE: call executable_extension only on a DTA name returned from
  137.  * Fsfirst(), not on an arbitrary path
  138.  */
  139.  
  140. static int
  141. executable_extension(s)
  142.     char *s;
  143. {
  144.     while (*s && *s != '.') s++;
  145.     if (!*s) return 0;
  146.     s++;
  147.     if (s[0] == 'T') {
  148.         return (s[1] == 'T' && s[2] == 'P') ||
  149.                (s[1] == 'O' && s[2] == 'S');
  150.     }
  151.     if (s[0] == 'P')
  152.         return s[1] == 'R' && s[2] == 'G';
  153.     if (s[0] == 'A')
  154.         return s[1] == 'P' && s[2] == 'P';
  155.     if (s[0] == 'G')
  156.         return s[1] == 'T' && s[2] == 'P';
  157.     return 0;
  158. }
  159.  
  160. /*
  161.  * Look in the table of tos indices to see if an index corresponding
  162.  * to this file name already exists. If so, mark it as being used
  163.  * and return it. If not, find an empty slot and make an index for
  164.  * this string. If no empty slots exist, garbage collect and
  165.  * try again.
  166.  *
  167.  * This routine is pretty dumb; we really should use a hash table
  168.  * of some sort
  169.  */
  170.  
  171. static struct tindex *tstrindex(s)
  172.     char *s;
  173. {
  174.     int i;
  175.     char *r;
  176.     struct tindex *t, *free = 0;
  177.  
  178.     assert(s != 0);
  179.     t = gl_ti;
  180.     for (i = 0; i < NUM_INDICES; i++, t++) {
  181.         if (t->name && !stricmp(t->name, s)) {
  182.             t->stamp = tclock;    /* update use time */
  183.             return t;
  184.         }
  185.         else if (!t->name && !free)
  186.             free = t;
  187.     }
  188.     if (!free) {
  189.         free = garbage_collect();
  190.     }
  191.     if (!free) {
  192.         FATAL("tosfs: unable to get a file name index");
  193.     }
  194.     r = kmalloc((long)strlen(s)+1);
  195.     if (!r) {
  196.         FATAL("tosfs: unable to allocate space for a file name");
  197.     }
  198.     strcpy(r, s);
  199.     free->name = r;
  200.     free->stamp = tclock;
  201.     free->open = 0;
  202.     free->locks = 0;
  203.  
  204. /* check to see if this file was recently returned by opendir() */
  205.     if (tmpindex.valid && tclock - tmpindex.stamp < MIN_AGE &&
  206.         !stricmp(free->name, tmpindex.name)) {
  207.         free->size = tmpindex.size;
  208.         free->time = tmpindex.time;
  209.         free->date = tmpindex.date;
  210.         free->attr = tmpindex.attr;
  211.         free->valid = 1;
  212.         tmpindex.valid = 0;
  213.     } else
  214.         free->valid = 0;
  215.     return free;
  216. }
  217.  
  218. /*
  219.  * garbage collection routine: for any TOS index older than MIN_AGE,
  220.  * check through all current processes to see if it's in use. If
  221.  * not, free the corresponding string.
  222.  * Returns: a pointer to a newly freed index, or NULL.
  223.  */
  224.  
  225. /* it's unlikely that the kernel would need to hold onto a file cookie
  226.    for longer than this many calls to tstrindex() without first
  227.    saving the cookie in a directory or file pointer
  228.  */
  229.  
  230. static struct tindex *
  231. garbage_collect()
  232. {
  233.     struct tindex *free, *t;
  234.     fcookie *fc, *gc;
  235.     PROC *p;
  236.     int i, j;
  237.     int age;
  238.  
  239.     free = 0;
  240.     t = gl_ti;
  241.     for (i = 0; i < NUM_INDICES; i++,t++) {
  242.         if (!t->name) continue;
  243.         age = tclock - t->stamp;
  244.         t->stamp = 0;
  245.         assert(age >= 0);
  246.         if (age > MIN_AGE) {
  247.         /* see if any process is using this index */
  248.             if (t->open)
  249.                 goto found_index;
  250.             for (p = proclist; p; p = p->gl_next) {
  251.                 fc = p->curdir;
  252.                 gc = p->root;
  253.                 for (j = 0; j < NUM_DRIVES; j++,fc++,gc++) {
  254.                     if (( fc->fs == &tos_filesys &&
  255.                           fc->index == (long)t ) ||
  256.                         ( gc->fs == &tos_filesys &&
  257.                           gc->index == (long)t ) )
  258.                         goto found_index;
  259.                 }
  260.             }
  261.         /* here, we couldn't find the index in use by any proc. */
  262.             kfree(t->name);
  263.             t->name = 0;
  264.             if (!free)
  265.                 free = t;
  266.         found_index:
  267.             ;
  268.         } else {
  269.     /* make sure that future garbage collections might look at this file */
  270.             t->stamp = -age;
  271.         }
  272.     }
  273.  
  274.     tclock = 0;    /* reset the clock */
  275.     tmpindex.valid = 0; /* expire the temporary Fsfirst buffer */
  276.     return free;
  277. }
  278.  
  279. #define DIRSEP(c) ((c) == '\\')
  280.  
  281. static int
  282. tfullpath(result, basei, path)
  283.     char *result;
  284.     struct tindex *basei;
  285.     const char *path;
  286. {
  287. #define TNMTEMP 32
  288.     char *n, name[TNMTEMP+1];
  289.     int dom;
  290.     int namelen, pathlen;
  291.     char *base = basei->name;
  292.     int r = 0;
  293.  
  294.     basei->stamp = ++tclock;
  295.     if (tclock > 10000) {
  296.     /* garbage collect every so often whether we need it or not */
  297.         (void)garbage_collect();
  298.     }
  299.     if (!*path) {
  300.         strncpy(result, base, PATH_MAX-1);
  301.         return r;
  302.     }
  303.  
  304.     dom = curproc->domain;
  305.  
  306.     strncpy(result, base, PATH_MAX-1);
  307.  
  308.     pathlen = strlen(result);
  309.  
  310. /* now path is relative to what's currently in "result" */
  311.  
  312.     while(*path) {
  313. /* get next name in path */
  314.         n = name; namelen = 0;
  315.         while (*path && !DIRSEP(*path)) {
  316. /* BUG: we really should to the translation to DOS 8.3
  317.  * format *here*, so that really long names are truncated
  318.  * correctly.
  319.  */
  320.             if (namelen < TNMTEMP) {
  321.                 *n++ = toupper(*path); path++; namelen++;
  322.             }
  323.             else
  324.                 path++;
  325.         }
  326.         *n++ = 0;
  327.         while (DIRSEP(*path)) path++;
  328. /* check for "." and ".." */
  329.         if (!strcmp(name, ".")) continue;
  330.         if (!strcmp(name, "..")) {
  331.             n = strrchr(result, '\\');
  332.             if (n) {
  333.                 *n = 0;
  334.                 pathlen = n - result;
  335.             }
  336.             else r = EMOUNT;
  337.             continue;
  338.         }
  339.         if (pathlen + namelen < PATH_MAX - 1) {
  340.             strcat(result, "\\");
  341.             pathlen++;
  342.  
  343.     /* make sure the name is restricted to DOS 8.3 format */
  344.             for (base = result; *base; base++)
  345.                 ;
  346.             n = name;
  347.             namelen = 0;
  348.             while (*n && *n != '.' && namelen++ < 8) {
  349.                 *base++ = *n++;
  350.                 pathlen++;
  351.             }
  352.             while (*n && *n != '.') n++;
  353.             if (*n == '.' && *(n+1) != 0) {
  354.                 *base++ = *n++;
  355.                 pathlen++;
  356.                 namelen = 0;
  357.                 while (*n && namelen++ < 3) {
  358.                     *base++ = *n++;
  359.                     pathlen++;
  360.                 }
  361.             }
  362.             *base = 0;
  363.         }
  364.     }
  365.     return r;
  366. }
  367.  
  368. static long
  369. tos_root(drv, fc)
  370.     int drv;
  371.     fcookie *fc;
  372. {
  373.     struct tindex *ti;
  374.  
  375.     ksprintf(tmpbuf, "%c:", drv+'A');
  376.     fc->fs = &tos_filesys;
  377.     fc->dev = drv;
  378.     ti = tstrindex(tmpbuf);
  379.     ti->size = ti->date = ti->time = 0;
  380.     ti->attr = FA_DIR;
  381.     ti->valid = 1;
  382.     fc->index = (long)ti;
  383.  
  384. /* if the drive has changed, make sure GEMDOS knows it! */
  385.     if (drvchanged[drv]) {
  386.         force_mediach(drv);
  387.     }
  388.     return 0;
  389. }
  390.  
  391. static long
  392. tos_lookup(dir, name, fc)
  393.     fcookie *dir;
  394.     const char *name;
  395.     fcookie *fc;
  396. {
  397.     long r;
  398.     struct tindex *ti = (struct tindex *)dir->index;
  399.  
  400.     r = tfullpath(tmpbuf, ti, name);
  401.  
  402. /* if the name is empty or otherwise trivial, just return the directory */
  403.     if (!strcmp(ti->name, tmpbuf)) {
  404.         *fc = *dir;
  405.         return r;
  406.     }
  407.  
  408. /* is there already an index for this file?? If so, is it up to date?? */
  409.     ti = tstrindex(tmpbuf);
  410.     if (!ti->valid) {
  411.         if (tmpbuf[1] == ':' && tmpbuf[2] == 0) {
  412.             /* a root directory -- lookup always succeeds */
  413.             foo.dta_size = 0;
  414.             foo.dta_date = foo.dta_time = 0;
  415.             foo.dta_attrib = FA_DIR;
  416.             foo.dta_name[0] = 0;
  417.         } else {
  418.             do_setdta(&foo);
  419.             r = Fsfirst(tmpbuf, FILEORDIR);
  420.             if (r) {
  421. DEBUG("tos_lookup: Fsfirst(%s) returned %ld", tmpbuf, r);
  422.                 return r;
  423.             }
  424.         }
  425.         ti->size = foo.dta_size;
  426.         ti->date = foo.dta_date;
  427.         ti->time = foo.dta_time;
  428.         ti->attr = foo.dta_attrib;
  429.         if (executable_extension(foo.dta_name))
  430.             ti->attr |= FA_EXEC;
  431.         ti->valid = 1;
  432.     }
  433.     fc->fs = &tos_filesys;
  434.     fc->index = (long)ti;
  435.     fc->dev = dir->dev;
  436.     return r;
  437. }
  438.  
  439. static long
  440. tos_getxattr(fc, xattr)
  441.     fcookie *fc;
  442.     XATTR *xattr;
  443. {
  444.     struct tindex *ti = (struct tindex *)fc->index;
  445.     long r;
  446.     static long junkindex = 0;
  447.  
  448.     xattr->index = junkindex++;
  449.     xattr->dev = fc->dev;
  450.     xattr->nlink = 1;
  451.     xattr->uid = xattr->gid = 0;
  452.  
  453.     ti->stamp = ++tclock;
  454.     if (!ti->valid) {
  455.         do_setdta(&foo);
  456.         if (ti->name[2] == 0) {        /* a root directory */
  457. /* actually, this can also happen if a program tries to open a file
  458.  * with an empty name... so we should fail gracefully
  459.  */
  460. TRACE("tosfs: a root directory became invalid??");
  461.             goto around;
  462.         }
  463.     
  464.         r = Fsfirst(ti->name, FILEORDIR);
  465.         if (r < 0)
  466.             FATAL("tosfs: search error on [%s]", ti->name);
  467.         ti->size = foo.dta_size;
  468.         ti->date = foo.dta_date;
  469.         ti->time = foo.dta_time;
  470.         ti->attr = foo.dta_attrib;
  471.         if (executable_extension(foo.dta_name))
  472.             ti->attr |= FA_EXEC;
  473. around:
  474.         ti->valid = 1;
  475.     }
  476.     xattr->size = ti->size;
  477.  
  478. /* BUG: blksize isn't accurate if the sector size is not 512 */
  479.     xattr->blksize = 1024;
  480.     xattr->nblocks = (xattr->size + 1023) / 1024;
  481.     xattr->mdate = xattr->cdate = xattr->adate = ti->date;
  482.     xattr->mtime = xattr->ctime = xattr->atime = ti->time;
  483.     xattr->mode = (ti->attr & FA_DIR) ? (S_IFDIR | DEFAULT_DIRMODE) :
  484.              (S_IFREG | DEFAULT_MODE);
  485.  
  486.     if (ti->attr & FA_RDONLY) {
  487.         xattr->mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
  488.     }
  489.  
  490.     if (ti->attr & FA_EXEC) {
  491.         xattr->mode |= (S_IXUSR|S_IXGRP|S_IXOTH);
  492.     }
  493.     xattr->attr = ti->attr & 0xff;
  494.     return 0;
  495. }
  496.  
  497. static long
  498. tos_chattr(fc, attrib)
  499.     fcookie *fc;
  500.     int attrib;
  501. {
  502.     struct tindex *ti = (struct tindex *)fc->index;
  503.  
  504.     if (ti->attr & FA_DIR) {
  505.         DEBUG("error: attempt to change attributes of a directory");
  506.         return EACCDN;
  507.     }
  508.     ti->valid = 0;
  509.     (void)tfullpath(tmpbuf, ti, "");
  510.     return Fattrib(tmpbuf, 1, attrib);
  511. }
  512.  
  513. static long
  514. tos_chown(dir, uid, gid)
  515.     fcookie *dir;
  516.     int uid, gid;
  517. {
  518.     return EINVFN;
  519. }
  520.  
  521. static long
  522. tos_chmode(fc, mode)
  523.     fcookie *fc;
  524.     unsigned mode;
  525. {
  526.     int oldattr, newattr;
  527.     long r;
  528.     struct tindex *ti = (struct tindex *)fc->index;
  529.  
  530.     oldattr = Fattrib(ti->name, 0, 0);
  531.     if (oldattr < 0)
  532.         return oldattr;
  533.  
  534.     ti->valid = 0;
  535.  
  536.     if (!(mode & S_IWUSR))
  537.         newattr = oldattr | FA_RDONLY;
  538.     else
  539.         newattr = oldattr & ~FA_RDONLY;
  540.     if (newattr != oldattr)
  541.         r = Fattrib(ti->name, 1, newattr);
  542.     else
  543.         r = 0;
  544.     return (r < 0) ? r : 0;
  545. }
  546.  
  547. static long
  548. tos_mkdir(dir, name, mode)
  549.     fcookie *dir;
  550.     const char *name;
  551.     unsigned mode;        /* ignored under TOS */
  552. {
  553.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  554.     tmpindex.valid = 0;
  555.  
  556.     return Dcreate(tmpbuf);
  557. }
  558.  
  559. static long
  560. tos_rmdir(dir, name)
  561.     fcookie *dir;
  562.     const char *name;
  563. {
  564.     struct tindex *ti;
  565.  
  566.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  567.     ti = tstrindex(tmpbuf);
  568.     ti->valid = 0;
  569.  
  570.     return Ddelete(tmpbuf);
  571. }
  572.  
  573. static long
  574. tos_remove(dir, name)
  575.     fcookie *dir;
  576.     const char *name;
  577. {
  578.     struct tindex *ti;
  579.  
  580.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  581.  
  582.     ti = tstrindex(tmpbuf);
  583.     if (ti->open) {
  584.         DEBUG("tos_remove: file is open, will be deleted later");
  585.         if (ti->attr & FA_RDONLY)
  586.             return EACCDN;
  587.         ti->attr |= FA_DELETE;
  588.         return 0;
  589.     }
  590.     ti->valid = 0;
  591.     return Fdelete(tmpbuf);
  592. }
  593.  
  594. static long
  595. tos_getname(root, dir, pathname)
  596.     fcookie *root, *dir;
  597.     char *pathname;
  598. {
  599.     ksprintf(pathname, "%s", ((struct tindex *)dir->index)->name +
  600.                 strlen(((struct tindex*)root->index)->name) );
  601.     if (curproc->domain == DOM_MINT)
  602.         strlwr(pathname);
  603.  
  604.     return 0;
  605. }
  606.  
  607. static long
  608. tos_rename(olddir, oldname, newdir, newname)
  609.     fcookie *olddir;
  610.     char *oldname;
  611.     fcookie *newdir;
  612.     const char *newname;
  613. {
  614.     char newbuf[128];
  615.     struct tindex *ti;
  616.     long r;
  617.  
  618.     (void)tfullpath(tmpbuf, (struct tindex *)olddir->index, oldname);
  619.     (void)tfullpath(newbuf, (struct tindex *)newdir->index, newname);
  620.     r = Frename(0, tmpbuf, newbuf);
  621.     if (r == 0) {
  622.         ti = tstrindex(tmpbuf);
  623.         kfree(ti->name);
  624.         ti->name = kmalloc((long)strlen(newbuf)+1);
  625.         if (!ti->name) {
  626.             FATAL("tosfs: unable to allocate space for a name");
  627.         }
  628.         strcpy(ti->name, newbuf);
  629.         ti->valid = 0;
  630.     }
  631.     return r;
  632. }
  633.  
  634. #define DIR_FLAG(x)    (x->fsstuff[0])
  635. #define STARTSEARCH    0    /* opendir() was just called */
  636. #define INSEARCH    1    /* readdir() has been called at least once */
  637. #define NMFILE        2    /* no more files to read */
  638.  
  639. #define DIR_DTA(x)    ((DTABUF *)(x->fsstuff + 2))
  640. #define DIR_NAME(x)    (x->fsstuff + 32)
  641.  
  642. /*
  643.  * The directory functions are a bit tricky. What we do is have
  644.  * opendir() do Fsfirst; the first readdir() picks up this name,
  645.  * subsequent readdir()'s have to do Fsnext
  646.  */
  647.  
  648. static long
  649. tos_opendir(dirh, flags)
  650.     DIR *dirh;
  651.     int flags;
  652. {
  653.     long r;
  654.     struct tindex *t = (struct tindex *)dirh->fc.index;
  655.  
  656.     (void)tfullpath(tmpbuf, t, "*.*");
  657.  
  658.     do_setdta(DIR_DTA(dirh));
  659.  
  660.     r = Fsfirst(tmpbuf, FILEORDIR);
  661.  
  662.     if (r == 0) {
  663.         t->open++;
  664.         DIR_FLAG(dirh) = STARTSEARCH;
  665.         return 0;
  666.     } else if (r == EFILNF) {
  667.         t->open++;
  668.         DIR_FLAG(dirh) = NMFILE;
  669.         return 0;
  670.     }
  671.      return r;
  672. }
  673.  
  674. static long
  675. tos_readdir(dirh, name, namelen, fc)
  676.     DIR *dirh;
  677.     char *name;
  678.     int namelen;
  679.     fcookie *fc;
  680. {
  681.     static long index = 0;
  682.     long ret;
  683.     int giveindex = dirh->flags == 0;
  684.     struct tindex *ti;
  685.     DTABUF *dta = DIR_DTA(dirh);
  686.  
  687. again:
  688.     if (DIR_FLAG(dirh) == NMFILE)
  689.         return ENMFIL;
  690.  
  691.     if (DIR_FLAG(dirh) == STARTSEARCH) {
  692.         DIR_FLAG(dirh) = INSEARCH;
  693.     } else {
  694.         assert(DIR_FLAG(dirh) == INSEARCH);
  695.         do_setdta(dta);
  696.         ret = Fsnext();
  697.         if (ret) {
  698.             DIR_FLAG(dirh) = NMFILE;
  699.             return ret;
  700.         }
  701.     }
  702.  
  703. /* don't return volume labels from readdir */
  704.     if (dta->dta_attrib == FA_LABEL) goto again;
  705.  
  706.     fc->fs = &tos_filesys;
  707.     fc->dev = dirh->fc.dev;
  708.  
  709.     (void)tfullpath(tmpiname, (struct tindex *)dirh->fc.index, DIR_NAME(dirh));
  710.  
  711.     ti = &tmpindex;
  712.     ti->name = tmpiname;
  713.     ti->valid = 1;
  714.     ti->size = dta->dta_size;
  715.     ti->date = dta->dta_date;
  716.     ti->time = dta->dta_time;
  717.     ti->attr = dta->dta_attrib;
  718.     ti->stamp = tclock;
  719.     if (executable_extension(dta->dta_name))
  720.         ti->attr |= FA_EXEC;
  721.     fc->index = (long)ti;
  722.  
  723.     if (giveindex) {
  724.         namelen -= sizeof(long);
  725.         if (namelen <= 0) return ERANGE;
  726.         *((long *)name) = index++;
  727.         name += sizeof(long);
  728.     }
  729.     strncpy(name, DIR_NAME(dirh), namelen-1);
  730.     name[namelen-1] = 0;
  731.     if (curproc->domain == DOM_MINT) {
  732.         strlwr(name);
  733.     }
  734.     if (strlen(DIR_NAME(dirh)) >= namelen)
  735.         return ENAMETOOLONG;
  736.     else
  737.         return 0;
  738. }
  739.  
  740. static long
  741. tos_rewinddir(dirh)
  742.     DIR *dirh;
  743. {
  744.     struct tindex *ti = (struct tindex *)dirh->fc.index;
  745.     long r;
  746.  
  747.     (void)tfullpath(tmpbuf, ti, "*.*");
  748.     do_setdta(DIR_DTA(dirh));
  749.     r = Fsfirst(tmpbuf, FILEORDIR);
  750.     if (r == 0) {
  751.         DIR_FLAG(dirh) = STARTSEARCH;
  752.     } else {
  753.         DIR_FLAG(dirh) = NMFILE;
  754.     }
  755.     return r;
  756. }
  757.  
  758. static long
  759. tos_closedir(dirh)
  760.     DIR *dirh;
  761. {
  762.     struct tindex *t = (struct tindex *)dirh->fc.index;
  763.  
  764.     assert(t->open);
  765.     --t->open;
  766.     DIR_FLAG(dirh) = NMFILE;
  767.     return 0;
  768. }
  769.  
  770. static long
  771. tos_pathconf(dir, which)
  772.     fcookie *dir;
  773.     int which;
  774. {
  775.     switch(which) {
  776.     case -1:
  777.         return DP_MAXREQ;
  778.     case DP_IOPEN:
  779.         return 60;    /* we can only keep about this many open */
  780.     case DP_MAXLINKS:
  781.          return 1;    /* no hard links */
  782.     case DP_PATHMAX:
  783.         return PATH_MAX;
  784.     case DP_NAMEMAX:
  785.         return 8+3+1;
  786.     case DP_ATOMIC:
  787.         return 512;    /* we can write at least a sector atomically */
  788.     case DP_TRUNC:
  789.         return DP_DOSTRUNC;    /* DOS style file names */
  790.     case DP_CASE:
  791.         return DP_CASECONV;    /* names converted to upper case */
  792.     default:
  793.         return EINVFN;
  794.     }
  795. }
  796.  
  797. long
  798. tos_dfree(dir, buf)
  799.     fcookie *dir;
  800.     long *buf;
  801. {
  802.     return Dfree(buf, (dir->dev)+1);
  803. }
  804.  
  805. /*
  806.  * writelabel: creates a volume label
  807.  * readlabel: reads a volume label
  808.  * both of these are only guaranteed to work in the root directory
  809.  */
  810.  
  811. /*
  812.  * BUG: this should first delete any old labels, so that it will
  813.  * work with TOS <1.4
  814.  */
  815.  
  816. long
  817. tos_writelabel(dir, name)
  818.     fcookie *dir;
  819.     const char *name;
  820. {
  821.     long r;
  822.     struct tindex *ti;
  823.  
  824.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  825.     r = Fcreate(tmpbuf, FA_LABEL);
  826.     if (r < 0) return r;
  827.     (void)Fclose(r);
  828.     ti = tstrindex(tmpbuf);
  829.     ti->valid = 0;
  830.     return 0;
  831. }
  832.  
  833. long
  834. tos_readlabel(dir, name, namelen)
  835.     fcookie *dir;
  836.     char *name;
  837.     int namelen;
  838. {
  839.     long r;
  840.     struct tindex *ti = (struct tindex *)dir->index;
  841.  
  842.     if (ti->name[2] != 0)        /* not a root directory? */
  843.         return EFILNF;
  844.  
  845.     (void)tfullpath(tmpbuf, ti, "*.*");
  846.     do_setdta(&foo);
  847.     r = Fsfirst(tmpbuf, FA_LABEL);
  848.     if (r)
  849.         return r;
  850.     strncpy(name, foo.dta_name, namelen-1);
  851.     return (strlen(foo.dta_name) < namelen) ? 0 : ENAMETOOLONG;
  852. }
  853.  
  854. /*
  855.  * TOS creat: this doesn't actually create the file, rather it
  856.  * sets up a (fake) index for the file that will be used by
  857.  * the later tos_open call.
  858.  */
  859.  
  860. static long
  861. tos_creat(dir, name, mode, attrib, fc)
  862.     fcookie *dir;
  863.     const char *name;
  864.     unsigned mode;
  865.     int attrib;
  866.     fcookie *fc;
  867. {
  868.     struct tindex *ti;
  869.  
  870.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  871.  
  872.     ti = tstrindex(tmpbuf);
  873.     ti->size = 0;
  874.     ti->date = datestamp;
  875.     ti->time = timestamp;
  876.     ti->attr = attrib;
  877.     ti->valid = 1;
  878.  
  879.     fc->fs = &tos_filesys;
  880.     fc->index = (long)ti;
  881.     fc->dev = dir->dev;
  882.     return 0;
  883. }
  884.     
  885. /*
  886.  * TOS device driver
  887.  */
  888.  
  889. static DEVDRV *
  890. tos_getdev(fc, devsp)
  891.     fcookie *fc;
  892.     long *devsp;
  893. {
  894.     return &tos_device;
  895. }
  896.  
  897. static long
  898. tos_open(f)
  899.     FILEPTR *f;
  900. {
  901.     struct tindex *ti;
  902.     int mode = f->flags;
  903.     int tosmode;
  904.     long r;
  905.     extern int flk;        /* in main.c, set if _FLK cookie already present */
  906.  
  907.     ti = (struct tindex *)(f->fc.index);
  908.     assert(ti != 0);
  909.  
  910.     ti->stamp = ++tclock;
  911.     ti->valid = 0;
  912.  
  913. /* TEMPORARY HACK: change all modes to O_RDWR for files opened in
  914.  * compatibility sharing mode. This is silly, but
  915.  * allows broken TOS programs that write to read-only handles to continue
  916.  * to work (it also helps file sharing, by making the realistic assumption
  917.  * that any open TOS file can be written to). Eventually,
  918.  * this should be tuneable by the user somehow.
  919.  * ALSO: change O_COMPAT opens into O_DENYNONE; again, this may be temporary.
  920.  */
  921.     if ( (mode & O_SHMODE) == O_COMPAT ) {
  922.         f->flags = (mode & ~(O_RWMODE|O_SHMODE)) | O_RDWR | O_DENYNONE;
  923.     }
  924.  
  925. /* check to see that nobody has opened this file already in an
  926.  * incompatible mode
  927.  */
  928.     if (denyshare(ti->open, f)) {
  929.         TRACE("tos_open: file sharing denied");
  930.         return EACCDN;
  931.     }
  932.  
  933. /*
  934.  * now open the file; if O_TRUNC was specified, actually
  935.  * create the file anew.
  936.  * BUG: O_TRUNC without O_CREAT doesn't work right. The kernel doesn't
  937.  * use this mode, anyways
  938.  */
  939.  
  940.     if (mode & O_TRUNC) {
  941.         if (ti->open) {
  942.             DEBUG("tos_open: attempt to truncate an open file");
  943.             return EACCDN;
  944.         }
  945.         r = Fcreate(ti->name, ti->attr);
  946.     } else {
  947.         if (flk)
  948.             tosmode = mode & (O_RWMODE|O_SHMODE);
  949.         else
  950.             tosmode = (mode & O_RWMODE);
  951.         if (tosmode == O_EXEC) tosmode = O_RDONLY;
  952.  
  953.         r = Fopen(ti->name, tosmode );
  954.         if (r == EFILNF && (mode & O_CREAT))
  955.             r = Fcreate(ti->name, ti->attr);
  956.     }
  957.  
  958.     if (r < 0) {
  959. /* get rid of the index for the file, since it doesn't exist */
  960.         kfree(ti->name);
  961.         ti->name = 0;
  962.         ti->valid = 0;
  963.         return r;
  964.     }
  965.  
  966.     f->devinfo = r;
  967.  
  968.     f->next = ti->open;
  969.     ti->open = f;
  970.     return 0;
  971. }
  972.  
  973. static long
  974. tos_write(f, buf, bytes)
  975.     FILEPTR *f; const char *buf; long bytes;
  976. {
  977.     struct tindex *ti = (struct tindex *)f->fc.index;
  978.  
  979.     ti->valid = 0;
  980.     return Fwrite((int)f->devinfo, bytes, buf);
  981. }
  982.  
  983. static long
  984. tos_read(f, buf, bytes)
  985.     FILEPTR *f; char *buf; long bytes;
  986. {
  987.     return Fread((int)f->devinfo, bytes, buf);
  988. }
  989.  
  990. static long
  991. tos_lseek(f, where, whence)
  992.     FILEPTR *f; long where; int whence;
  993. {
  994.     long r;
  995.  
  996.     r = Fseek(where, (int)f->devinfo, whence);
  997.     return r;
  998. }
  999.  
  1000. static long
  1001. tos_ioctl(f, mode, buf)
  1002.     FILEPTR *f; int mode; void *buf;
  1003. {
  1004.     LOCK t, *lck, **old;
  1005.     struct flock *fl;
  1006.     long r;
  1007.     struct tindex *ti;
  1008.     extern int flk;        /* set in main.c if _FLK already installed */
  1009.  
  1010.     if (mode == FIONREAD || mode == FIONWRITE) {
  1011.         *((long *)buf) = 1;
  1012.         return 0;
  1013.     }
  1014.     else if (mode == F_SETLK || mode == F_GETLK) {
  1015.         fl = ((struct flock *)buf);
  1016.         t.l = *fl;
  1017.         switch(t.l.l_whence) {
  1018.         case 0:
  1019.             break;
  1020.         case 1:        /* SEEK_CUR */
  1021.             r = Fseek(0L, (int)f->devinfo, 1);
  1022.             t.l.l_start += r;
  1023.             break;
  1024.         case 2:
  1025.             r = Fseek(0L, (int)f->devinfo, 1);
  1026.             t.l.l_start = Fseek(t.l.l_start, (int)f->devinfo, 2);
  1027.             (void)Fseek(r, (int)f->devinfo, 0);
  1028.             break;
  1029.         default:
  1030.             DEBUG("Invalid value for l_whence");
  1031.             return EINVFN;
  1032.         }
  1033. /* BUG: can't lock a file starting at >2gigabytes from the beginning */
  1034.         if (t.l.l_start < 0) t.l.l_start = 0;
  1035.         t.l.l_whence = 0;
  1036.         ti = (struct tindex *)f->fc.index;
  1037.  
  1038.         if (mode == F_GETLK) {
  1039.             lck = denylock(ti->locks, &t);
  1040.             if (lck)
  1041.                 *fl = lck->l;
  1042.             else
  1043.                 fl->l_type = F_UNLCK;
  1044.             return 0;
  1045.         }
  1046.  
  1047.         if (t.l.l_type == F_UNLCK) {
  1048.         /* try to find the lock */
  1049.             old = &ti->locks;
  1050.             lck = *old;
  1051.             while (lck) {
  1052.                 if (lck->l.l_pid == curproc->pid &&
  1053.                     lck->l.l_start == t.l.l_start &&
  1054.                     lck->l.l_len == t.l.l_len) {
  1055.         /* found it -- remove the lock */
  1056.                     *old = lck->next;
  1057.                     TRACE("tosfs: unlocked %s: %ld + %ld",
  1058.                         ti->name, t.l.l_start, t.l.l_len);
  1059.                     kfree(lck);
  1060.                     if (flk)
  1061.                         (void)Flock((int)f->devinfo, 1,
  1062.                             t.l.l_start, t.l.l_len);
  1063.                     break;
  1064.                 }
  1065.                 old = &lck->next;
  1066.                 lck = lck->next;
  1067.             }
  1068.             return lck ?  0 : ENSLOCK;
  1069.         }
  1070.         TRACE("tosfs: lock %s: %ld + %ld", ti->name,
  1071.             t.l.l_start, t.l.l_len);
  1072.     /* see if there's a conflicting lock */
  1073.         lck = denylock(ti->locks, &t);
  1074.         if (lck) {
  1075.             DEBUG("tosfs: lock conflicts with one held by %d",
  1076.                 lck->l.l_pid);
  1077.             return ELOCKED;
  1078.         }
  1079.     /* if not, add this lock to the list */
  1080.         lck = kmalloc(SIZEOF(LOCK));
  1081.         if (!lck) return ENSMEM;
  1082.     /* see if other _FLK code might object */
  1083.         if (flk) {
  1084.             r = Flock((int)f->devinfo, 0, t.l.l_start, t.l.l_len);
  1085.             if (r) {
  1086.                 kfree(lck);
  1087.                 return r;
  1088.             }
  1089.         }
  1090.         lck->l = t.l;
  1091.         lck->l.l_pid = curproc->pid;
  1092.         lck->next = ti->locks;
  1093.         ti->locks = lck;
  1094.     /* mark the file as being locked */
  1095.         f->flags |= O_LOCK;
  1096.         return 0;
  1097.     }
  1098.     return EINVFN;
  1099. }
  1100.  
  1101. static long
  1102. tos_datime(f, timeptr, rwflag)
  1103.     FILEPTR *f;
  1104.     short *timeptr;
  1105.     int rwflag;
  1106. {
  1107.     if (rwflag) {
  1108.         struct tindex *ti = (struct tindex *)f->fc.index;
  1109.         ti->valid = 0;
  1110.     }
  1111.     return Fdatime(timeptr, (int)f->devinfo, rwflag);
  1112. }
  1113.  
  1114. static long
  1115. tos_close(f, pid)
  1116.     FILEPTR *f;
  1117.     int pid;
  1118. {
  1119.     LOCK *lck, **oldl;
  1120.     struct tindex *t;
  1121.     FILEPTR **old, *p;
  1122.     long r = 0;
  1123.     extern int flk;        /* set in main.c */
  1124.  
  1125.     t = (struct tindex *)(f->fc.index);
  1126. /* if this handle was locked, remove any locks held by the process
  1127.  */
  1128.     if (f->flags & O_LOCK) {
  1129.         TRACE("tos_close: releasing locks (file mode: %x)", f->flags);
  1130.         oldl = &t->locks;
  1131.         lck = *oldl;
  1132.         while (lck) {
  1133.             if (lck->l.l_pid == pid) {
  1134.                 *oldl = lck->next;
  1135.                 if (flk)
  1136.                     (void)Flock((int)f->devinfo, 1,
  1137.                         lck->l.l_start, lck->l.l_len);
  1138.                 kfree(lck);
  1139.             } else {
  1140.                 oldl = &lck->next;
  1141.             }
  1142.             lck = *oldl;
  1143.         }
  1144.     }
  1145.  
  1146.     if (f->links <= 0) {
  1147. /* remove f from the list of open file pointers on this index */
  1148.         t->valid = 0;
  1149.         old = &t->open;
  1150.         p = t->open;
  1151.         while (p && p != f) {
  1152.             old = &p->next;
  1153.             p = p->next;
  1154.         }
  1155.         assert(p);
  1156.         *old = f->next;
  1157.         f->next = 0;
  1158.         r = Fclose((int)f->devinfo);
  1159.  
  1160. /* if the file was marked for deletion, delete it */
  1161.         if (!t->open) {
  1162.             if (t->attr & FA_DELETE) {
  1163.                 (void)Fdelete(t->name);
  1164.                 t->name = 0;
  1165.             }
  1166.         }
  1167.     }
  1168.     return r;
  1169. }
  1170.  
  1171. /*
  1172.  * check for disk change: called by the kernel if Mediach returns a
  1173.  * non-zero value
  1174.  */
  1175.  
  1176. long
  1177. tos_dskchng(drv)
  1178.     int drv;
  1179. {
  1180.     char dlet;
  1181.     int i;
  1182.     struct tindex *ti;
  1183.  
  1184.     dlet = 'A' + drv;
  1185.     ti = gl_ti;
  1186.     for (i = 0; i < NUM_INDICES; i++, ti++) {
  1187.         if (ti->name[0] == dlet) {
  1188.             kfree(ti->name);
  1189.             ti->name = 0;
  1190.         }
  1191.     }
  1192. /*
  1193.  * OK, make sure that GEMDOS knows to look for a change if we
  1194.  * ever use this drive again.
  1195.  */
  1196.     drvchanged[drv] = 1;
  1197.     return 1;
  1198. }
  1199.  
  1200. /*
  1201.  * utility function: sets the TOS DTA, and also records what directory
  1202.  * this was in. This is just to save us a call into the kernel if the
  1203.  * correct DTA has already been set.
  1204.  */
  1205.  
  1206. static void
  1207. do_setdta(dta)
  1208.     DTABUF *dta;
  1209. {
  1210.     if (dta != lastdta) {
  1211.         Fsetdta(dta);
  1212.         lastdta = dta;
  1213.     }
  1214. }
  1215.  
  1216. /*
  1217.  * routines for forcing a media change on drive "drv"
  1218.  */
  1219.  
  1220. static int chdrv;
  1221.  
  1222. /* new Getbpb function: when this is called, all the other
  1223.  * vectors can be un-installed
  1224.  */
  1225.  
  1226. static long (*Oldgetbpb) P_((int));
  1227. static long (*Oldmediach) P_((int));
  1228. static long (*Oldrwabs) P_((int, void *, int, int, int, long));
  1229.  
  1230. static long
  1231. Newgetbpb(d)
  1232.     int d;
  1233. {
  1234.     if (d == chdrv) {
  1235.         *((Func *)0x472L) = Oldgetbpb;
  1236.         *((Func *)0x476L) = Oldrwabs;
  1237.         *((Func *)0x47eL) = Oldmediach;
  1238.     }
  1239.     return (*Oldgetbpb)(d);
  1240. }
  1241.  
  1242. static long
  1243. Newmediach(d)
  1244.     int d;
  1245. {
  1246.     if (d == chdrv)
  1247.         return 2;
  1248.     return (*Oldmediach)(d);
  1249. }
  1250.  
  1251. static long
  1252. Newrwabs(d, buf, a, b, c, l)
  1253.     int d;
  1254.     void *buf;
  1255.     int a, b, c;
  1256.     long l;
  1257. {
  1258.     if (d == chdrv)
  1259.         return E_CHNG;
  1260.     return (*Oldrwabs)(d, buf, a, b, c, l);
  1261. }
  1262.  
  1263. static void
  1264. force_mediach(d)
  1265.     int d;
  1266. {
  1267.     long r;
  1268.     static char fname[] = "X:\\.INF";
  1269.  
  1270.     TRACE("tosfs: disk change drive %c", d+'A');
  1271.  
  1272.     chdrv = d;
  1273.     Oldrwabs = *((Func *)0x476L);
  1274.     if (Oldrwabs == Newrwabs) {
  1275.         ALERT("tosfs: error in media change code");
  1276.     } else {
  1277.         *((Func *)0x476L) = Newrwabs;
  1278.         Oldmediach = *((Func *)0x47eL);
  1279.         *((Func *)0x47eL) = Newmediach;
  1280.         Oldgetbpb = *((Func *)0x472L);
  1281.         *((Func *)0x472L) = Newgetbpb;
  1282.     }
  1283.  
  1284.     fname[0] = d + 'A';
  1285.     r = Fopen(fname, 0);
  1286.     if (r >= 0) (void)Fclose(r);
  1287.  
  1288.     drvchanged[d] = 0;
  1289. }
  1290.